Version 0.4.0.0 .
Revert change 18601 in trunk.
svn merge -r 18562:18904 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 18912 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
git-svn-id: http://dart.googlecode.com/svn/trunk@18915 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/dart.gyp b/dart.gyp
index c988d74..860d4464 100644
--- a/dart.gyp
+++ b/dart.gyp
@@ -95,6 +95,14 @@
],
},
{
+ 'target_name': 'analyzer',
+ 'type': 'none',
+ 'dependencies': [
+ 'create_sdk',
+ 'editor/analyzer.gyp:analyzer',
+ ],
+ },
+ {
# This is the target that is built on the dartc bots.
# It must depend on anything that is required by dartc
# tests.
diff --git a/pkg/args/lib/src/usage.dart b/pkg/args/lib/src/usage.dart
index 72525e2..b83a2d5 100644
--- a/pkg/args/lib/src/usage.dart
+++ b/pkg/args/lib/src/usage.dart
@@ -170,7 +170,7 @@
writeLine(int column, String text) {
// Write any pending newlines.
while (newlinesNeeded > 0) {
- buffer.add('\n');
+ buffer.write('\n');
newlinesNeeded--;
}
@@ -178,19 +178,19 @@
// to the next line.
while (currentColumn != column) {
if (currentColumn < NUM_COLUMNS - 1) {
- buffer.add(padRight('', columnWidths[currentColumn]));
+ buffer.write(padRight('', columnWidths[currentColumn]));
} else {
- buffer.add('\n');
+ buffer.write('\n');
}
currentColumn = (currentColumn + 1) % NUM_COLUMNS;
}
if (column < columnWidths.length) {
// Fixed-size column, so pad it.
- buffer.add(padRight(text, columnWidths[column]));
+ buffer.write(padRight(text, columnWidths[column]));
} else {
// The last column, so just write it.
- buffer.add(text);
+ buffer.write(text);
}
// Advance to the next column.
@@ -210,17 +210,17 @@
buildAllowedList(Option option) {
var allowedBuffer = new StringBuffer();
- allowedBuffer.add('[');
+ allowedBuffer.write('[');
bool first = true;
for (var allowed in option.allowed) {
- if (!first) allowedBuffer.add(', ');
- allowedBuffer.add(allowed);
+ if (!first) allowedBuffer.write(', ');
+ allowedBuffer.write(allowed);
if (allowed == option.defaultValue) {
- allowedBuffer.add(' (default)');
+ allowedBuffer.write(' (default)');
}
first = false;
}
- allowedBuffer.add(']');
+ allowedBuffer.write(']');
return allowedBuffer.toString();
}
}
@@ -228,11 +228,11 @@
/** Pads [source] to [length] by adding spaces at the end. */
String padRight(String source, int length) {
final result = new StringBuffer();
- result.add(source);
+ result.write(source);
while (result.length < length) {
- result.add(' ');
+ result.write(' ');
}
return result.toString();
-}
\ No newline at end of file
+}
diff --git a/pkg/fixnum/lib/src/int32.dart b/pkg/fixnum/lib/src/int32.dart
index bbff8a0..2aad272 100644
--- a/pkg/fixnum/lib/src/int32.dart
+++ b/pkg/fixnum/lib/src/int32.dart
@@ -288,7 +288,7 @@
return _i == _convert(other);
}
- int compareTo(Comparable other) {
+ int compareTo(other) {
if (other is int64) {
return this.toInt64().compareTo(other);
}
diff --git a/pkg/http/lib/src/io_client.dart b/pkg/http/lib/src/io_client.dart
index 47fb1f0..9bbb7ee 100644
--- a/pkg/http/lib/src/io_client.dart
+++ b/pkg/http/lib/src/io_client.dart
@@ -24,67 +24,38 @@
Future<StreamedResponse> send(BaseRequest request) {
var stream = request.finalize();
- var completer = new Completer<StreamedResponse>();
- var connection = _inner.openUrl(request.method, request.url);
- bool completed = false;
- connection.followRedirects = request.followRedirects;
- connection.maxRedirects = request.maxRedirects;
- connection.onError = (e) {
- async.then((_) {
- // TODO(nweiz): issue 4974 means that any errors that appear in the
- // onRequest or onResponse callbacks get passed to onError. If the
- // completer has already fired, we want to re-throw those exceptions
- // to the top level so that they aren't silently ignored.
- if (completed) throw e;
-
- completed = true;
- completer.completeError(e);
- });
- };
-
- var pipeCompleter = new Completer();
- connection.onRequest = (underlyingRequest) {
- underlyingRequest.contentLength = request.contentLength;
- underlyingRequest.persistentConnection = request.persistentConnection;
+ return _inner.openUrl(request.method, request.url).then((ioRequest) {
+ ioRequest.followRedirects = request.followRedirects;
+ ioRequest.maxRedirects = request.maxRedirects;
+ ioRequest.contentLength = request.contentLength;
+ ioRequest.persistentConnection = request.persistentConnection;
request.headers.forEach((name, value) {
- underlyingRequest.headers.set(name, value);
+ ioRequest.headers.set(name, value);
});
-
- chainToCompleter(
- stream.pipe(wrapOutputStream(underlyingRequest.outputStream)),
- pipeCompleter);
- };
-
- connection.onResponse = (response) {
+ return Future.wait([stream.pipe(ioRequest), ioRequest.response])
+ .then((list) => list[1]);
+ }).then((response) {
var headers = {};
response.headers.forEach((key, values) {
headers[key] = values.join(',');
});
- if (completed) return;
-
- completed = true;
- completer.complete(new StreamedResponse(
- wrapInputStream(response.inputStream),
+ return new StreamedResponse(
+ response,
response.statusCode,
response.contentLength,
request: request,
headers: headers,
isRedirect: response.isRedirect,
persistentConnection: response.persistentConnection,
- reasonPhrase: response.reasonPhrase));
- };
-
- return Future.wait([
- completer.future,
- pipeCompleter.future
- ]).then((values) => values.first);
+ reasonPhrase: response.reasonPhrase);
+ });
}
/// Closes the client. This terminates all active connections. If a client
/// remains unclosed, the Dart process may not terminate.
void close() {
- if (_inner != null) _inner.shutdown(force: true);
+ if (_inner != null) _inner.close(force: true);
_inner = null;
}
}
diff --git a/pkg/http/lib/src/multipart_file.dart b/pkg/http/lib/src/multipart_file.dart
index a0b50cb..50f4704 100644
--- a/pkg/http/lib/src/multipart_file.dart
+++ b/pkg/http/lib/src/multipart_file.dart
@@ -87,7 +87,7 @@
{String filename, ContentType contentType}) {
if (filename == null) filename = new Path(file.name).filename;
return file.length().then((length) {
- var stream = wrapInputStream(file.openInputStream());
+ var stream = new ByteStream(file.openRead());
return new MultipartFile(field, stream, length,
filename: filename,
contentType: contentType);
diff --git a/pkg/http/lib/src/multipart_request.dart b/pkg/http/lib/src/multipart_request.dart
index b311b8e..b89c839 100644
--- a/pkg/http/lib/src/multipart_request.dart
+++ b/pkg/http/lib/src/multipart_request.dart
@@ -73,7 +73,7 @@
///
/// This doesn't need to be closed. When the request is sent, whichever files
/// are written to this sink at that point will be used.
- StreamSink<MultipartFile> get files => _files;
+ CollectionSink<MultipartFile> get files => _files;
/// The private version of [files], typed so that the underlying collection is
/// accessible.
diff --git a/pkg/http/test/client_test.dart b/pkg/http/test/client_test.dart
index fd056c9..e101f28 100644
--- a/pkg/http/test/client_test.dart
+++ b/pkg/http/test/client_test.dart
@@ -13,48 +13,51 @@
import 'utils.dart';
void main() {
- setUp(startServer);
tearDown(stopServer);
test('#send a StreamedRequest', () {
- var client = new http.Client();
- var request = new http.StreamedRequest("POST", serverUrl);
- request.headers[HttpHeaders.CONTENT_TYPE] =
- 'application/json; charset=utf-8';
+ expect(startServer().then((_) {
+ var client = new http.Client();
+ var request = new http.StreamedRequest("POST", serverUrl);
+ request.headers[HttpHeaders.CONTENT_TYPE] =
+ 'application/json; charset=utf-8';
- expect(client.send(request).then((response) {
- expect(response.request, equals(request));
- expect(response.statusCode, equals(200));
- expect(response.headers['single'], equals('value'));
- // dart:io internally normalizes outgoing headers so that they never have
- // multiple headers with the same name, so there's no way to test whether
- // we handle that case correctly.
+ expect(client.send(request).then((response) {
+ expect(response.request, equals(request));
+ expect(response.statusCode, equals(200));
+ expect(response.headers['single'], equals('value'));
+ // dart:io internally normalizes outgoing headers so that they never
+ // have multiple headers with the same name, so there's no way to test
+ // whether we handle that case correctly.
- return response.stream.bytesToString();
- }).whenComplete(client.close), completion(parse(equals({
- 'method': 'POST',
- 'path': '/',
- 'headers': {
- 'content-type': ['application/json; charset=utf-8'],
- 'transfer-encoding': ['chunked']
- },
- 'body': '{"hello": "world"}'
- }))));
+ return response.stream.bytesToString();
+ }).whenComplete(client.close), completion(parse(equals({
+ 'method': 'POST',
+ 'path': '/',
+ 'headers': {
+ 'content-type': ['application/json; charset=utf-8'],
+ 'transfer-encoding': ['chunked']
+ },
+ 'body': '{"hello": "world"}'
+ }))));
- request.sink.add('{"hello": "world"}'.charCodes);
- request.sink.close();
+ request.sink.add('{"hello": "world"}'.charCodes);
+ request.sink.close();
+ }), completes);
});
test('#send with an invalid URL', () {
- var client = new http.Client();
- var url = Uri.parse('http://http.invalid');
- var request = new http.StreamedRequest("POST", url);
- request.headers[HttpHeaders.CONTENT_TYPE] =
- 'application/json; charset=utf-8';
+ expect(startServer().then((_) {
+ var client = new http.Client();
+ var url = Uri.parse('http://http.invalid');
+ var request = new http.StreamedRequest("POST", url);
+ request.headers[HttpHeaders.CONTENT_TYPE] =
+ 'application/json; charset=utf-8';
- expect(client.send(request), throwsSocketIOException);
+ expect(client.send(request), throwsSocketIOException);
- request.sink.add('{"hello": "world"}'.charCodes);
- request.sink.close();
+ request.sink.add('{"hello": "world"}'.charCodes);
+ request.sink.close();
+ }), completes);
});
}
diff --git a/pkg/http/test/http_test.dart b/pkg/http/test/http_test.dart
index 48eada0..9c567cf 100644
--- a/pkg/http/test/http_test.dart
+++ b/pkg/http/test/http_test.dart
@@ -12,23 +12,161 @@
main() {
group('http.', () {
- setUp(startServer);
tearDown(stopServer);
test('head', () {
- expect(http.head(serverUrl).then((response) {
- expect(response.statusCode, equals(200));
- expect(response.body, equals(''));
+ expect(startServer().then((_) {
+ expect(http.head(serverUrl).then((response) {
+ expect(response.statusCode, equals(200));
+ expect(response.body, equals(''));
+ }), completes);
}), completes);
});
test('get', () {
- expect(http.get(serverUrl, headers: {
- 'X-Random-Header': 'Value',
- 'X-Other-Header': 'Other Value'
- }).then((response) {
- expect(response.statusCode, equals(200));
- expect(response.body, parse(equals({
+ expect(startServer().then((_) {
+ expect(http.get(serverUrl, headers: {
+ 'X-Random-Header': 'Value',
+ 'X-Other-Header': 'Other Value'
+ }).then((response) {
+ expect(response.statusCode, equals(200));
+ expect(response.body, parse(equals({
+ 'method': 'GET',
+ 'path': '/',
+ 'headers': {
+ 'content-length': ['0'],
+ 'x-random-header': ['Value'],
+ 'x-other-header': ['Other Value']
+ },
+ })));
+ }), completes);
+ }), completes);
+ });
+
+ test('post', () {
+ expect(startServer().then((_) {
+ expect(http.post(serverUrl, headers: {
+ 'X-Random-Header': 'Value',
+ 'X-Other-Header': 'Other Value'
+ }, fields: {
+ 'some-field': 'value',
+ 'other-field': 'other value'
+ }).then((response) {
+ expect(response.statusCode, equals(200));
+ expect(response.body, parse(equals({
+ 'method': 'POST',
+ 'path': '/',
+ 'headers': {
+ 'content-type': [
+ 'application/x-www-form-urlencoded; charset=UTF-8'
+ ],
+ 'content-length': ['40'],
+ 'x-random-header': ['Value'],
+ 'x-other-header': ['Other Value']
+ },
+ 'body': 'some-field=value&other-field=other+value'
+ })));
+ }), completes);
+ }), completes);
+ });
+
+ test('post without fields', () {
+ expect(startServer().then((_) {
+ expect(http.post(serverUrl, headers: {
+ 'X-Random-Header': 'Value',
+ 'X-Other-Header': 'Other Value',
+ 'Content-Type': 'text/plain'
+ }).then((response) {
+ expect(response.statusCode, equals(200));
+ expect(response.body, parse(equals({
+ 'method': 'POST',
+ 'path': '/',
+ 'headers': {
+ 'content-length': ['0'],
+ 'content-type': ['text/plain'],
+ 'x-random-header': ['Value'],
+ 'x-other-header': ['Other Value']
+ }
+ })));
+ }), completes);
+ }), completes);
+ });
+
+ test('put', () {
+ expect(startServer().then((_) {
+ expect(http.put(serverUrl, headers: {
+ 'X-Random-Header': 'Value',
+ 'X-Other-Header': 'Other Value'
+ }, fields: {
+ 'some-field': 'value',
+ 'other-field': 'other value'
+ }).then((response) {
+ expect(response.statusCode, equals(200));
+ expect(response.body, parse(equals({
+ 'method': 'PUT',
+ 'path': '/',
+ 'headers': {
+ 'content-type': [
+ 'application/x-www-form-urlencoded; charset=UTF-8'
+ ],
+ 'content-length': ['40'],
+ 'x-random-header': ['Value'],
+ 'x-other-header': ['Other Value']
+ },
+ 'body': 'some-field=value&other-field=other+value'
+ })));
+ }), completes);
+ }), completes);
+ });
+
+ test('put without fields', () {
+ expect(startServer().then((_) {
+ expect(http.put(serverUrl, headers: {
+ 'X-Random-Header': 'Value',
+ 'X-Other-Header': 'Other Value',
+ 'Content-Type': 'text/plain'
+ }).then((response) {
+ expect(response.statusCode, equals(200));
+ expect(response.body, parse(equals({
+ 'method': 'PUT',
+ 'path': '/',
+ 'headers': {
+ 'content-length': ['0'],
+ 'content-type': ['text/plain'],
+ 'x-random-header': ['Value'],
+ 'x-other-header': ['Other Value']
+ }
+ })));
+ }), completes);
+ }), completes);
+ });
+
+ test('delete', () {
+ expect(startServer().then((_) {
+ expect(http.delete(serverUrl, headers: {
+ 'X-Random-Header': 'Value',
+ 'X-Other-Header': 'Other Value'
+ }).then((response) {
+ expect(response.statusCode, equals(200));
+ expect(response.body, parse(equals({
+ 'method': 'DELETE',
+ 'path': '/',
+ 'headers': {
+ 'content-length': ['0'],
+ 'x-random-header': ['Value'],
+ 'x-other-header': ['Other Value']
+ }
+ })));
+ }), completes);
+ }), completes);
+ });
+
+ test('read', () {
+ expect(startServer().then((_) {
+ expect(http.read(serverUrl, headers: {
+ 'X-Random-Header': 'Value',
+ 'X-Other-Header': 'Other Value'
+ }).then((val) => val), completion(parse(equals({
'method': 'GET',
'path': '/',
'headers': {
@@ -36,156 +174,39 @@
'x-random-header': ['Value'],
'x-other-header': ['Other Value']
},
- })));
+ }))));
}), completes);
});
- test('post', () {
- expect(http.post(serverUrl, headers: {
- 'X-Random-Header': 'Value',
- 'X-Other-Header': 'Other Value'
- }, fields: {
- 'some-field': 'value',
- 'other-field': 'other value'
- }).then((response) {
- expect(response.statusCode, equals(200));
- expect(response.body, parse(equals({
- 'method': 'POST',
- 'path': '/',
- 'headers': {
- 'content-type': [
- 'application/x-www-form-urlencoded; charset=UTF-8'
- ],
- 'content-length': ['40'],
- 'x-random-header': ['Value'],
- 'x-other-header': ['Other Value']
- },
- 'body': 'some-field=value&other-field=other+value'
- })));
- }), completes);
- });
-
- test('post without fields', () {
- expect(http.post(serverUrl, headers: {
- 'X-Random-Header': 'Value',
- 'X-Other-Header': 'Other Value',
- 'Content-Type': 'text/plain'
- }).then((response) {
- expect(response.statusCode, equals(200));
- expect(response.body, parse(equals({
- 'method': 'POST',
- 'path': '/',
- 'headers': {
- 'content-length': ['0'],
- 'content-type': ['text/plain'],
- 'x-random-header': ['Value'],
- 'x-other-header': ['Other Value']
- }
- })));
- }), completes);
- });
-
- test('put', () {
- expect(http.put(serverUrl, headers: {
- 'X-Random-Header': 'Value',
- 'X-Other-Header': 'Other Value'
- }, fields: {
- 'some-field': 'value',
- 'other-field': 'other value'
- }).then((response) {
- expect(response.statusCode, equals(200));
- expect(response.body, parse(equals({
- 'method': 'PUT',
- 'path': '/',
- 'headers': {
- 'content-type': [
- 'application/x-www-form-urlencoded; charset=UTF-8'
- ],
- 'content-length': ['40'],
- 'x-random-header': ['Value'],
- 'x-other-header': ['Other Value']
- },
- 'body': 'some-field=value&other-field=other+value'
- })));
- }), completes);
- });
-
- test('put without fields', () {
- expect(http.put(serverUrl, headers: {
- 'X-Random-Header': 'Value',
- 'X-Other-Header': 'Other Value',
- 'Content-Type': 'text/plain'
- }).then((response) {
- expect(response.statusCode, equals(200));
- expect(response.body, parse(equals({
- 'method': 'PUT',
- 'path': '/',
- 'headers': {
- 'content-length': ['0'],
- 'content-type': ['text/plain'],
- 'x-random-header': ['Value'],
- 'x-other-header': ['Other Value']
- }
- })));
- }), completes);
- });
-
- test('delete', () {
- expect(http.delete(serverUrl, headers: {
- 'X-Random-Header': 'Value',
- 'X-Other-Header': 'Other Value'
- }).then((response) {
- expect(response.statusCode, equals(200));
- expect(response.body, parse(equals({
- 'method': 'DELETE',
- 'path': '/',
- 'headers': {
- 'content-length': ['0'],
- 'x-random-header': ['Value'],
- 'x-other-header': ['Other Value']
- }
- })));
- }), completes);
- });
-
- test('read', () {
- expect(http.read(serverUrl, headers: {
- 'X-Random-Header': 'Value',
- 'X-Other-Header': 'Other Value'
- }).then((val) => val), completion(parse(equals({
- 'method': 'GET',
- 'path': '/',
- 'headers': {
- 'content-length': ['0'],
- 'x-random-header': ['Value'],
- 'x-other-header': ['Other Value']
- },
- }))));
- });
-
test('read throws an error for a 4** status code', () {
- expect(http.read(serverUrl.resolve('/error')), throwsHttpException);
+ expect(startServer().then((_) {
+ expect(http.read(serverUrl.resolve('/error')), throwsHttpException);
+ }), completes);
});
test('readBytes', () {
- var future = http.readBytes(serverUrl, headers: {
- 'X-Random-Header': 'Value',
- 'X-Other-Header': 'Other Value'
- }).then((bytes) => new String.fromCharCodes(bytes));
+ expect(startServer().then((_) {
+ var future = http.readBytes(serverUrl, headers: {
+ 'X-Random-Header': 'Value',
+ 'X-Other-Header': 'Other Value'
+ }).then((bytes) => new String.fromCharCodes(bytes));
- expect(future, completion(parse(equals({
- 'method': 'GET',
- 'path': '/',
- 'headers': {
- 'content-length': ['0'],
- 'x-random-header': ['Value'],
- 'x-other-header': ['Other Value']
- },
- }))));
+ expect(future, completion(parse(equals({
+ 'method': 'GET',
+ 'path': '/',
+ 'headers': {
+ 'content-length': ['0'],
+ 'x-random-header': ['Value'],
+ 'x-other-header': ['Other Value']
+ },
+ }))));
+ }), completes);
});
test('readBytes throws an error for a 4** status code', () {
- expect(http.readBytes(serverUrl.resolve('/error')), throwsHttpException);
+ expect(startServer().then((_) {
+ expect(http.readBytes(serverUrl.resolve('/error')), throwsHttpException);
+ }), completes);
});
});
}
diff --git a/pkg/http/test/request_test.dart b/pkg/http/test/request_test.dart
index 843cdab..1f009b9 100644
--- a/pkg/http/test/request_test.dart
+++ b/pkg/http/test/request_test.dart
@@ -13,23 +13,24 @@
void main() {
test('.send', () {
- startServer();
+ expect(startServer().then((_) {
- var request = new http.Request('POST', serverUrl);
- request.body = "hello";
+ var request = new http.Request('POST', serverUrl);
+ request.body = "hello";
- expect(request.send().then((response) {
- expect(response.statusCode, equals(200));
- return response.stream.bytesToString();
- }).whenComplete(stopServer), completion(parse(equals({
- 'method': 'POST',
- 'path': '/',
- 'headers': {
- 'content-type': ['text/plain; charset=UTF-8'],
- 'content-length': ['5']
- },
- 'body': 'hello'
- }))));
+ expect(request.send().then((response) {
+ expect(response.statusCode, equals(200));
+ return response.stream.bytesToString();
+ }).whenComplete(stopServer), completion(parse(equals({
+ 'method': 'POST',
+ 'path': '/',
+ 'headers': {
+ 'content-type': ['text/plain; charset=UTF-8'],
+ 'content-length': ['5']
+ },
+ 'body': 'hello'
+ }))));
+ }), completes);
});
group('#contentLength', () {
@@ -183,47 +184,49 @@
print("This test is known to be flaky, please ignore "
"(debug prints below added by sgjesse@)");
print("#followRedirects test starting server...");
- startServer();
- print("#followRedirects test server running");
+ expect(startServer().then((_) {
+ print("#followRedirects test server running");
- var request = new http.Request('POST', serverUrl.resolve('/redirect'))
- ..followRedirects = false;
- var future = request.send().then((response) {
- print("#followRedirects test response received");
- expect(response.statusCode, equals(302));
- });
- future.catchError((_) {}).then(expectAsync1((_) {
- print("#followRedirects test stopping server...");
- stopServer();
- print("#followRedirects test server stopped");
- }));
+ var request = new http.Request('POST', serverUrl.resolve('/redirect'))
+ ..followRedirects = false;
+ var future = request.send().then((response) {
+ print("#followRedirects test response received");
+ expect(response.statusCode, equals(302));
+ });
+ expect(future.catchError((_) {}).then((_) {
+ print("#followRedirects test stopping server...");
+ stopServer();
+ print("#followRedirects test server stopped");
+ }), completes);
- expect(future, completes);
- print("#followRedirects test started");
+ expect(future, completes);
+ print("#followRedirects test started");
+ }), completes);
});
test('#maxRedirects', () {
print("This test is known to be flaky, please ignore "
"(debug prints below added by sgjesse@)");
print("#maxRedirects test starting server...");
- startServer();
- print("#maxRedirects test server running");
+ expect(startServer().then((_) {
+ print("#maxRedirects test server running");
- var request = new http.Request('POST', serverUrl.resolve('/loop?1'))
- ..maxRedirects = 2;
- var future = request.send().catchError((e) {
- print("#maxRedirects test exception received");
- expect(e.error, isRedirectLimitExceededException);
- expect(e.error.redirects.length, equals(2));
- });
- future.catchError((_) {}).then(expectAsync1((_) {
- print("#maxRedirects test stopping server...");
- stopServer();
- print("#maxRedirects test server stopped");
- }));
+ var request = new http.Request('POST', serverUrl.resolve('/loop?1'))
+ ..maxRedirects = 2;
+ var future = request.send().catchError((e) {
+ print("#maxRedirects test exception received");
+ expect(e.error, isRedirectLimitExceededException);
+ expect(e.error.redirects.length, equals(2));
+ });
+ expect(future.catchError((_) {}).then((_) {
+ print("#maxRedirects test stopping server...");
+ stopServer();
+ print("#maxRedirects test server stopped");
+ }), completes);
- expect(future, completes);
- print("#maxRedirects test started");
+ expect(future, completes);
+ print("#maxRedirects test started");
+ }), completes);
});
group('content-type header', () {
diff --git a/pkg/http/test/utils.dart b/pkg/http/test/utils.dart
index 10067d2..606bb77 100644
--- a/pkg/http/test/utils.dart
+++ b/pkg/http/test/utils.dart
@@ -10,6 +10,7 @@
import 'dart:uri';
import 'package:unittest/unittest.dart';
+import 'package:http/src/byte_stream.dart';
import 'package:http/http.dart' as http;
import 'package:http/src/utils.dart';
@@ -23,81 +24,83 @@
Uri get dummyUrl => Uri.parse('http://dartlang.org/');
/// Starts a new HTTP server.
-void startServer() {
- _server = new HttpServer();
+Future startServer() {
+ return HttpServer.bind("127.0.0.1", 0).then((s) {
+ _server = s;
+ s.listen((request) {
+ var path = request.uri.path;
+ var response = request.response;
- _server.addRequestHandler((request) => request.path == '/error',
- (request, response) {
- response.statusCode = 400;
- response.contentLength = 0;
- response.outputStream.close();
- });
+ if (path == '/error') {
+ response.statusCode = 400;
+ response.contentLength = 0;
+ response.close();
+ return;
+ }
- _server.addRequestHandler((request) => request.path == '/loop',
- (request, response) {
- var n = int.parse(Uri.parse(request.uri).query);
- response.statusCode = 302;
- response.headers.set('location',
- serverUrl.resolve('/loop?${n + 1}').toString());
- response.contentLength = 0;
- response.outputStream.close();
- });
+ if (path == '/loop') {
+ var n = int.parse(request.uri.query);
+ response.statusCode = 302;
+ response.headers.set('location',
+ serverUrl.resolve('/loop?${n + 1}').toString());
+ response.contentLength = 0;
+ response.close();
+ return;
+ }
- _server.addRequestHandler((request) => request.path == '/redirect',
- (request, response) {
- response.statusCode = 302;
- response.headers.set('location', serverUrl.resolve('/').toString());
- response.contentLength = 0;
- response.outputStream.close();
- });
+ if (path == '/redirect') {
+ response.statusCode = 302;
+ response.headers.set('location', serverUrl.resolve('/').toString());
+ response.contentLength = 0;
+ response.close();
+ return;
+ }
- _server.defaultRequestHandler = (request, response) {
- consumeInputStream(request.inputStream).then((requestBodyBytes) {
- response.statusCode = 200;
- response.headers.contentType = new ContentType("application", "json");
+ new ByteStream(request).toBytes().then((requestBodyBytes) {
+ response.statusCode = 200;
+ response.headers.contentType = new ContentType("application", "json");
response.headers.set('single', 'value');
- var requestBody;
- if (requestBodyBytes.isEmpty) {
- requestBody = null;
- } else if (request.headers.contentType.charset != null) {
- var encoding = requiredEncodingForCharset(
- request.headers.contentType.charset);
- requestBody = decodeString(requestBodyBytes, encoding);
- } else {
- requestBody = requestBodyBytes;
- }
+ var requestBody;
+ if (requestBodyBytes.isEmpty) {
+ requestBody = null;
+ } else if (request.headers.contentType.charset != null) {
+ var encoding = requiredEncodingForCharset(
+ request.headers.contentType.charset);
+ requestBody = decodeString(requestBodyBytes, encoding);
+ } else {
+ requestBody = requestBodyBytes;
+ }
- var content = {
- 'method': request.method,
- 'path': request.path,
- 'headers': {}
- };
- if (requestBody != null) content['body'] = requestBody;
- request.headers.forEach((name, values) {
- // These headers are automatically generated by dart:io, so we don't
- // want to test them here.
- if (name == 'cookie' || name == 'host') return;
+ var content = {
+ 'method': request.method,
+ 'path': request.uri.path,
+ 'headers': {}
+ };
+ if (requestBody != null) content['body'] = requestBody;
+ request.headers.forEach((name, values) {
+ // These headers are automatically generated by dart:io, so we don't
+ // want to test them here.
+ if (name == 'cookie' || name == 'host') return;
- content['headers'][name] = values;
+ content['headers'][name] = values;
+ });
+
+ var outputEncoding;
+ var encodingName = request.queryParameters['response-encoding'];
+ if (encodingName != null) {
+ outputEncoding = requiredEncodingForCharset(encodingName);
+ } else {
+ outputEncoding = Encoding.ASCII;
+ }
+
+ var body = json.stringify(content);
+ response.contentLength = body.length;
+ response.addString(body, outputEncoding);
+ response.close();
});
-
- var outputEncoding;
- var encodingName = request.queryParameters['response-encoding'];
- if (encodingName != null) {
- outputEncoding = requiredEncodingForCharset(encodingName);
- } else {
- outputEncoding = Encoding.ASCII;
- }
-
- var body = json.stringify(content);
- response.contentLength = body.length;
- response.outputStream.writeString(body, outputEncoding);
- response.outputStream.close();
});
- };
-
- _server.listen("127.0.0.1", 0);
+ });
}
/// Stops the current HTTP server.
@@ -106,11 +109,6 @@
_server = null;
}
-// TODO(nweiz): remove this once issue 7785 is fixed.
-/// Buffers all input from an InputStream and returns it as a future.
-Future<List<int>> consumeInputStream(InputStream stream) =>
- new http.ByteStream(wrapInputStream(stream)).toBytes();
-
/// A matcher that matches JSON that parses to a value that matches the inner
/// matcher.
Matcher parse(matcher) => new _Parse(matcher);
diff --git a/pkg/intl/test/date_time_format_http_request_test.dart b/pkg/intl/test/date_time_format_http_request_test.dart
index 139cf6d..65bc87b 100644
--- a/pkg/intl/test/date_time_format_http_request_test.dart
+++ b/pkg/intl/test/date_time_format_http_request_test.dart
@@ -21,7 +21,7 @@
main() {
useHtmlConfiguration();
- url = "http://localhost:${window.location.port}/pkg/intl/lib/src/data/dates/";
+ url = "http://localhost:${window.location.port}/root_dart/pkg/intl/lib/src/data/dates/";
// Initialize one locale just so we know what the list is.
test('Run everything', () {
initializeDateFormatting("en_US", url).then(expectAsync1(runEverything));});
diff --git a/pkg/intl/tool/generate_locale_data_files.dart b/pkg/intl/tool/generate_locale_data_files.dart
index b999926..a6fe140 100644
--- a/pkg/intl/tool/generate_locale_data_files.dart
+++ b/pkg/intl/tool/generate_locale_data_files.dart
@@ -29,23 +29,24 @@
void writeLocaleList() {
var file = new File('${dataDirectory}localeList.dart');
- var outputStream = file.openOutputStream();
- outputStream.writeString(
+ var output = file.openWrite();
+ output.addString(
'// Copyright (c) 2012, the Dart project authors. Please see the '
'AUTHORS file\n// for details. All rights reserved. Use of this source'
'code is governed by a\n// BSD-style license that can be found in the'
' LICENSE file.\n\n'
'/// Hard-coded list of all available locales for dates.\n');
- outputStream.writeString('final availableLocalesForDateFormatting = const [');
+ output.addString('final availableLocalesForDateFormatting = const [');
List<String> allLocales = DateFormat.allLocalesWithSymbols();
allLocales.forEach((locale) {
- outputStream.writeString('"$locale"');
+ output.addString('"$locale"');
if (locale == allLocales.last) {
- outputStream.writeString('];');
+ output.addString('];');
} else {
- outputStream.writeString(',\n ');
+ output.addString(',\n ');
}
});
+ output.close();
}
void writeSymbolData() {
@@ -60,18 +61,18 @@
void writeSymbols(locale, symbols) {
var file = new File('${dataDirectory}symbols/${locale}.json');
- var outputStream = file.openOutputStream();
- writeToJSON(symbols, outputStream);
- outputStream.close();
+ var output = file.openWrite();
+ writeToJSON(symbols, output);
+ output.close();
}
void writePatterns(locale, patterns) {
var file = new File('${dataDirectory}patterns/${locale}.json');
- var outputStream = file.openOutputStream();
- outputStream.writeString(json.stringify(patterns));
- outputStream.close();
+ var output = file.openWrite();
+ output.addString(json.stringify(patterns));
+ output.close();
}
-void writeToJSON(dynamic data, OutputStream out) {
- out.writeString(json.stringify(data.serializeToMap()));
+void writeToJSON(dynamic data, IOSink out) {
+ out.addString(json.stringify(data.serializeToMap()));
}
diff --git a/pkg/logging/lib/logging.dart b/pkg/logging/lib/logging.dart
index 5e0a239..935b3ae 100644
--- a/pkg/logging/lib/logging.dart
+++ b/pkg/logging/lib/logging.dart
@@ -247,7 +247,7 @@
* own level, make sure you use a value between those used in [Level.ALL] and
* [Level.OFF].
*/
-class Level implements Comparable {
+class Level implements Comparable<Level> {
// TODO(sigmund): mark name/value as 'const' when the language supports it.
final String name;
diff --git a/pkg/path/lib/path.dart b/pkg/path/lib/path.dart
index 3bd2332..9e540b2 100644
--- a/pkg/path/lib/path.dart
+++ b/pkg/path/lib/path.dart
@@ -112,6 +112,23 @@
String part5, String part6, String part7, String part8]) =>
_builder.join(part1, part2, part3, part4, part5, part6, part7, part8);
+/// Joins the given path parts into a single path using the current platform's
+/// [separator]. Example:
+///
+/// path.joinAll(['path', 'to', 'foo']); // -> 'path/to/foo'
+///
+/// If any part ends in a path separator, then a redundant separator will not
+/// be added:
+///
+/// path.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo
+///
+/// If a part is an absolute path, then anything before that will be ignored:
+///
+/// path.joinAll(['path', '/to', 'foo']); // -> '/to/foo'
+///
+/// For a fixed number of parts, [join] is usually terser.
+String joinAll(Iterable<String> parts) => _builder.joinAll(parts);
+
// TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed.
/// Splits [path] into its components using the current platform's [separator].
///
@@ -321,15 +338,30 @@
///
String join(String part1, [String part2, String part3, String part4,
String part5, String part6, String part7, String part8]) {
+ var parts = [part1, part2, part3, part4, part5, part6, part7, part8];
+ _validateArgList("join", parts);
+ return joinAll(parts.where((part) => part != null));
+ }
+
+ /// Joins the given path parts into a single path. Example:
+ ///
+ /// builder.joinAll(['path', 'to', 'foo']); // -> 'path/to/foo'
+ ///
+ /// If any part ends in a path separator, then a redundant separator will not
+ /// be added:
+ ///
+ /// builder.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo
+ ///
+ /// If a part is an absolute path, then anything before that will be ignored:
+ ///
+ /// builder.joinAll(['path', '/to', 'foo']); // -> '/to/foo'
+ ///
+ /// For a fixed number of parts, [join] is usually terser.
+ String joinAll(Iterable<String> parts) {
var buffer = new StringBuffer();
var needsSeparator = false;
- var parts = [part1, part2, part3, part4, part5, part6, part7, part8];
- _validateArgList("join", parts);
-
for (var part in parts) {
- if (part == null) continue;
-
if (this.isAbsolute(part)) {
// An absolute path discards everything before it.
buffer.clear();
diff --git a/pkg/path/test/path_posix_test.dart b/pkg/path/test/path_posix_test.dart
index e1c3942..763035f 100644
--- a/pkg/path/test/path_posix_test.dart
+++ b/pkg/path/test/path_posix_test.dart
@@ -161,6 +161,25 @@
});
});
+ group('joinAll', () {
+ test('allows more than eight parts', () {
+ expect(builder.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']),
+ 'a/b/c/d/e/f/g/h/i');
+ });
+
+ test('does not add separator if a part ends in one', () {
+ expect(builder.joinAll(['a/', 'b', 'c/', 'd']), 'a/b/c/d');
+ expect(builder.joinAll(['a\\', 'b']), r'a\/b');
+ });
+
+ test('ignores parts before an absolute path', () {
+ expect(builder.joinAll(['a', '/', 'b', 'c']), '/b/c');
+ expect(builder.joinAll(['a', '/b', '/c', 'd']), '/c/d');
+ expect(builder.joinAll(['a', r'c:\b', 'c', 'd']), r'a/c:\b/c/d');
+ expect(builder.joinAll(['a', r'\\b', 'c', 'd']), r'a/\\b/c/d');
+ });
+ });
+
group('split', () {
test('simple cases', () {
expect(builder.split(''), []);
diff --git a/pkg/path/test/path_windows_test.dart b/pkg/path/test/path_windows_test.dart
index 12e370b..8164224 100644
--- a/pkg/path/test/path_windows_test.dart
+++ b/pkg/path/test/path_windows_test.dart
@@ -180,6 +180,26 @@
});
});
+ group('joinAll', () {
+ test('allows more than eight parts', () {
+ expect(builder.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']),
+ r'a\b\c\d\e\f\g\h\i');
+ });
+
+ test('does not add separator if a part ends or begins in one', () {
+ expect(builder.joinAll([r'a\', 'b', r'c\', 'd']), r'a\b\c\d');
+ expect(builder.joinAll(['a/', 'b']), r'a/b');
+ expect(builder.joinAll(['a', '/b']), 'a/b');
+ expect(builder.joinAll(['a', r'\b']), r'a\b');
+ });
+
+ test('ignores parts before an absolute path', () {
+ expect(builder.joinAll(['a', '/b', '/c', 'd']), r'a/b/c\d');
+ expect(builder.joinAll(['a', r'c:\b', 'c', 'd']), r'c:\b\c\d');
+ expect(builder.joinAll(['a', r'\\b', r'\\c', 'd']), r'\\c\d');
+ });
+ });
+
group('split', () {
test('simple cases', () {
expect(builder.split(''), []);
diff --git a/pkg/scheduled_test/lib/scheduled_test.dart b/pkg/scheduled_test/lib/scheduled_test.dart
index 05fb9f0..456e50b3 100644
--- a/pkg/scheduled_test/lib/scheduled_test.dart
+++ b/pkg/scheduled_test/lib/scheduled_test.dart
@@ -109,7 +109,8 @@
/// It's important that errors in these callbacks are still registered, though,
/// and that [Schedule.onException] and [Schedule.onComplete] still run after
/// they finish. When using `unittest`, you wrap these callbacks with
-/// `expectAsyncN`; when using `scheduled_test`, you use [wrapAsync].
+/// `expectAsyncN`; when using `scheduled_test`, you use [wrapAsync] or
+/// [wrapFuture].
///
/// [wrapAsync] has two important functions. First, any errors that occur in it
/// will be passed into the [Schedule] instead of causing the whole test to
@@ -139,6 +140,25 @@
/// });
/// }
///
+/// [wrapFuture] works similarly to [wrapAsync], but instead of wrapping a
+/// single callback it wraps a whole [Future] chain. Like [wrapAsync], it
+/// ensures that the task queue doesn't complete until the out-of-band chain has
+/// finished, and that any errors in the chain are piped back into the scheduled
+/// test. For example:
+///
+/// import 'package:scheduled_test/scheduled_test.dart';
+///
+/// void main() {
+/// test('sendRequest sends a request', () {
+/// wrapFuture(server.nextRequest.then((request) {
+/// expect(request.body, equals('payload'));
+/// expect(request.headers['content-type'], equals('text/plain'));
+/// }));
+///
+/// schedule(() => sendRequest('payload'));
+/// });
+/// }
+///
/// ## Timeouts
///
/// `scheduled_test` has a built-in timeout of 30 seconds (configurable via
@@ -244,7 +264,10 @@
/// If [description] is passed, it's used to describe the task for debugging
/// purposes when an error occurs.
///
-/// This function is identical to [currentSchedule.tasks.schedule].
+/// If this is called when a task queue is currently running, it will run [fn]
+/// on the next event loop iteration rather than adding it to a queue. The
+/// current task will not complete until [fn] (and any [Future] it returns) has
+/// finished running. Any errors in [fn] will automatically be handled.
Future schedule(fn(), [String description]) =>
currentSchedule.tasks.schedule(fn, description);
@@ -302,3 +325,18 @@
return currentSchedule.wrapAsync(f);
};
}
+
+/// Like [wrapAsync], this ensures that the current task queue waits for
+/// out-of-band asynchronous code, and that errors raised in that code are
+/// handled correctly. However, [wrapFuture] wraps a [Future] chain rather than
+/// a single callback.
+///
+/// The returned [Future] completes to the same value or error as [future].
+Future wrapFuture(Future future) {
+ if (currentSchedule == null) {
+ throw new StateError("Unexpected call to wrapFuture with no current "
+ "schedule.");
+ }
+
+ return currentSchedule.wrapFuture(future);
+}
diff --git a/pkg/scheduled_test/lib/src/future_group.dart b/pkg/scheduled_test/lib/src/future_group.dart
new file mode 100644
index 0000000..213f0fc
--- /dev/null
+++ b/pkg/scheduled_test/lib/src/future_group.dart
@@ -0,0 +1,49 @@
+// 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 future_group;
+
+import 'dart:async';
+
+/// A completer that waits until all added [Future]s complete.
+// TODO(rnystrom): Copied from web_components. Remove from here when it gets
+// added to dart:core. (See #6626.)
+class FutureGroup<T> {
+ int _pending = 0;
+ Completer<List<T>> _completer = new Completer<List<T>>();
+ final List<Future<T>> futures = <Future<T>>[];
+ bool completed = false;
+
+ final List<T> _values = <T>[];
+
+ /// Wait for [task] to complete.
+ Future<T> add(Future<T> task) {
+ if (completed) {
+ throw new StateError("The FutureGroup has already completed.");
+ }
+
+ _pending++;
+ futures.add(task.then((value) {
+ if (completed) return;
+
+ _pending--;
+ _values.add(value);
+
+ if (_pending <= 0) {
+ completed = true;
+ _completer.complete(_values);
+ }
+ }).catchError((e) {
+ if (completed) return;
+
+ completed = true;
+ _completer.completeError(e.error, e.stackTrace);
+ }));
+
+ return task;
+ }
+
+ Future<List> get future => _completer.future;
+}
+
diff --git a/pkg/scheduled_test/lib/src/schedule.dart b/pkg/scheduled_test/lib/src/schedule.dart
index 58a1487..ac601ea 100644
--- a/pkg/scheduled_test/lib/src/schedule.dart
+++ b/pkg/scheduled_test/lib/src/schedule.dart
@@ -127,19 +127,22 @@
try {
setUp();
} catch (e, stackTrace) {
+ // Even though the scheduling failed, we need to run the onException and
+ // onComplete queues, so we set the schedule state to RUNNING.
+ _state = ScheduleState.RUNNING;
throw new ScheduleError.from(this, e, stackTrace: stackTrace);
}
_state = ScheduleState.RUNNING;
return tasks._run();
}).catchError((e) {
- errors.add(e);
+ _addError(e);
return onException._run().catchError((innerError) {
// If an error occurs in a task in the onException queue, make sure it's
// registered in the error list and re-throw it. We could also re-throw
// `e`; ultimately, all the errors will be shown to the user if any
// ScheduleError is thrown.
- errors.add(innerError);
+ _addError(innerError);
throw innerError;
}).then((_) {
// If there are no errors in the onException queue, re-throw the
@@ -150,7 +153,7 @@
return onComplete._run().catchError((e) {
// If an error occurs in a task in the onComplete queue, make sure it's
// registered in the error list and re-throw it.
- errors.add(e);
+ _addError(e);
throw e;
});
}).whenComplete(() {
@@ -189,7 +192,7 @@
void _signalPostTimeoutError(error, [stackTrace]) {
var scheduleError = new ScheduleError.from(this, error,
stackTrace: stackTrace);
- errors.add(scheduleError);
+ _addError(scheduleError);
if (_state == ScheduleState.DONE) {
throw new StateError(
"An out-of-band error was caught after the test timed out.\n"
@@ -203,7 +206,7 @@
/// out-of-band callbacks are properly handled by the scheduled test.
///
/// The top-level `wrapAsync` function should usually be used in preference to
- /// this.
+ /// this in test code.
Function wrapAsync(fn(arg)) {
if (_state == ScheduleState.DONE) {
throw new StateError("wrapAsync called after the schedule has finished "
@@ -237,6 +240,35 @@
};
}
+ /// Like [wrapAsync], this ensures that the current task queue waits for
+ /// out-of-band asynchronous code, and that errors raised in that code are
+ /// handled correctly. However, [wrapFuture] wraps a [Future] chain rather
+ /// than a single callback.
+ ///
+ /// The returned [Future] completes to the same value or error as [future].
+ ///
+ /// The top-level `wrapFuture` function should usually be used in preference
+ /// to this in test code.
+ Future wrapFuture(Future future) {
+ var doneCallback = wrapAsync((_) => null);
+ done() => new Future.immediate(null).then(doneCallback);
+
+ future = future.then((result) {
+ done();
+ return result;
+ }).catchError((e) {
+ signalError(e);
+ done();
+ throw e;
+ });
+
+ // Don't top-level the error, since it's already been signaled to the
+ // schedule.
+ future.catchError((_) => null);
+
+ return future;
+ }
+
/// Returns a string representation of all errors registered on this schedule.
String errorString() {
if (errors.isEmpty) return "The schedule had no errors.";
@@ -285,6 +317,14 @@
if (_noPendingCallbacks == null) _noPendingCallbacks = new Completer();
return _noPendingCallbacks.future;
}
+
+ /// Register an error in the schedule's error list. This ensures that there
+ /// are no duplicate errors, and that all errors are wrapped in
+ /// [ScheduleError].
+ void _addError(error) {
+ if (errors.contains(error)) return;
+ errors.add(new ScheduleError.from(this, error));
+ }
}
/// An enum of states for a [Schedule].
@@ -333,6 +373,10 @@
TaskQueue._(this.name, this._schedule);
+ /// Whether this queue is currently running.
+ bool get isRunning => _schedule.state == ScheduleState.RUNNING &&
+ _schedule.currentQueue == this;
+
/// Schedules a task, [fn], to run asynchronously as part of this queue. Tasks
/// will be run in the order they're scheduled. In [fn] returns a [Future],
/// tasks after it won't be run until that [Future] completes.
@@ -343,8 +387,23 @@
///
/// If [description] is passed, it's used to describe the task for debugging
/// purposes when an error occurs.
+ ///
+ /// If this is called when this queue is currently running, it will run [fn]
+ /// on the next event loop iteration rather than adding it to a queue--this is
+ /// known as a "nested task". The current task will not complete until [fn]
+ /// (and any [Future] it returns) has finished running. Any errors in [fn]
+ /// will automatically be handled. Nested tasks run in parallel, unlike
+ /// top-level tasks which run in sequence.
Future schedule(fn(), [String description]) {
- var task = new Task(fn, this, description);
+ if (isRunning) {
+ var task = _schedule.currentTask;
+ var wrappedFn = () => _schedule.wrapFuture(
+ new Future.immediate(null).then((_) => fn()));
+ if (task == null) return wrappedFn();
+ return task.runChild(wrappedFn, description);
+ }
+
+ var task = new Task(fn, description, this);
_contents.add(task);
return task.result;
}
@@ -362,7 +421,7 @@
_taskFuture = null;
_schedule.heartbeat();
}).catchError((e) {
- if (_error != null) _schedule.errors.add(_error);
+ if (_error != null) _schedule._addError(_error);
throw new ScheduleError.from(_schedule, e);
});
}).whenComplete(() {
@@ -379,7 +438,7 @@
void _signalError(ScheduleError error) {
// If multiple errors are detected while a task is running, make sure the
// earlier ones are recorded in the schedule.
- if (_error != null) _schedule.errors.add(_error);
+ if (_error != null) _schedule._addError(_error);
_error = error;
}
diff --git a/pkg/scheduled_test/lib/src/schedule_error.dart b/pkg/scheduled_test/lib/src/schedule_error.dart
index 76d6e00..d36be02 100644
--- a/pkg/scheduled_test/lib/src/schedule_error.dart
+++ b/pkg/scheduled_test/lib/src/schedule_error.dart
@@ -26,13 +26,15 @@
/// The state of the schedule at the time the error was detected.
final ScheduleState _stateWhenDetected;
+ int get hashCode => schedule.hashCode ^ task.hashCode ^ queue.hashCode ^
+ _stateWhenDetected.hashCode ^ error.hashCode ^ stackTrace.hashCode ^
+ cause.hashCode;
+
/// Creates a new [ScheduleError] wrapping [error]. The metadata in
/// [AsyncError]s and [ScheduleError]s will be preserved.
factory ScheduleError.from(Schedule schedule, error, {stackTrace,
AsyncError cause}) {
- if (error is ScheduleError) {
- if (schedule == null) schedule = error.schedule;
- }
+ if (error is ScheduleError) return error;
if (error is AsyncError) {
// Overwrite the explicit stack trace, because it probably came from a
@@ -52,6 +54,11 @@
this.queue = schedule.currentQueue,
this._stateWhenDetected = schedule.state;
+ bool operator ==(other) => other is ScheduleError && task == other.task &&
+ queue == other.queue && _stateWhenDetected == other._stateWhenDetected &&
+ error == other.error && stackTrace == other.stackTrace &&
+ cause == other.cause;
+
String toString() {
var result = new StringBuffer();
diff --git a/pkg/scheduled_test/lib/src/task.dart b/pkg/scheduled_test/lib/src/task.dart
index 558a9cd..0819bd0 100644
--- a/pkg/scheduled_test/lib/src/task.dart
+++ b/pkg/scheduled_test/lib/src/task.dart
@@ -5,25 +5,49 @@
library task;
import 'dart:async';
+import 'dart:collection';
+import 'future_group.dart';
import 'schedule.dart';
import 'utils.dart';
typedef Future TaskBody();
/// A single task to be run as part of a [TaskQueue].
+///
+/// There are two levels of tasks. **Top-level tasks** are created by calling
+/// [TaskQueue.schedule] before the queue in question is running. They're run in
+/// sequence as part of that [TaskQueue]. **Nested tasks** are created by
+/// calling [TaskQueue.schedule] once the queue is already running, and are run
+/// in parallel as part of a top-level task.
class Task {
/// The queue to which this [Task] belongs.
final TaskQueue queue;
+ // TODO(nweiz): make this a read-only view when issue 8321 is fixed.
+ /// Child tasks that have been spawned while running this task. This will be
+ /// empty if this task is a nested task.
+ final children = new Queue<Task>();
+
+ /// A [FutureGroup] that will complete once all current child tasks are
+ /// finished running. This will be null if no child tasks are currently
+ /// running.
+ FutureGroup _childGroup;
+
/// A description of this task. Used for debugging. May be `null`.
final String description;
+ /// The parent task, if this is a nested task that was started while another
+ /// task was running. This will be `null` for top-level tasks.
+ final Task parent;
+
/// The body of the task.
TaskBody fn;
- /// The identifier of the task. This is unique within [queue]. It's used for
- /// debugging when [description] isn't provided.
+ /// The identifier of the task. For top-level tasks, this is the index of the
+ /// task within [queue]; for nested tasks, this is the index within
+ /// [parent.children]. It's used for debugging when [description] isn't
+ /// provided.
int _id;
/// A Future that will complete to the return value of [fn] once this task
@@ -31,10 +55,19 @@
Future get result => _resultCompleter.future;
final _resultCompleter = new Completer();
- Task(fn(), this.queue, this.description) {
- _id = this.queue.contents.length;
+ Task(fn(), String description, TaskQueue queue)
+ : this._(fn, description, queue, null, queue.contents.length);
+
+ Task._child(fn(), String description, Task parent)
+ : this._(fn, description, parent.queue, parent, parent.children.length);
+
+ Task._(fn(), this.description, this.queue, this.parent, this._id) {
this.fn = () {
- var future = new Future.immediate(null).then((_) => fn());
+ var future = new Future.immediate(null).then((_) => fn())
+ .whenComplete(() {
+ if (_childGroup == null || _childGroup.completed) return;
+ return _childGroup.future;
+ });
chainToCompleter(future, _resultCompleter);
return future;
};
@@ -44,6 +77,22 @@
result.catchError((_) {});
}
+ /// Run [fn] as a child of this task. Returns a Future that will complete with
+ /// the result of the child task. This task will not complete until [fn] has
+ /// finished.
+ Future runChild(fn(), String description) {
+ var task = new Task._child(fn, description, this);
+ children.add(task);
+ if (_childGroup == null || _childGroup.completed) {
+ _childGroup = new FutureGroup();
+ }
+ // Ignore errors in the FutureGroup; they'll get picked up via wrapFuture,
+ // and we don't want them to short-circuit the other Futures.
+ _childGroup.add(task.result.catchError((_) {}));
+ task.fn();
+ return task.result;
+ }
+
String toString() => description == null ? "#$_id" : description;
/// Returns a detailed representation of [queue] with this task highlighted.
diff --git a/pkg/scheduled_test/test/scheduled_test_test.dart b/pkg/scheduled_test/test/scheduled_test_test.dart
index dcf5bea..f07024d 100644
--- a/pkg/scheduled_test/test/scheduled_test_test.dart
+++ b/pkg/scheduled_test/test/scheduled_test_test.dart
@@ -142,6 +142,74 @@
});
});
+ expectTestsFail('an out-of-band failure in wrapFuture is handled', () {
+ mock_clock.mock().run();
+ test('test', () {
+ schedule(() {
+ wrapFuture(sleep(1).then((_) => expect('foo', equals('bar'))));
+ });
+ schedule(() => sleep(2));
+ });
+ });
+
+ expectTestsFail('an out-of-band failure in wrapFuture that finishes after '
+ 'the schedule is handled', () {
+ mock_clock.mock().run();
+ test('test', () {
+ schedule(() {
+ wrapFuture(sleep(2).then((_) => expect('foo', equals('bar'))));
+ });
+ schedule(() => sleep(1));
+ });
+ });
+
+ expectTestsPass("wrapFuture should return the value of the wrapped future",
+ () {
+ test('test', () {
+ schedule(() {
+ expect(wrapFuture(pumpEventQueue().then((_) => 'foo')),
+ completion(equals('foo')));
+ });
+ });
+ });
+
+ expectTestsPass("wrapFuture should pass through the error of the wrapped "
+ "future", () {
+ var error;
+ test('test 1', () {
+ schedule(() {
+ wrapFuture(pumpEventQueue().then((_) {
+ throw 'error';
+ })).catchError(wrapAsync((e) {
+ error = e.error;
+ }));
+ });
+ });
+
+ test('test 2', () {
+ expect(error, equals('error'));
+ });
+ }, passing: ['test 2']);
+
+ expectTestsPass("scheduled blocks whose return values are passed to "
+ "wrapFuture should report exceptions once", () {
+ var errors;
+ test('test 1', () {
+ currentSchedule.onException.schedule(() {
+ errors = currentSchedule.errors;
+ });
+
+ wrapFuture(schedule(() {
+ throw 'error';
+ }));
+ });
+
+ test('test 2', () {
+ expect(errors, everyElement(new isInstanceOf<ScheduleError>()));
+ expect(errors.map((e) => e.error), equals(['error']));
+ });
+ }, passing: ['test 2']);
+
expectTestsFail('an out-of-band error reported via signalError is '
'handled', () {
mock_clock.mock().run();
@@ -524,6 +592,40 @@
});
}, passing: ['test 2']);
+ expectTestsPass('currentSchedule.errors contains multiple out-of-band errors '
+ 'from both the main task queue and onException in onComplete reported '
+ 'via wrapFuture', () {
+ mock_clock.mock().run();
+ var errors;
+ test('test 1', () {
+ currentSchedule.onComplete.schedule(() {
+ errors = currentSchedule.errors;
+ });
+
+ currentSchedule.onException.schedule(() {
+ wrapFuture(sleep(1).then((_) {
+ throw 'error3';
+ }));
+ wrapFuture(sleep(2).then((_) {
+ throw 'error4';
+ }));
+ });
+
+ wrapFuture(sleep(1).then((_) {
+ throw 'error1';
+ }));
+ wrapFuture(sleep(2).then((_) {
+ throw 'error2';
+ }));
+ });
+
+ test('test 2', () {
+ expect(errors, everyElement(new isInstanceOf<ScheduleError>()));
+ expect(errors.map((e) => e.error),
+ orderedEquals(['error1', 'error2', 'error3', 'error4']));
+ });
+ }, passing: ['test 2']);
+
expectTestsPass('currentSchedule.errors contains both an out-of-band error '
'and an error raised afterwards in a task', () {
mock_clock.mock().run();
@@ -917,4 +1019,158 @@
// TODO(nweiz): test out-of-band post-timeout errors that are detected after
// the test finishes once we can detect top-level errors (issue 8417).
+
+ expectTestsPass("nested schedule() runs its function immediately (but "
+ "asynchronously)", () {
+ test('test', () {
+ schedule(() {
+ var nestedScheduleRun = false;
+ schedule(() {
+ nestedScheduleRun = true;
+ });
+
+ expect(nestedScheduleRun, isFalse);
+ expect(pumpEventQueue().then((_) => nestedScheduleRun),
+ completion(isTrue));
+ });
+ });
+ });
+
+ expectTestsPass("out-of-band schedule() runs its function immediately (but "
+ "asynchronously)", () {
+ mock_clock.mock().run();
+ test('test', () {
+ schedule(() {
+ wrapFuture(sleep(1).then((_) {
+ var nestedScheduleRun = false;
+ schedule(() {
+ nestedScheduleRun = true;
+ });
+
+ expect(nestedScheduleRun, isFalse);
+ expect(pumpEventQueue().then((_) => nestedScheduleRun),
+ completion(isTrue));
+ }));
+ });
+ });
+ });
+
+ expectTestsPass("nested schedule() calls don't wait for one another", () {
+ mock_clock.mock().run();
+ test('test', () {
+ var sleepFinished = false;
+ schedule(() {
+ schedule(() => sleep(1).then((_) {
+ sleepFinished = true;
+ }));
+ schedule(() => expect(sleepFinished, isFalse));
+ });
+ });
+ });
+
+ expectTestsPass("nested schedule() calls block their parent task", () {
+ mock_clock.mock().run();
+ test('test', () {
+ var sleepFinished = false;
+ schedule(() {
+ schedule(() => sleep(1).then((_) {
+ sleepFinished = true;
+ }));
+ });
+
+ schedule(() => expect(sleepFinished, isTrue));
+ });
+ });
+
+ expectTestsPass("out-of-band schedule() calls block their parent queue", () {
+ mock_clock.mock().run();
+ test('test', () {
+ var scheduleRun = false;
+ wrapFuture(sleep(1).then((_) {
+ schedule(() => sleep(1).then((_) {
+ scheduleRun = true;
+ }));
+ }));
+
+ currentSchedule.onComplete.schedule(() => expect(scheduleRun, isTrue));
+ });
+ });
+
+ expectTestsPass("nested schedule() calls forward their Future values", () {
+ mock_clock.mock().run();
+ test('test', () {
+ schedule(() {
+ expect(schedule(() => 'foo'), completion(equals('foo')));
+ });
+ });
+ });
+
+ expectTestsPass("errors in nested schedule() calls are properly registered",
+ () {
+ var errors;
+ test('test 1', () {
+ currentSchedule.onException.schedule(() {
+ errors = currentSchedule.errors;
+ });
+
+ schedule(() {
+ schedule(() {
+ throw 'error';
+ });
+ });
+ });
+
+ test('test 2', () {
+ expect(errors, everyElement(new isInstanceOf<ScheduleError>()));
+ expect(errors.map((e) => e.error), equals(['error']));
+ });
+ }, passing: ['test 2']);
+
+ expectTestsPass("nested scheduled blocks whose return values are passed to "
+ "wrapFuture should report exceptions once", () {
+ var errors;
+ test('test 1', () {
+ currentSchedule.onException.schedule(() {
+ errors = currentSchedule.errors;
+ });
+
+ schedule(() {
+ wrapFuture(schedule(() {
+ throw 'error';
+ }));
+
+ return pumpEventQueue();
+ });
+ });
+
+ test('test 2', () {
+ expect(errors, everyElement(new isInstanceOf<ScheduleError>()));
+ expect(errors.map((e) => e.error), equals(['error']));
+ });
+ }, passing: ['test 2']);
+
+ expectTestsPass("a nested task failing shouldn't short-circuit the parent "
+ "task", () {
+ var parentTaskFinishedBeforeOnComplete = false;
+ test('test 1', () {
+ var parentTaskFinished = false;
+ currentSchedule.onComplete.schedule(() {
+ parentTaskFinishedBeforeOnComplete = parentTaskFinished;
+ });
+
+ schedule(() {
+ schedule(() {
+ throw 'error';
+ });
+
+ return sleep(1).then((_) {
+ parentTaskFinished = true;
+ });
+ });
+ });
+
+ test('test 2', () {
+ expect(parentTaskFinishedBeforeOnComplete, isTrue);
+ });
+ }, passing: ['test 2']);
}
diff --git a/pkg/serialization/lib/src/basic_rule.dart b/pkg/serialization/lib/src/basic_rule.dart
index 303a9e1..19a8bca 100644
--- a/pkg/serialization/lib/src/basic_rule.dart
+++ b/pkg/serialization/lib/src/basic_rule.dart
@@ -271,7 +271,7 @@
* This represents a field in an object. It is intended to be used as part of
* a [_FieldList].
*/
-abstract class _Field implements Comparable {
+abstract class _Field implements Comparable<_Field> {
/** The FieldList that contains us. */
final _FieldList fieldList;
diff --git a/pkg/unittest/lib/compact_vm_config.dart b/pkg/unittest/lib/compact_vm_config.dart
index 6b46a65..ede7010 100644
--- a/pkg/unittest/lib/compact_vm_config.dart
+++ b/pkg/unittest/lib/compact_vm_config.dart
@@ -89,24 +89,24 @@
var duration = (new DateTime.now()).difference(startTime);
var buffer = new StringBuffer();
// \r moves back to the beginnig of the current line.
- buffer.add('\r${_timeString(duration)} ');
- buffer.add(_GREEN);
- buffer.add('+');
- buffer.add(passed);
- buffer.add(_NONE);
- if (failed != 0) buffer.add(_RED);
- buffer.add(' -');
- buffer.add(failed);
- if (failed != 0) buffer.add(_NONE);
- buffer.add(': ');
- buffer.add(color);
+ buffer.write('\r${_timeString(duration)} ');
+ buffer.write(_GREEN);
+ buffer.write('+');
+ buffer.write(passed);
+ buffer.write(_NONE);
+ if (failed != 0) buffer.write(_RED);
+ buffer.write(' -');
+ buffer.write(failed);
+ if (failed != 0) buffer.write(_NONE);
+ buffer.write(': ');
+ buffer.write(color);
int nonVisible = _nonVisiblePrefix + color.length +
(failed != 0 ? (_RED.length + _NONE.length) : 0);
int len = buffer.length - nonVisible;
var mx = MAX_LINE - len;
- buffer.add(_snippet(message, MAX_LINE - len));
- buffer.add(_NONE);
+ buffer.write(_snippet(message, MAX_LINE - len));
+ buffer.write(_NONE);
// Pad the rest of the line so that it looks erased.
len = buffer.length - nonVisible - _NONE.length;
@@ -114,11 +114,11 @@
_lastLength = len;
} else {
while (len < _lastLength) {
- buffer.add(' ');
+ buffer.write(' ');
_lastLength--;
}
}
- stdout.writeString(buffer.toString());
+ stdout.addString(buffer.toString());
}
String _padTime(int time) =>
@@ -146,11 +146,11 @@
if (i < words.length - 4) {
// Require at least 3 words at the end.
var buffer = new StringBuffer();
- buffer.add(words.first);
- buffer.add(' ...');
+ buffer.write(words.first);
+ buffer.write(' ...');
for (; i < words.length; i++) {
- buffer.add(' ');
- buffer.add(words[i]);
+ buffer.write(' ');
+ buffer.write(words[i]);
}
return buffer.toString();
}
diff --git a/pkg/unittest/lib/html_config.dart b/pkg/unittest/lib/html_config.dart
index d4c9cdb..9c64b53 100644
--- a/pkg/unittest/lib/html_config.dart
+++ b/pkg/unittest/lib/html_config.dart
@@ -18,17 +18,17 @@
document.body.innerHtml = "PASS";
} else {
var newBody = new StringBuffer();
- newBody.add("<table class='unittest-table'><tbody>");
- newBody.add(passed == results.length && uncaughtError == null
+ newBody.write("<table class='unittest-table'><tbody>");
+ newBody.write(passed == results.length && uncaughtError == null
? "<tr><td colspan='3' class='unittest-pass'>PASS</td></tr>"
: "<tr><td colspan='3' class='unittest-fail'>FAIL</td></tr>");
for (final test_ in results) {
- newBody.add(_toHtml(test_));
+ newBody.write(_toHtml(test_));
}
if (uncaughtError != null) {
- newBody.add('''<tr>
+ newBody.write('''<tr>
<td>--</td>
<td class="unittest-error">ERROR</td>
<td>Uncaught error: $uncaughtError</td>
@@ -36,12 +36,12 @@
}
if (passed == results.length && uncaughtError == null) {
- newBody.add("""
+ newBody.write("""
<tr><td colspan='3' class='unittest-pass'>
All ${passed} tests passed
</td></tr>""");
} else {
- newBody.add("""
+ newBody.write("""
<tr><td colspan='3'>Total
<span class='unittest-pass'>${passed} passed</span>,
<span class='unittest-fail'>${failed} failed</span>
@@ -49,7 +49,7 @@
${errors + (uncaughtError == null ? 0 : 1)} errors</span>
</td></tr>""");
}
- newBody.add("</tbody></table>");
+ newBody.write("</tbody></table>");
document.body.innerHtml = newBody.toString();
}
}
diff --git a/pkg/unittest/lib/src/string_matchers.dart b/pkg/unittest/lib/src/string_matchers.dart
index 7f04a0c..933d0a0 100644
--- a/pkg/unittest/lib/src/string_matchers.dart
+++ b/pkg/unittest/lib/src/string_matchers.dart
@@ -74,11 +74,11 @@
var character = _string[i];
if (isWhitespace(character)) {
if (!skipSpace) {
- result.add(' ');
+ result.write(' ');
skipSpace = true;
}
} else {
- result.add(character);
+ result.write(character);
skipSpace = false;
}
}
diff --git a/pkg/unittest/lib/test_controller.js b/pkg/unittest/lib/test_controller.js
index 5332c19..34d56ed 100644
--- a/pkg/unittest/lib/test_controller.js
+++ b/pkg/unittest/lib/test_controller.js
@@ -135,6 +135,8 @@
try {
main();
} catch (e) {
+ dartPrint(e);
+ if (e.stack) dartPrint(e.stack);
window.postMessage('unittest-suite-fail', '*');
return;
}
diff --git a/pkg/webdriver/lib/webdriver.dart b/pkg/webdriver/lib/webdriver.dart
index 837836b..ffb0dff 100644
--- a/pkg/webdriver/lib/webdriver.dart
+++ b/pkg/webdriver/lib/webdriver.dart
@@ -52,17 +52,11 @@
*/
void writeStringToFile(String fileName, String contents) {
- var file = new File(fileName);
- var ostream = file.openOutputStream(FileMode.WRITE);
- ostream.writeString(contents);
- ostream.close();
+ new File(fileName).writeAsStringSync(contents);
}
void writeBytesToFile(String fileName, List<int> contents) {
- var file = new File(fileName);
- var ostream = file.openOutputStream(FileMode.WRITE);
- ostream.write(contents);
- ostream.close();
+ new File(fileName).writeAsBytesSync(contents);
}
class WebDriverError {
diff --git a/runtime/bin/bin.gypi b/runtime/bin/bin.gypi
index d2a1657..51d6740 100644
--- a/runtime/bin/bin.gypi
+++ b/runtime/bin/bin.gypi
@@ -4,12 +4,8 @@
{
'variables': {
- 'crypto_cc_file': '<(SHARED_INTERMEDIATE_DIR)/crypto_gen.cc',
'io_cc_file': '<(SHARED_INTERMEDIATE_DIR)/io_gen.cc',
'io_patch_cc_file': '<(SHARED_INTERMEDIATE_DIR)/io_patch_gen.cc',
- 'json_cc_file': '<(SHARED_INTERMEDIATE_DIR)/json_gen.cc',
- 'uri_cc_file': '<(SHARED_INTERMEDIATE_DIR)/uri_gen.cc',
- 'utf_cc_file': '<(SHARED_INTERMEDIATE_DIR)/utf_gen.cc',
'builtin_in_cc_file': 'builtin_in.cc',
'builtin_cc_file': '<(SHARED_INTERMEDIATE_DIR)/builtin_gen.cc',
'snapshot_in_cc_file': 'snapshot_in.cc',
@@ -48,55 +44,6 @@
]
},
{
- 'target_name': 'generate_crypto_cc_file',
- 'type': 'none',
- 'variables': {
- 'crypto_dart': '<(SHARED_INTERMEDIATE_DIR)/crypto_gen.dart',
- },
- 'includes': [
- 'crypto_sources.gypi',
- ],
- 'actions': [
- {
- 'action_name': 'generate_crypto_dart',
- 'inputs': [
- '../tools/concat_library.py',
- '<@(_sources)',
- ],
- 'outputs': [
- '<(crypto_dart)',
- ],
- 'action': [
- 'python',
- '<@(_inputs)',
- '--output', '<(crypto_dart)',
- ],
- 'message': 'Generating ''<(crypto_dart)'' file.',
- },
- {
- 'action_name': 'generate_crypto_cc',
- 'inputs': [
- '../tools/create_string_literal.py',
- '<(builtin_in_cc_file)',
- '<(crypto_dart)',
- ],
- 'outputs': [
- '<(crypto_cc_file)',
- ],
- 'action': [
- 'python',
- 'tools/create_string_literal.py',
- '--output', '<(crypto_cc_file)',
- '--input_cc', '<(builtin_in_cc_file)',
- '--include', 'bin/builtin.h',
- '--var_name', 'Builtin::crypto_source_',
- '<(crypto_dart)',
- ],
- 'message': 'Generating ''<(crypto_cc_file)'' file.'
- },
- ]
- },
- {
'target_name': 'generate_io_cc_file',
'type': 'none',
'variables': {
@@ -179,163 +126,12 @@
]
},
{
- 'target_name': 'generate_json_cc_file',
- 'type': 'none',
- 'variables': {
- 'json_dart': '<(SHARED_INTERMEDIATE_DIR)/json_gen.dart',
- },
- 'includes': [
- 'json_sources.gypi',
- ],
- 'actions': [
- {
- 'action_name': 'generate_json_dart',
- 'inputs': [
- '../tools/concat_library.py',
- '<@(_sources)',
- ],
- 'outputs': [
- '<(json_dart)',
- ],
- 'action': [
- 'python',
- '<@(_inputs)',
- '--output', '<(json_dart)',
- ],
- 'message': 'Generating ''<(json_dart)'' file.',
- },
- {
- 'action_name': 'generate_json_cc',
- 'inputs': [
- '../tools/create_string_literal.py',
- '<(builtin_in_cc_file)',
- '<(json_dart)',
- ],
- 'outputs': [
- '<(json_cc_file)',
- ],
- 'action': [
- 'python',
- 'tools/create_string_literal.py',
- '--output', '<(json_cc_file)',
- '--input_cc', '<(builtin_in_cc_file)',
- '--include', 'bin/builtin.h',
- '--var_name', 'Builtin::json_source_',
- '<(json_dart)',
- ],
- 'message': 'Generating ''<(json_cc_file)'' file.'
- },
- ]
- },
- {
- 'target_name': 'generate_uri_cc_file',
- 'type': 'none',
- 'variables': {
- 'uri_dart': '<(SHARED_INTERMEDIATE_DIR)/uri_gen.dart',
- },
- 'includes': [
- 'uri_sources.gypi',
- ],
- 'actions': [
- {
- 'action_name': 'generate_uri_dart',
- 'inputs': [
- '../tools/concat_library.py',
- '<@(_sources)',
- ],
- 'outputs': [
- '<(uri_dart)',
- ],
- 'action': [
- 'python',
- '<@(_inputs)',
- '--output', '<(uri_dart)',
- ],
- 'message': 'Generating ''<(uri_dart)'' file.'
- },
- {
- 'action_name': 'generate_uri_cc',
- 'inputs': [
- '../tools/create_string_literal.py',
- '<(builtin_in_cc_file)',
- '<(uri_dart)',
- ],
- 'outputs': [
- '<(uri_cc_file)',
- ],
- 'action': [
- 'python',
- 'tools/create_string_literal.py',
- '--output', '<(uri_cc_file)',
- '--input_cc', '<(builtin_in_cc_file)',
- '--include', 'bin/builtin.h',
- '--var_name', 'Builtin::uri_source_',
- '<(uri_dart)',
- ],
- 'message': 'Generating ''<(uri_cc_file)'' file.'
- },
- ]
- },
- {
- 'target_name': 'generate_utf_cc_file',
- 'type': 'none',
- 'variables': {
- 'utf_dart': '<(SHARED_INTERMEDIATE_DIR)/utf_gen.dart',
- },
- 'includes': [
- 'utf_sources.gypi',
- ],
- 'actions': [
- {
- 'action_name': 'generate_utf_dart',
- 'inputs': [
- '../tools/concat_library.py',
- '<@(_sources)',
- ],
- 'outputs': [
- '<(utf_dart)',
- ],
- 'action': [
- 'python',
- '<@(_inputs)',
- '--output', '<(utf_dart)',
- ],
- 'message': 'Generating ''<(utf_dart)'' file.',
- },
- {
- 'action_name': 'generate_utf_cc',
- 'inputs': [
- '../tools/create_string_literal.py',
- '<(builtin_in_cc_file)',
- '<(utf_dart)',
- ],
- 'outputs': [
- '<(utf_cc_file)',
- ],
- 'action': [
- 'python',
- 'tools/create_string_literal.py',
- '--output', '<(utf_cc_file)',
- '--input_cc', '<(builtin_in_cc_file)',
- '--include', 'bin/builtin.h',
- '--var_name', 'Builtin::utf_source_',
- '<(utf_dart)',
- ],
- 'message': 'Generating ''<(utf_cc_file)'' file.'
- },
- ]
- },
- {
'target_name': 'libdart_builtin',
'type': 'static_library',
'dependencies': [
'generate_builtin_cc_file',
- 'generate_crypto_cc_file',
'generate_io_cc_file',
'generate_io_patch_cc_file',
- 'generate_json_cc_file',
- 'generate_uri_cc_file',
- 'generate_utf_cc_file',
],
'include_dirs': [
'..',
@@ -459,12 +255,8 @@
'builtin_gen_snapshot.cc',
# Include generated source files.
'<(builtin_cc_file)',
- '<(crypto_cc_file)',
'<(io_cc_file)',
'<(io_patch_cc_file)',
- '<(json_cc_file)',
- '<(uri_cc_file)',
- '<(utf_cc_file)',
],
'conditions': [
['OS=="win"', {
@@ -594,12 +386,8 @@
'builtin.cc',
# Include generated source files.
'<(builtin_cc_file)',
- '<(crypto_cc_file)',
'<(io_cc_file)',
'<(io_patch_cc_file)',
- '<(json_cc_file)',
- '<(uri_cc_file)',
- '<(utf_cc_file)',
'snapshot_empty.cc',
],
'conditions': [
@@ -649,12 +437,8 @@
'builtin.cc',
# Include generated source files.
'<(builtin_cc_file)',
- '<(crypto_cc_file)',
'<(io_cc_file)',
'<(io_patch_cc_file)',
- '<(json_cc_file)',
- '<(uri_cc_file)',
- '<(utf_cc_file)',
],
'includes': [
'builtin_impl_sources.gypi',
diff --git a/runtime/bin/builtin.cc b/runtime/bin/builtin.cc
index f9218df..2af1339 100644
--- a/runtime/bin/builtin.cc
+++ b/runtime/bin/builtin.cc
@@ -13,12 +13,8 @@
Builtin::builtin_lib_props Builtin::builtin_libraries_[] = {
/* { url_, source_, patch_url_, patch_source_, has_natives_ } */
{ DartUtils::kBuiltinLibURL, builtin_source_, NULL, NULL, true },
- { DartUtils::kJsonLibURL, json_source_, NULL, NULL, false },
- { DartUtils::kUriLibURL, uri_source_, NULL, NULL, false },
- { DartUtils::kCryptoLibURL, crypto_source_, NULL, NULL, false },
{ DartUtils::kIOLibURL, io_source_,
DartUtils::kIOLibPatchURL, io_patch_, true },
- { DartUtils::kUtfLibURL, utf_source_, NULL, NULL, false }
};
diff --git a/runtime/bin/builtin.h b/runtime/bin/builtin.h
index ca33e57..4a33b6c 100644
--- a/runtime/bin/builtin.h
+++ b/runtime/bin/builtin.h
@@ -25,11 +25,7 @@
// the builtin_libraries_ array in builtin.cc and builtin_nolib.cc.
enum BuiltinLibraryId {
kBuiltinLibrary = 0,
- kJsonLibrary,
- kUriLibrary,
- kCryptoLibrary,
kIOLibrary,
- kUtfLibrary,
kInvalidLibrary,
};
@@ -45,14 +41,9 @@
static Dart_NativeFunction BuiltinNativeLookup(Dart_Handle name,
int argument_count);
- static const char async_source_[];
static const char builtin_source_[];
- static const char crypto_source_[];
static const char io_source_[];
static const char io_patch_[];
- static const char json_source_[];
- static const char uri_source_[];
- static const char utf_source_[];
static const char web_source_[];
typedef struct {
diff --git a/runtime/bin/builtin_gen_snapshot.cc b/runtime/bin/builtin_gen_snapshot.cc
index fd64f46..af68759 100644
--- a/runtime/bin/builtin_gen_snapshot.cc
+++ b/runtime/bin/builtin_gen_snapshot.cc
@@ -13,12 +13,8 @@
Builtin::builtin_lib_props Builtin::builtin_libraries_[] = {
/* { url_, source_, patch_url_, patch_source_, has_natives_ } */
{ DartUtils::kBuiltinLibURL, builtin_source_, NULL, NULL, true },
- { DartUtils::kJsonLibURL, json_source_, NULL, NULL, false },
- { DartUtils::kUriLibURL, uri_source_, NULL, NULL, false },
- { DartUtils::kCryptoLibURL, crypto_source_, NULL, NULL, false },
{ DartUtils::kIOLibURL, io_source_,
DartUtils::kIOLibPatchURL, io_patch_, true },
- { DartUtils::kUtfLibURL, utf_source_, NULL, NULL, false }
};
diff --git a/runtime/bin/builtin_impl_sources.gypi b/runtime/bin/builtin_impl_sources.gypi
index 54c8707..2f0f9d5 100644
--- a/runtime/bin/builtin_impl_sources.gypi
+++ b/runtime/bin/builtin_impl_sources.gypi
@@ -47,6 +47,8 @@
'fdutils_macos.cc',
'hashmap_test.cc',
'isolate_data.h',
+ 'native_service.h',
+ 'native_service.cc',
'thread.h',
'utils.h',
'utils_android.cc',
diff --git a/runtime/bin/builtin_nolib.cc b/runtime/bin/builtin_nolib.cc
index be941a7..048aa86 100644
--- a/runtime/bin/builtin_nolib.cc
+++ b/runtime/bin/builtin_nolib.cc
@@ -13,11 +13,7 @@
Builtin::builtin_lib_props Builtin::builtin_libraries_[] = {
/* { url_, source_, patch_url_, patch_source_, has_natives_ } */
{ DartUtils::kBuiltinLibURL, NULL, NULL, NULL, true },
- { DartUtils::kJsonLibURL, NULL, NULL, NULL, false },
- { DartUtils::kUriLibURL, NULL, NULL, NULL, false },
- { DartUtils::kCryptoLibURL, NULL, NULL, NULL, false },
{ DartUtils::kIOLibURL, NULL, NULL, NULL, true },
- { DartUtils::kUtfLibURL, NULL, NULL, NULL, false }
};
@@ -58,7 +54,7 @@
Dart_Handle url = DartUtils::NewString(builtin_libraries_[id].url_);
Dart_Handle library = Dart_LookupLibrary(url);
if (Dart_IsError(library)) {
- ASSERT(id > kUtfLibrary);
+ ASSERT(id > kIOLibrary);
library = Dart_LoadLibrary(url, Source(id));
if (!Dart_IsError(library) && (builtin_libraries_[id].has_natives_)) {
// Setup the native resolver for built in library functions.
diff --git a/runtime/bin/crypto.cc b/runtime/bin/crypto.cc
index e4db17c..af5886d 100644
--- a/runtime/bin/crypto.cc
+++ b/runtime/bin/crypto.cc
@@ -23,7 +23,7 @@
delete[] buffer;
Dart_ThrowException(DartUtils::NewDartOSError());
}
- Dart_Handle result = Dart_NewByteArray(count);
+ Dart_Handle result = Dart_NewTypedData(kUint8, count);
if (Dart_IsError(result)) {
delete[] buffer;
Dart_Handle error = DartUtils::NewString("Failed to allocate storage.");
diff --git a/runtime/bin/crypto_android.cc b/runtime/bin/crypto_android.cc
index 08f6dac..701a208 100644
--- a/runtime/bin/crypto_android.cc
+++ b/runtime/bin/crypto_android.cc
@@ -2,8 +2,11 @@
// 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 <errno.h>
-#include <fcntl.h>
+#include "platform/globals.h"
+#if defined(TARGET_OS_ANDROID)
+
+#include <errno.h> // NOLINT
+#include <fcntl.h> // NOLINT
#include "bin/fdutils.h"
#include "bin/crypto.h"
@@ -16,3 +19,5 @@
close(fd);
return bytes_read == count;
}
+
+#endif // defined(TARGET_OS_ANDROID)
diff --git a/runtime/bin/crypto_linux.cc b/runtime/bin/crypto_linux.cc
index 08f6dac..7ff6b6e 100644
--- a/runtime/bin/crypto_linux.cc
+++ b/runtime/bin/crypto_linux.cc
@@ -2,8 +2,11 @@
// 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 <errno.h>
-#include <fcntl.h>
+#include "platform/globals.h"
+#if defined(TARGET_OS_LINUX)
+
+#include <errno.h> // NOLINT
+#include <fcntl.h> // NOLINT
#include "bin/fdutils.h"
#include "bin/crypto.h"
@@ -16,3 +19,5 @@
close(fd);
return bytes_read == count;
}
+
+#endif // defined(TARGET_OS_LINUX)
diff --git a/runtime/bin/crypto_macos.cc b/runtime/bin/crypto_macos.cc
index 08f6dac..0a44ff8 100644
--- a/runtime/bin/crypto_macos.cc
+++ b/runtime/bin/crypto_macos.cc
@@ -2,8 +2,11 @@
// 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 <errno.h>
-#include <fcntl.h>
+#include "platform/globals.h"
+#if defined(TARGET_OS_MACOS)
+
+#include <errno.h> // NOLINT
+#include <fcntl.h> // NOLINT
#include "bin/fdutils.h"
#include "bin/crypto.h"
@@ -16,3 +19,5 @@
close(fd);
return bytes_read == count;
}
+
+#endif // defined(TARGET_OS_MACOS)
diff --git a/runtime/bin/crypto_sources.gypi b/runtime/bin/crypto_sources.gypi
deleted file mode 100644
index fc8f00f..0000000
--- a/runtime/bin/crypto_sources.gypi
+++ /dev/null
@@ -1,20 +0,0 @@
-# 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.
-
-# This file contains all sources for the dart:crypto library.
-#
-# TODO(ager): ../lib/crypto/crypto_vm.dart should be removed when the
-# VM can use the #source directive for libraries. At that point
-# ../../sdk/lib/crypto/crypto.dart should be the only crypto library file.
-{
- 'sources': [
- '../lib/crypto/crypto_vm.dart',
- '../../sdk/lib/crypto/crypto_utils.dart',
- '../../sdk/lib/crypto/hash_utils.dart',
- '../../sdk/lib/crypto/hmac.dart',
- '../../sdk/lib/crypto/md5.dart',
- '../../sdk/lib/crypto/sha1.dart',
- '../../sdk/lib/crypto/sha256.dart',
- ],
-}
diff --git a/runtime/bin/crypto_win.cc b/runtime/bin/crypto_win.cc
index 640efb2..8f4952f 100644
--- a/runtime/bin/crypto_win.cc
+++ b/runtime/bin/crypto_win.cc
@@ -3,6 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
#define _CRT_RAND_S
+
+#include "platform/globals.h"
+#if defined(TARGET_OS_WINDOWS)
+
#include "bin/crypto.h"
@@ -20,3 +24,5 @@
}
return true;
}
+
+#endif // defined(TARGET_OS_WINDOWS)
diff --git a/runtime/bin/dartutils.cc b/runtime/bin/dartutils.cc
index 2f281ac..ea53472 100644
--- a/runtime/bin/dartutils.cc
+++ b/runtime/bin/dartutils.cc
@@ -18,14 +18,10 @@
const char* DartUtils::kASyncLibURL = "dart:async";
const char* DartUtils::kBuiltinLibURL = "dart:builtin";
const char* DartUtils::kCoreLibURL = "dart:core";
-const char* DartUtils::kCryptoLibURL = "dart:crypto";
const char* DartUtils::kIOLibURL = "dart:io";
const char* DartUtils::kIOLibPatchURL = "dart:io-patch";
-const char* DartUtils::kJsonLibURL = "dart:json";
const char* DartUtils::kUriLibURL = "dart:uri";
const char* DartUtils::kUtfLibURL = "dart:utf";
-const char* DartUtils::kIsolateLibURL = "dart:isolate";
-
const char* DartUtils::kIdFieldName = "_id";
@@ -149,11 +145,6 @@
}
-bool DartUtils::IsDartCryptoLibURL(const char* url_name) {
- return (strcmp(url_name, kCryptoLibURL) == 0);
-}
-
-
bool DartUtils::IsDartIOLibURL(const char* url_name) {
return (strcmp(url_name, kIOLibURL) == 0);
}
@@ -164,21 +155,6 @@
}
-bool DartUtils::IsDartJsonLibURL(const char* url_name) {
- return (strcmp(url_name, kJsonLibURL) == 0);
-}
-
-
-bool DartUtils::IsDartUriLibURL(const char* url_name) {
- return (strcmp(url_name, kUriLibURL) == 0);
-}
-
-
-bool DartUtils::IsDartUtfLibURL(const char* url_name) {
- return (strcmp(url_name, kUtfLibURL) == 0);
-}
-
-
Dart_Handle DartUtils::CanonicalizeURL(CommandLineOptions* url_mapping,
Dart_Handle library,
const char* url_str) {
@@ -314,16 +290,8 @@
if (tag == kImportTag) {
// Handle imports of other built-in libraries present in the SDK.
Builtin::BuiltinLibraryId id;
- if (DartUtils::IsDartCryptoLibURL(url_string)) {
- id = Builtin::kCryptoLibrary;
- } else if (DartUtils::IsDartIOLibURL(url_string)) {
+ if (DartUtils::IsDartIOLibURL(url_string)) {
id = Builtin::kIOLibrary;
- } else if (DartUtils::IsDartJsonLibURL(url_string)) {
- id = Builtin::kJsonLibrary;
- } else if (DartUtils::IsDartUriLibURL(url_string)) {
- id = Builtin::kUriLibrary;
- } else if (DartUtils::IsDartUtfLibURL(url_string)) {
- id = Builtin::kUtfLibrary;
} else {
return Dart_Error("Do not know how to load '%s'", url_string);
}
diff --git a/runtime/bin/dartutils.h b/runtime/bin/dartutils.h
index eca83d5..07404e5 100644
--- a/runtime/bin/dartutils.h
+++ b/runtime/bin/dartutils.h
@@ -97,12 +97,8 @@
const char* val);
static bool IsDartSchemeURL(const char* url_name);
static bool IsDartExtensionSchemeURL(const char* url_name);
- static bool IsDartCryptoLibURL(const char* url_name);
static bool IsDartIOLibURL(const char* url_name);
static bool IsDartBuiltinLibURL(const char* url_name);
- static bool IsDartJsonLibURL(const char* url_name);
- static bool IsDartUriLibURL(const char* url_name);
- static bool IsDartUtfLibURL(const char* url_name);
static Dart_Handle CanonicalizeURL(CommandLineOptions* url_mapping,
Dart_Handle library,
const char* url_str);
@@ -166,10 +162,8 @@
static const char* kASyncLibURL;
static const char* kBuiltinLibURL;
static const char* kCoreLibURL;
- static const char* kCryptoLibURL;
static const char* kIOLibURL;
static const char* kIOLibPatchURL;
- static const char* kJsonLibURL;
static const char* kUriLibURL;
static const char* kUtfLibURL;
static const char* kIsolateLibURL;
diff --git a/runtime/bin/dbg_connection_android.cc b/runtime/bin/dbg_connection_android.cc
index ce25559..0fc57ac 100644
--- a/runtime/bin/dbg_connection_android.cc
+++ b/runtime/bin/dbg_connection_android.cc
@@ -2,10 +2,13 @@
// 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 <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/epoll.h>
+#include "platform/globals.h"
+#if defined(TARGET_OS_ANDROID)
+
+#include <errno.h> // NOLINT
+#include <stdio.h> // NOLINT
+#include <stdlib.h> // NOLINT
+#include <sys/epoll.h> // NOLINT
#include "bin/dbg_connection.h"
#include "bin/fdutils.h"
@@ -115,3 +118,5 @@
intptr_t DebuggerConnectionImpl::Receive(intptr_t socket, char* buf, int len) {
return TEMP_FAILURE_RETRY(read(socket, buf, len));
}
+
+#endif // defined(TARGET_OS_ANDROID)
diff --git a/runtime/bin/dbg_connection_linux.cc b/runtime/bin/dbg_connection_linux.cc
index ce25559..c278987f 100644
--- a/runtime/bin/dbg_connection_linux.cc
+++ b/runtime/bin/dbg_connection_linux.cc
@@ -2,10 +2,13 @@
// 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 <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/epoll.h>
+#include "platform/globals.h"
+#if defined(TARGET_OS_LINUX)
+
+#include <errno.h> // NOLINT
+#include <stdio.h> // NOLINT
+#include <stdlib.h> // NOLINT
+#include <sys/epoll.h> // NOLINT
#include "bin/dbg_connection.h"
#include "bin/fdutils.h"
@@ -115,3 +118,5 @@
intptr_t DebuggerConnectionImpl::Receive(intptr_t socket, char* buf, int len) {
return TEMP_FAILURE_RETRY(read(socket, buf, len));
}
+
+#endif // defined(TARGET_OS_LINUX)
diff --git a/runtime/bin/dbg_connection_macos.cc b/runtime/bin/dbg_connection_macos.cc
index 675e129..a3e17fd 100644
--- a/runtime/bin/dbg_connection_macos.cc
+++ b/runtime/bin/dbg_connection_macos.cc
@@ -2,12 +2,15 @@
// 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 <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/event.h>
-#include <unistd.h>
+#include "platform/globals.h"
+#if defined(TARGET_OS_MACOS)
+
+#include <errno.h> // NOLINT
+#include <stdio.h> // NOLINT
+#include <stdlib.h> // NOLINT
+#include <string.h> // NOLINT
+#include <sys/event.h> // NOLINT
+#include <unistd.h> // NOLINT
#include "bin/dartutils.h"
#include "bin/dbg_connection.h"
@@ -169,3 +172,5 @@
intptr_t DebuggerConnectionImpl::Receive(intptr_t socket, char* buf, int len) {
return TEMP_FAILURE_RETRY(read(socket, buf, len));
}
+
+#endif // defined(TARGET_OS_MACOS)
diff --git a/runtime/bin/dbg_connection_win.cc b/runtime/bin/dbg_connection_win.cc
index 1c9bf54..a241c85 100644
--- a/runtime/bin/dbg_connection_win.cc
+++ b/runtime/bin/dbg_connection_win.cc
@@ -2,6 +2,9 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_WINDOWS)
+
#include "bin/dbg_connection.h"
#include "bin/eventhandler.h"
@@ -39,3 +42,5 @@
ClientSocket* client_socket = reinterpret_cast<ClientSocket*>(socket);
return recv(client_socket->socket(), buf, len, 0);
}
+
+#endif // defined(TARGET_OS_WINDOWS)
diff --git a/runtime/bin/directory.cc b/runtime/bin/directory.cc
index a4b5e08..1493b2d 100644
--- a/runtime/bin/directory.cc
+++ b/runtime/bin/directory.cc
@@ -9,10 +9,14 @@
#include "include/dart_api.h"
#include "platform/assert.h"
-dart::Mutex Directory::mutex_;
-int Directory::service_ports_size_ = 0;
-Dart_Port* Directory::service_ports_ = NULL;
-int Directory::service_ports_index_ = 0;
+
+// Forward declaration.
+static void DirectoryService(Dart_Port, Dart_Port, Dart_CObject*);
+
+NativeService Directory::directory_service_("DirectoryService",
+ DirectoryService,
+ 16);
+
void FUNCTION_NAME(Directory_Current)(Dart_NativeArguments args) {
Dart_EnterScope();
@@ -236,10 +240,10 @@
}
-void DirectoryService(Dart_Port dest_port_id,
- Dart_Port reply_port_id,
- Dart_CObject* message) {
- CObject* response = CObject::False();
+static void DirectoryService(Dart_Port dest_port_id,
+ Dart_Port reply_port_id,
+ Dart_CObject* message) {
+ CObject* response = CObject::IllegalArgumentError();
CObjectArray request(message);
if (message->type == Dart_CObject::kArray) {
if (request.Length() > 1 && request[0]->IsInt32()) {
@@ -274,27 +278,7 @@
Dart_Port Directory::GetServicePort() {
- MutexLocker lock(&mutex_);
- if (service_ports_size_ == 0) {
- ASSERT(service_ports_ == NULL);
- service_ports_size_ = 16;
- service_ports_ = new Dart_Port[service_ports_size_];
- service_ports_index_ = 0;
- for (int i = 0; i < service_ports_size_; i++) {
- service_ports_[i] = ILLEGAL_PORT;
- }
- }
-
- Dart_Port result = service_ports_[service_ports_index_];
- if (result == ILLEGAL_PORT) {
- result = Dart_NewNativePort("DirectoryService",
- DirectoryService,
- true);
- ASSERT(result != ILLEGAL_PORT);
- service_ports_[service_ports_index_] = result;
- }
- service_ports_index_ = (service_ports_index_ + 1) % service_ports_size_;
- return result;
+ return directory_service_.GetServicePort();
}
@@ -342,14 +326,16 @@
bool SyncDirectoryListing::HandleDirectory(char* dir_name) {
Dart_Handle dir_name_dart = DartUtils::NewString(dir_name);
- Dart_Handle dir = Dart_New(directory_class_, Dart_Null(), 1, &dir_name_dart);
+ Dart_Handle dir =
+ Dart_New(directory_class_, Dart_Null(), 1, &dir_name_dart);
Dart_Invoke(results_, add_string_, 1, &dir);
return true;
}
bool SyncDirectoryListing::HandleFile(char* file_name) {
Dart_Handle file_name_dart = DartUtils::NewString(file_name);
- Dart_Handle file = Dart_New(file_class_, Dart_Null(), 1, &file_name_dart);
+ Dart_Handle file =
+ Dart_New(file_class_, Dart_Null(), 1, &file_name_dart);
Dart_Invoke(results_, add_string_, 1, &file);
return true;
}
diff --git a/runtime/bin/directory.h b/runtime/bin/directory.h
index b038ad1..87bea9f 100644
--- a/runtime/bin/directory.h
+++ b/runtime/bin/directory.h
@@ -7,6 +7,7 @@
#include "bin/builtin.h"
#include "bin/dartutils.h"
+#include "bin/native_service.h"
#include "platform/globals.h"
#include "platform/thread.h"
@@ -22,8 +23,8 @@
class AsyncDirectoryListing : public DirectoryListing {
public:
enum Response {
- kListDirectory = 0,
- kListFile = 1,
+ kListFile = 0,
+ kListDirectory = 1,
kListError = 2,
kListDone = 3
};
@@ -99,10 +100,7 @@
static Dart_Port GetServicePort();
private:
- static dart::Mutex mutex_;
- static int service_ports_size_;
- static Dart_Port* service_ports_;
- static int service_ports_index_;
+ static NativeService directory_service_;
DISALLOW_ALLOCATION();
DISALLOW_IMPLICIT_CONSTRUCTORS(Directory);
diff --git a/runtime/bin/directory_android.cc b/runtime/bin/directory_android.cc
index 8b40073..fd58c81 100644
--- a/runtime/bin/directory_android.cc
+++ b/runtime/bin/directory_android.cc
@@ -2,34 +2,43 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_ANDROID)
+
#include "bin/directory.h"
-#include <dirent.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <unistd.h>
+#include <dirent.h> // NOLINT
+#include <errno.h> // NOLINT
+#include <string.h> // NOLINT
+#include <sys/param.h> // NOLINT
+#include <sys/stat.h> // NOLINT
+#include <unistd.h> // NOLINT
#include "bin/file.h"
#include "bin/platform.h"
class PathBuffer {
public:
- PathBuffer() : length(0) { }
+ PathBuffer() : length(0) {
+ data = new char[PATH_MAX + 1];
+ }
+ ~PathBuffer() {
+ delete[] data;
+ }
-
- char data[PATH_MAX + 1];
+ char* data;
int length;
bool Add(const char* name) {
- size_t written = snprintf(data + length,
- PATH_MAX - length,
- "%s",
- name);
+ int written = snprintf(data + length,
+ PATH_MAX - length,
+ "%s",
+ name);
data[PATH_MAX] = '\0';
- if (written == strnlen(name, PATH_MAX + 1)) {
+ if (written <= PATH_MAX - length &&
+ written >= 0 &&
+ static_cast<size_t>(written) == strnlen(name, PATH_MAX + 1)) {
length += written;
return true;
} else {
@@ -45,12 +54,11 @@
};
-
// Forward declarations.
-static bool ListRecursively(const char* dir_name,
+static bool ListRecursively(PathBuffer* path,
bool recursive,
DirectoryListing* listing);
-static bool DeleteRecursively(const char* dir_name);
+static bool DeleteRecursively(PathBuffer* path);
static void PostError(DirectoryListing *listing,
@@ -59,25 +67,6 @@
}
-static PathBuffer* ComputeFullPath(const char* dir_name) {
- PathBuffer* path = new PathBuffer();
- char* abs_path;
- do {
- abs_path = realpath(dir_name, path->data);
- } while (abs_path == NULL && errno == EINTR);
- if (abs_path == NULL) {
- delete path;
- return NULL;
- }
- path->length = strnlen(path->data, PATH_MAX);
- if (path->Add(File::PathSeparator())) {
- return path;
- } else {
- delete path;
- return NULL;
- }
-}
-
static bool HandleDir(char* dir_name,
PathBuffer* path,
bool recursive,
@@ -87,11 +76,11 @@
if (!path->Add(dir_name)) {
PostError(listing, path->data);
return false;
- }
return listing->HandleDirectory(path->data) &&
- (!recursive || ListRecursively(path->data, recursive, listing));
+ (!recursive || ListRecursively(path, recursive, listing));
}
+
static bool HandleFile(char* file_name,
PathBuffer* path,
DirectoryListing *listing) {
@@ -103,28 +92,22 @@
}
-static bool ListRecursively(const char* dir_name,
+static bool ListRecursively(PathBuffer* path,
bool recursive,
DirectoryListing *listing) {
+ if (!path->Add(File::PathSeparator())) {
+ PostError(listing, path->data);
+ return false;
+ }
DIR* dir_pointer;
do {
- dir_pointer = opendir(dir_name);
+ dir_pointer = opendir(path->data);
} while (dir_pointer == NULL && errno == EINTR);
if (dir_pointer == NULL) {
- PostError(listing, dir_name);
+ PostError(listing, path->data);
return false;
}
- // Compute full path for the directory currently being listed. The
- // path buffer will be used to construct the current path in the
- // recursive traversal. path_length does not always equal
- // strlen(path) but indicates the current prefix of path that is the
- // path of the current directory in the traversal.
- PathBuffer* path = ComputeFullPath(dir_name);
- if (path == NULL) {
- PostError(listing, dir_name);
- return false;
- }
// Iterate the directory and post the directories and files to the
// ports.
int path_length = path->length;
@@ -188,14 +171,13 @@
if (status != 0) {
errno = status;
success = false;
- PostError(listing, dir_name);
+ PostError(listing, path->data);
}
if (closedir(dir_pointer) == -1) {
success = false;
- PostError(listing, dir_name);
+ PostError(listing, path->data);
}
- delete path;
return success;
}
@@ -211,36 +193,31 @@
PathBuffer* path) {
if (strcmp(dir_name, ".") == 0) return true;
if (strcmp(dir_name, "..") == 0) return true;
- return path->Add(dir_name) && DeleteRecursively(path->data);
+ return path->Add(dir_name) && DeleteRecursively(path);
}
-static bool DeleteRecursively(const char* dir_name) {
+static bool DeleteRecursively(PathBuffer* path) {
+ if (!path->Add(File::PathSeparator())) return false;
// Do not recurse into links for deletion. Instead delete the link.
struct stat st;
- if (TEMP_FAILURE_RETRY(lstat(dir_name, &st)) == -1) {
+ if (TEMP_FAILURE_RETRY(lstat(path->data, &st)) == -1) {
return false;
} else if (S_ISLNK(st.st_mode)) {
- return (remove(dir_name) == 0);
+ return (remove(path->data) == 0);
}
// Not a link. Attempt to open as a directory and recurse into the
// directory.
DIR* dir_pointer;
do {
- dir_pointer = opendir(dir_name);
+ dir_pointer = opendir(path->data);
} while (dir_pointer == NULL && errno == EINTR);
if (dir_pointer == NULL) {
return false;
}
- // Compute full path for the directory currently being deleted. The
- // path buffer will be used to construct the current path in the
- // recursive traversal.
- PathBuffer* path = ComputeFullPath(dir_name);
- if (path == NULL) return false;
-
// Iterate the directory and delete all files and directories.
int path_length = path->length;
int read = 0;
@@ -293,14 +270,12 @@
}
path->Reset(path_length);
}
- delete path;
if ((read != 0) ||
(closedir(dir_pointer) == -1) ||
- (remove(dir_name) == -1)) {
+ (remove(path->data) == -1)) {
return false;
}
-
return success;
}
@@ -308,8 +283,12 @@
bool Directory::List(const char* dir_name,
bool recursive,
DirectoryListing *listing) {
- bool completed = ListRecursively(dir_name, recursive, listing);
- return completed;
+ PathBuffer path;
+ if (!path.Add(dir_name)) {
+ PostError(listing, dir_name);
+ return false;
+ }
+ return ListRecursively(&path, recursive, listing);
}
@@ -385,9 +364,9 @@
// dir_template. Creates the directory with the permissions specified
// by the process umask.
// The return value must be freed by the caller.
- PathBuffer* path = new PathBuffer();
- path->Add(const_template);
- if (path->length == 0) {
+ PathBuffer path;
+ path.Add(const_template);
+ if (path.length == 0) {
// Android does not have a /tmp directory. A partial substitute,
// suitable for bring-up work and tests, is to create a tmp
// directory in /data/local/tmp.
@@ -400,12 +379,11 @@
mkdir(ANDROID_TEMP_DIR, 0777);
}
path->Add(ANDROID_TEMP_DIR "/tmp/temp_dir1_");
- } else if ((path->data)[path->length - 1] == '/') {
- path->Add("temp_dir_");
+ } else if ((path.data)[path.length - 1] == '/') {
+ path.Add("temp_dir_");
}
- if (!path->Add("XXXXXX")) {
+ if (!path.Add("XXXXXX")) {
// Pattern has overflowed.
- delete path;
return NULL;
}
char* result;
@@ -413,14 +391,12 @@
result = MakeTempDirectory(path->data);
} while (result == NULL && errno == EINTR);
if (result == NULL) {
- delete path;
return NULL;
}
- int length = strnlen(path->data, PATH_MAX);
+ int length = strnlen(path.data, PATH_MAX);
result = static_cast<char*>(malloc(length + 1));
- strncpy(result, path->data, length);
+ strncpy(result, path.data, length);
result[length] = '\0';
- delete path;
return result;
}
@@ -429,7 +405,11 @@
if (!recursive) {
return (TEMP_FAILURE_RETRY(remove(dir_name)) == 0);
} else {
- return DeleteRecursively(dir_name);
+ PathBuffer path;
+ if (!path.Add(dir_name)) {
+ return false;
+ }
+ return DeleteRecursively(&path);
}
}
@@ -439,3 +419,5 @@
if (exists != EXISTS) return false;
return (TEMP_FAILURE_RETRY(rename(path, new_path)) == 0);
}
+
+#endif // defined(TARGET_OS_ANDROID)
diff --git a/runtime/bin/directory_linux.cc b/runtime/bin/directory_linux.cc
index 7b94aac..30cc860 100644
--- a/runtime/bin/directory_linux.cc
+++ b/runtime/bin/directory_linux.cc
@@ -2,34 +2,43 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_LINUX)
+
#include "bin/directory.h"
-#include <dirent.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <unistd.h>
+#include <dirent.h> // NOLINT
+#include <errno.h> // NOLINT
+#include <string.h> // NOLINT
+#include <sys/param.h> // NOLINT
+#include <sys/stat.h> // NOLINT
+#include <unistd.h> // NOLINT
#include "bin/file.h"
#include "bin/platform.h"
class PathBuffer {
public:
- PathBuffer() : length(0) { }
+ PathBuffer() : length(0) {
+ data = new char[PATH_MAX + 1];
+ }
+ ~PathBuffer() {
+ delete[] data;
+ }
-
- char data[PATH_MAX + 1];
+ char* data;
int length;
bool Add(const char* name) {
- size_t written = snprintf(data + length,
- PATH_MAX - length,
- "%s",
- name);
+ int written = snprintf(data + length,
+ PATH_MAX - length,
+ "%s",
+ name);
data[PATH_MAX] = '\0';
- if (written == strnlen(name, PATH_MAX + 1)) {
+ if (written <= PATH_MAX - length &&
+ written >= 0 &&
+ static_cast<size_t>(written) == strnlen(name, PATH_MAX + 1)) {
length += written;
return true;
} else {
@@ -46,10 +55,10 @@
// Forward declarations.
-static bool ListRecursively(const char* dir_name,
+static bool ListRecursively(PathBuffer* path,
bool recursive,
DirectoryListing* listing);
-static bool DeleteRecursively(const char* dir_name);
+static bool DeleteRecursively(PathBuffer* path);
static void PostError(DirectoryListing *listing,
@@ -58,25 +67,6 @@
}
-static PathBuffer* ComputeFullPath(const char* dir_name) {
- PathBuffer* path = new PathBuffer();
- char* abs_path;
- do {
- abs_path = realpath(dir_name, path->data);
- } while (abs_path == NULL && errno == EINTR);
- if (abs_path == NULL) {
- delete path;
- return NULL;
- }
- path->length = strnlen(path->data, PATH_MAX);
- if (path->Add(File::PathSeparator())) {
- return path;
- } else {
- delete path;
- return NULL;
- }
-}
-
static bool HandleDir(char* dir_name,
PathBuffer* path,
bool recursive,
@@ -88,9 +78,10 @@
return false;
}
return listing->HandleDirectory(path->data) &&
- (!recursive || ListRecursively(path->data, recursive, listing));
+ (!recursive || ListRecursively(path, recursive, listing));
}
+
static bool HandleFile(char* file_name,
PathBuffer* path,
DirectoryListing *listing) {
@@ -102,28 +93,22 @@
}
-static bool ListRecursively(const char* dir_name,
+static bool ListRecursively(PathBuffer* path,
bool recursive,
DirectoryListing *listing) {
+ if (!path->Add(File::PathSeparator())) {
+ PostError(listing, path->data);
+ return false;
+ }
DIR* dir_pointer;
do {
- dir_pointer = opendir(dir_name);
+ dir_pointer = opendir(path->data);
} while (dir_pointer == NULL && errno == EINTR);
if (dir_pointer == NULL) {
- PostError(listing, dir_name);
+ PostError(listing, path->data);
return false;
}
- // Compute full path for the directory currently being listed. The
- // path buffer will be used to construct the current path in the
- // recursive traversal. path_length does not always equal
- // strlen(path) but indicates the current prefix of path that is the
- // path of the current directory in the traversal.
- PathBuffer* path = ComputeFullPath(dir_name);
- if (path == NULL) {
- PostError(listing, dir_name);
- return false;
- }
// Iterate the directory and post the directories and files to the
// ports.
int path_length = path->length;
@@ -187,14 +172,13 @@
if (status != 0) {
errno = status;
success = false;
- PostError(listing, dir_name);
+ PostError(listing, path->data);
}
if (closedir(dir_pointer) == -1) {
success = false;
- PostError(listing, dir_name);
+ PostError(listing, path->data);
}
- delete path;
return success;
}
@@ -210,36 +194,31 @@
PathBuffer* path) {
if (strcmp(dir_name, ".") == 0) return true;
if (strcmp(dir_name, "..") == 0) return true;
- return path->Add(dir_name) && DeleteRecursively(path->data);
+ return path->Add(dir_name) && DeleteRecursively(path);
}
-static bool DeleteRecursively(const char* dir_name) {
+static bool DeleteRecursively(PathBuffer* path) {
+ if (!path->Add(File::PathSeparator())) return false;
// Do not recurse into links for deletion. Instead delete the link.
struct stat st;
- if (TEMP_FAILURE_RETRY(lstat(dir_name, &st)) == -1) {
+ if (TEMP_FAILURE_RETRY(lstat(path->data, &st)) == -1) {
return false;
} else if (S_ISLNK(st.st_mode)) {
- return (remove(dir_name) == 0);
+ return (remove(path->data) == 0);
}
// Not a link. Attempt to open as a directory and recurse into the
// directory.
DIR* dir_pointer;
do {
- dir_pointer = opendir(dir_name);
+ dir_pointer = opendir(path->data);
} while (dir_pointer == NULL && errno == EINTR);
if (dir_pointer == NULL) {
return false;
}
- // Compute full path for the directory currently being deleted. The
- // path buffer will be used to construct the current path in the
- // recursive traversal.
- PathBuffer* path = ComputeFullPath(dir_name);
- if (path == NULL) return false;
-
// Iterate the directory and delete all files and directories.
int path_length = path->length;
int read = 0;
@@ -292,14 +271,12 @@
}
path->Reset(path_length);
}
- delete path;
if ((read != 0) ||
(closedir(dir_pointer) == -1) ||
- (remove(dir_name) == -1)) {
+ (remove(path->data) == -1)) {
return false;
}
-
return success;
}
@@ -307,8 +284,12 @@
bool Directory::List(const char* dir_name,
bool recursive,
DirectoryListing *listing) {
- bool completed = ListRecursively(dir_name, recursive, listing);
- return completed;
+ PathBuffer path;
+ if (!path.Add(dir_name)) {
+ PostError(listing, dir_name);
+ return false;
+ }
+ return ListRecursively(&path, recursive, listing);
}
@@ -363,31 +344,28 @@
// dir_template. Creates the directory with the permissions specified
// by the process umask.
// The return value must be freed by the caller.
- PathBuffer* path = new PathBuffer();
- path->Add(const_template);
- if (path->length == 0) {
- path->Add("/tmp/temp_dir1_");
- } else if ((path->data)[path->length - 1] == '/') {
- path->Add("temp_dir_");
+ PathBuffer path;
+ path.Add(const_template);
+ if (path.length == 0) {
+ path.Add("/tmp/temp_dir1_");
+ } else if ((path.data)[path.length - 1] == '/') {
+ path.Add("temp_dir_");
}
- if (!path->Add("XXXXXX")) {
+ if (!path.Add("XXXXXX")) {
// Pattern has overflowed.
- delete path;
return NULL;
}
char* result;
do {
- result = mkdtemp(path->data);
+ result = mkdtemp(path.data);
} while (result == NULL && errno == EINTR);
if (result == NULL) {
- delete path;
return NULL;
}
- int length = strnlen(path->data, PATH_MAX);
+ int length = strnlen(path.data, PATH_MAX);
result = static_cast<char*>(malloc(length + 1));
- strncpy(result, path->data, length);
+ strncpy(result, path.data, length);
result[length] = '\0';
- delete path;
return result;
}
@@ -396,7 +374,11 @@
if (!recursive) {
return (TEMP_FAILURE_RETRY(remove(dir_name)) == 0);
} else {
- return DeleteRecursively(dir_name);
+ PathBuffer path;
+ if (!path.Add(dir_name)) {
+ return false;
+ }
+ return DeleteRecursively(&path);
}
}
@@ -406,3 +388,5 @@
if (exists != EXISTS) return false;
return (TEMP_FAILURE_RETRY(rename(path, new_path)) == 0);
}
+
+#endif // defined(TARGET_OS_LINUX)
diff --git a/runtime/bin/directory_macos.cc b/runtime/bin/directory_macos.cc
index 8a0f2b7..4685479 100644
--- a/runtime/bin/directory_macos.cc
+++ b/runtime/bin/directory_macos.cc
@@ -2,34 +2,43 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_MACOS)
+
#include "bin/directory.h"
-#include <dirent.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <unistd.h>
+#include <dirent.h> // NOLINT
+#include <errno.h> // NOLINT
+#include <string.h> // NOLINT
+#include <sys/param.h> // NOLINT
+#include <sys/stat.h> // NOLINT
+#include <unistd.h> // NOLINT
#include "bin/file.h"
#include "bin/platform.h"
class PathBuffer {
public:
- PathBuffer() : length(0) { }
+ PathBuffer() : length(0) {
+ data = new char[PATH_MAX + 1];
+ }
+ ~PathBuffer() {
+ delete[] data;
+ }
-
- char data[PATH_MAX + 1];
+ char* data;
int length;
bool Add(const char* name) {
- size_t written = snprintf(data + length,
- PATH_MAX - length,
- "%s",
- name);
+ int written = snprintf(data + length,
+ PATH_MAX - length,
+ "%s",
+ name);
data[PATH_MAX] = '\0';
- if (written == strlen(name)) {
+ if (written <= PATH_MAX - length &&
+ written >= 0 &&
+ static_cast<size_t>(written) == strlen(name)) {
length += written;
return true;
} else {
@@ -45,12 +54,11 @@
};
-
// Forward declarations.
-static bool ListRecursively(const char* dir_name,
+static bool ListRecursively(PathBuffer* path,
bool recursive,
DirectoryListing* listing);
-static bool DeleteRecursively(const char* dir_name);
+static bool DeleteRecursively(PathBuffer* path);
static void PostError(DirectoryListing *listing,
@@ -59,25 +67,6 @@
}
-static PathBuffer* ComputeFullPath(const char* dir_name) {
- PathBuffer* path = new PathBuffer();
- char* abs_path;
- do {
- abs_path = realpath(dir_name, path->data);
- } while (abs_path == NULL && errno == EINTR);
- if (abs_path == NULL) {
- delete path;
- return NULL;
- }
- path->length = strlen(path->data);
- if (path->Add(File::PathSeparator())) {
- return path;
- } else {
- delete path;
- return NULL;
- }
-}
-
static bool HandleDir(char* dir_name,
PathBuffer* path,
bool recursive,
@@ -89,9 +78,10 @@
return false;
}
return listing->HandleDirectory(path->data) &&
- (!recursive || ListRecursively(path->data, recursive, listing));
+ (!recursive || ListRecursively(path, recursive, listing));
}
+
static bool HandleFile(char* file_name,
PathBuffer* path,
DirectoryListing *listing) {
@@ -103,28 +93,22 @@
}
-static bool ListRecursively(const char* dir_name,
+static bool ListRecursively(PathBuffer* path,
bool recursive,
DirectoryListing *listing) {
+ if (!path->Add(File::PathSeparator())) {
+ PostError(listing, path->data);
+ return false;
+ }
DIR* dir_pointer;
do {
- dir_pointer = opendir(dir_name);
+ dir_pointer = opendir(path->data);
} while (dir_pointer == NULL && errno == EINTR);
if (dir_pointer == NULL) {
- PostError(listing, dir_name);
+ PostError(listing, path->data);
return false;
}
- // Compute full path for the directory currently being listed. The
- // path buffer will be used to construct the current path in the
- // recursive traversal. path_length does not always equal
- // strlen(path) but indicates the current prefix of path that is the
- // path of the current directory in the traversal.
- PathBuffer* path = ComputeFullPath(dir_name);
- if (path == NULL) {
- PostError(listing, dir_name);
- return false;
- }
// Iterate the directory and post the directories and files to the
// ports.
int path_length = path->length;
@@ -188,14 +172,13 @@
if (status != 0) {
errno = status;
success = false;
- PostError(listing, dir_name);
+ PostError(listing, path->data);
}
if (closedir(dir_pointer) == -1) {
success = false;
- PostError(listing, dir_name);
+ PostError(listing, path->data);
}
- delete path;
return success;
}
@@ -211,36 +194,31 @@
PathBuffer* path) {
if (strcmp(dir_name, ".") == 0) return true;
if (strcmp(dir_name, "..") == 0) return true;
- return path->Add(dir_name) && DeleteRecursively(path->data);
+ return path->Add(dir_name) && DeleteRecursively(path);
}
-static bool DeleteRecursively(const char* dir_name) {
- // Do not recurse into links for deletion. Instead delete the link.
+static bool DeleteRecursively(PathBuffer* path) {
+ if (!path->Add(File::PathSeparator())) return false;
+ // Do not recurse into links for deletion. Instead delete the link.
struct stat st;
- if (TEMP_FAILURE_RETRY(lstat(dir_name, &st)) == -1) {
+ if (TEMP_FAILURE_RETRY(lstat(path->data, &st)) == -1) {
return false;
} else if (S_ISLNK(st.st_mode)) {
- return (remove(dir_name) == 0);
+ return (remove(path->data) == 0);
}
// Not a link. Attempt to open as a directory and recurse into the
// directory.
DIR* dir_pointer;
do {
- dir_pointer = opendir(dir_name);
+ dir_pointer = opendir(path->data);
} while (dir_pointer == NULL && errno == EINTR);
if (dir_pointer == NULL) {
return false;
}
- // Compute full path for the directory currently being deleted. The
- // path buffer will be used to construct the current path in the
- // recursive traversal.
- PathBuffer* path = ComputeFullPath(dir_name);
- if (path == NULL) return false;
-
// Iterate the directory and delete all files and directories.
int path_length = path->length;
int read = 0;
@@ -293,14 +271,12 @@
}
path->Reset(path_length);
}
- delete path;
if ((read != 0) ||
(closedir(dir_pointer) == -1) ||
- (remove(dir_name) == -1)) {
+ (remove(path->data) == -1)) {
return false;
}
-
return success;
}
@@ -308,8 +284,12 @@
bool Directory::List(const char* dir_name,
bool recursive,
DirectoryListing *listing) {
- bool completed = ListRecursively(dir_name, recursive, listing);
- return completed;
+ PathBuffer path;
+ if (!path.Add(dir_name)) {
+ PostError(listing, dir_name);
+ return false;
+ }
+ return ListRecursively(&path, recursive, listing);
}
@@ -364,31 +344,28 @@
// dir_template. Creates the directory with the permissions specified
// by the process umask.
// The return value must be freed by the caller.
- PathBuffer* path = new PathBuffer();
- path->Add(const_template);
- if (path->length == 0) {
- path->Add("/tmp/temp_dir1_");
- } else if ((path->data)[path->length - 1] == '/') {
- path->Add("temp_dir_");
+ PathBuffer path;
+ path.Add(const_template);
+ if (path.length == 0) {
+ path.Add("/tmp/temp_dir1_");
+ } else if ((path.data)[path.length - 1] == '/') {
+ path.Add("temp_dir_");
}
- if (!path->Add("XXXXXX")) {
+ if (!path.Add("XXXXXX")) {
// Pattern has overflowed.
- delete path;
return NULL;
}
char* result;
do {
- result = mkdtemp(path->data);
+ result = mkdtemp(path.data);
} while (result == NULL && errno == EINTR);
if (result == NULL) {
- delete path;
return NULL;
}
- int length = strlen(path->data);
+ int length = strlen(path.data);
result = static_cast<char*>(malloc(length + 1));
- strncpy(result, path->data, length);
+ strncpy(result, path.data, length);
result[length] = '\0';
- delete path;
return result;
}
@@ -397,7 +374,11 @@
if (!recursive) {
return (TEMP_FAILURE_RETRY(remove(dir_name)) == 0);
} else {
- return DeleteRecursively(dir_name);
+ PathBuffer path;
+ if (!path.Add(dir_name)) {
+ return false;
+ }
+ return DeleteRecursively(&path);
}
}
@@ -407,3 +388,5 @@
if (exists != EXISTS) return false;
return (TEMP_FAILURE_RETRY(rename(path, new_path)) == 0);
}
+
+#endif // defined(TARGET_OS_MACOS)
diff --git a/runtime/bin/directory_win.cc b/runtime/bin/directory_win.cc
index 84242c6..97a2207 100644
--- a/runtime/bin/directory_win.cc
+++ b/runtime/bin/directory_win.cc
@@ -2,27 +2,38 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_WINDOWS)
+
#include "bin/directory.h"
-#include <errno.h>
-#include <sys/stat.h>
+#include <errno.h> // NOLINT
+#include <sys/stat.h> // NOLINT
#include "bin/log.h"
class PathBuffer {
public:
- PathBuffer() : length(0) { }
+ PathBuffer() : length(0) {
+ data = new wchar_t[MAX_PATH + 1];
+ }
- wchar_t data[MAX_PATH + 1];
+ ~PathBuffer() {
+ delete[] data;
+ }
+
+ wchar_t* data;
int length;
bool Add(const wchar_t* name) {
- size_t written = _snwprintf(data + length,
- MAX_PATH - length,
- L"%s",
- name);
+ int written = _snwprintf(data + length,
+ MAX_PATH - length,
+ L"%s",
+ name);
data[MAX_PATH] = L'\0';
- if (written == wcsnlen(name, MAX_PATH + 1)) {
+ if (written <= MAX_PATH - length &&
+ written >= 0 &&
+ static_cast<size_t>(written) == wcsnlen(name, MAX_PATH + 1)) {
length += written;
return true;
} else {
@@ -39,10 +50,10 @@
// Forward declarations.
-static bool ListRecursively(const wchar_t* dir_name,
+static bool ListRecursively(PathBuffer* path,
bool recursive,
DirectoryListing* listing);
-static bool DeleteRecursively(const wchar_t* dir_name);
+static bool DeleteRecursively(PathBuffer* path);
static void PostError(DirectoryListing* listing,
@@ -66,7 +77,7 @@
char* utf8_path = StringUtils::WideToUtf8(path->data);
bool ok = listing->HandleDirectory(utf8_path);
free(utf8_path);
- return ok && (!recursive || ListRecursively(path->data, recursive, listing));
+ return ok && (!recursive || ListRecursively(path, recursive, listing));
}
@@ -100,42 +111,11 @@
}
-static PathBuffer* ComputeFullSearchPath(const wchar_t* dir_name) {
- // GetFullPathName only works in a multi-threaded environment if
- // SetCurrentDirectory is not used. We currently have no plan for
- // exposing SetCurrentDirectory.
- PathBuffer* path = new PathBuffer();
-
- size_t written = GetFullPathNameW(dir_name, MAX_PATH + 1, path->data, NULL);
- // GetFullPathName only accepts input strings of size less than
- // MAX_PATH and returns 0 to indicate failure for paths longer than
- // that. Therefore the path buffer is always big enough.
- if (written == 0 || written > MAX_PATH) {
- delete path;
- return NULL;
- }
- path->length = written;
- if (path->Add(L"\\*")) {
- return path;
- } else {
- delete path;
- return NULL;
- }
-}
-
-
-static bool ListRecursively(const wchar_t* dir_name,
+static bool ListRecursively(PathBuffer* path,
bool recursive,
DirectoryListing* listing) {
- // Compute full path for the directory currently being listed. The
- // path buffer will be used to construct the current path in the
- // recursive traversal. path_length does not always equal
- // strlen(path) but indicates the current prefix of path that is the
- // path of the current directory in the traversal.
- PathBuffer* path = ComputeFullSearchPath(dir_name);
- if (path == NULL) {
- PostError(listing, dir_name);
- delete path;
+ if (!path->Add(L"\\*")) {
+ PostError(listing, path->data);
return false;
}
@@ -147,7 +127,6 @@
if (find_handle == INVALID_HANDLE_VALUE) {
PostError(listing, path->data);
- delete path;
return false;
}
@@ -167,14 +146,13 @@
if (GetLastError() != ERROR_NO_MORE_FILES) {
success = false;
- PostError(listing, dir_name);
+ PostError(listing, path->data);
}
if (FindClose(find_handle) == 0) {
success = false;
- PostError(listing, dir_name);
+ PostError(listing, path->data);
}
- delete path;
return success;
}
@@ -214,7 +192,7 @@
static bool DeleteDir(wchar_t* dir_name, PathBuffer* path) {
if (wcscmp(dir_name, L".") == 0) return true;
if (wcscmp(dir_name, L"..") == 0) return true;
- return path->Add(dir_name) && DeleteRecursively(path->data);
+ return path->Add(dir_name) && DeleteRecursively(path);
}
@@ -229,23 +207,17 @@
}
-static bool DeleteRecursively(const wchar_t* dir_name) {
+static bool DeleteRecursively(PathBuffer* path) {
// If the directory is a junction, it's pointing to some other place in the
// filesystem that we do not want to recurse into.
- DWORD attributes = GetFileAttributesW(dir_name);
+ DWORD attributes = GetFileAttributesW(path->data);
if ((attributes != INVALID_FILE_ATTRIBUTES) &&
(attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
// Just delete the junction itself.
- return RemoveDirectoryW(dir_name) != 0;
+ return RemoveDirectoryW(path->data) != 0;
}
- // Compute full path for the directory currently being deleted. The
- // path buffer will be used to construct the current path in the
- // recursive traversal. path_length does not always equal
- // strlen(path) but indicates the current prefix of path that is the
- // path of the current directory in the traversal.
- PathBuffer* path = ComputeFullSearchPath(dir_name);
- if (path == NULL) return false;
+ if (!path->Add(L"\\*")) return false;
WIN32_FIND_DATAW find_file_data;
HANDLE find_handle = FindFirstFileW(path->data, &find_file_data);
@@ -255,7 +227,6 @@
path->Reset(path_length);
if (find_handle == INVALID_HANDLE_VALUE) {
- delete path;
return false;
}
@@ -266,11 +237,10 @@
success = success && DeleteEntry(&find_file_data, path);
}
- delete path;
-
+ path->Reset(path_length - 1); // Drop the "\" from the end of the path.
if ((GetLastError() != ERROR_NO_MORE_FILES) ||
(FindClose(find_handle) == 0) ||
- (RemoveDirectoryW(dir_name) == 0)) {
+ (RemoveDirectoryW(path->data) == 0)) {
return false;
}
@@ -282,9 +252,13 @@
bool recursive,
DirectoryListing* listing) {
const wchar_t* system_name = StringUtils::Utf8ToWide(dir_name);
- bool completed = ListRecursively(system_name, recursive, listing);
+ PathBuffer path;
+ if (!path.Add(system_name)) {
+ PostError(listing, system_name);
+ return false;
+ }
free(const_cast<wchar_t*>(system_name));
- return completed;
+ return ListRecursively(&path, recursive, listing);
}
@@ -345,51 +319,45 @@
// dir_template. Creates this directory, with a default security
// descriptor inherited from its parent directory.
// The return value must be freed by the caller.
- PathBuffer* path = new PathBuffer();
+ PathBuffer path;
if (0 == strncmp(const_template, "", 1)) {
- path->length = GetTempPathW(MAX_PATH, path->data);
- if (path->length == 0) {
- delete path;
+ path.length = GetTempPathW(MAX_PATH, path.data);
+ if (path.length == 0) {
return NULL;
}
} else {
const wchar_t* system_template = StringUtils::Utf8ToWide(const_template);
- path->Add(system_template);
+ path.Add(system_template);
free(const_cast<wchar_t*>(system_template));
}
// Length of tempdir-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx is 44.
- if (path->length > MAX_PATH - 44) {
- delete path;
+ if (path.length > MAX_PATH - 44) {
return NULL;
}
- if ((path->data)[path->length - 1] == L'\\') {
+ if ((path.data)[path.length - 1] == L'\\') {
// No base name for the directory - use "tempdir".
- path->Add(L"tempdir");
+ path.Add(L"tempdir");
}
UUID uuid;
RPC_STATUS status = UuidCreateSequential(&uuid);
if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) {
- delete path;
return NULL;
}
RPC_WSTR uuid_string;
status = UuidToStringW(&uuid, &uuid_string);
if (status != RPC_S_OK) {
- delete path;
return NULL;
}
- path->Add(L"-");
+ path.Add(L"-");
// RPC_WSTR is an unsigned short*, so we cast to wchar_t*.
- path->Add(reinterpret_cast<wchar_t*>(uuid_string));
+ path.Add(reinterpret_cast<wchar_t*>(uuid_string));
RpcStringFreeW(&uuid_string);
- if (!CreateDirectoryW(path->data, NULL)) {
- delete path;
+ if (!CreateDirectoryW(path.data, NULL)) {
return NULL;
}
- char* result = StringUtils::WideToUtf8(path->data);
- delete path;
+ char* result = StringUtils::WideToUtf8(path.data);
return result;
}
@@ -400,7 +368,11 @@
if (!recursive) {
result = (RemoveDirectoryW(system_dir_name) != 0);
} else {
- result = DeleteRecursively(system_dir_name);
+ PathBuffer path;
+ if (!path.Add(system_dir_name)) {
+ return false;
+ }
+ result = DeleteRecursively(&path);
}
free(const_cast<wchar_t*>(system_dir_name));
return result;
@@ -417,7 +389,7 @@
// if the new_path is currently a directory we need to delete it
// first.
if (new_exists == EXISTS) {
- bool success = DeleteRecursively(system_new_path);
+ bool success = Delete(new_path, true);
if (!success) return false;
}
DWORD flags = MOVEFILE_WRITE_THROUGH;
@@ -427,3 +399,5 @@
free(const_cast<wchar_t*>(system_new_path));
return (move_status != 0);
}
+
+#endif // defined(TARGET_OS_WINDOWS)
diff --git a/runtime/bin/eventhandler_android.cc b/runtime/bin/eventhandler_android.cc
index 9c2b619..2a708fd 100644
--- a/runtime/bin/eventhandler_android.cc
+++ b/runtime/bin/eventhandler_android.cc
@@ -2,35 +2,28 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_ANDROID)
+
#include "bin/eventhandler.h"
-#include <errno.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/epoll.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <unistd.h>
+#include <errno.h> // NOLINT
+#include <pthread.h> // NOLINT
+#include <stdio.h> // NOLINT
+#include <string.h> // NOLINT
+#include <sys/epoll.h> // NOLINT
+#include <sys/stat.h> // NOLINT
+#include <unistd.h> // NOLINT
#include "bin/dartutils.h"
#include "bin/fdutils.h"
#include "bin/log.h"
+#include "bin/utils.h"
#include "platform/hashmap.h"
#include "platform/thread.h"
#include "platform/utils.h"
-int64_t GetCurrentTimeMilliseconds() {
- struct timeval tv;
- if (gettimeofday(&tv, NULL) < 0) {
- UNREACHABLE();
- return 0;
- }
- return ((static_cast<int64_t>(tv.tv_sec) * 1000000) + tv.tv_usec) / 1000;
-}
-
-
static const int kInterruptMessageSize = sizeof(InterruptMessage);
static const int kInfinityTimeout = -1;
static const int kTimerId = -1;
@@ -358,14 +351,14 @@
if (timeout_ == kInfinityTimeout) {
return kInfinityTimeout;
}
- intptr_t millis = timeout_ - GetCurrentTimeMilliseconds();
+ intptr_t millis = timeout_ - TimerUtils::GetCurrentTimeMilliseconds();
return (millis < 0) ? 0 : millis;
}
void EventHandlerImplementation::HandleTimeout() {
if (timeout_ != kInfinityTimeout) {
- intptr_t millis = timeout_ - GetCurrentTimeMilliseconds();
+ intptr_t millis = timeout_ - TimerUtils::GetCurrentTimeMilliseconds();
if (millis <= 0) {
DartUtils::PostNull(timeout_port_);
timeout_ = kInfinityTimeout;
@@ -431,3 +424,5 @@
// The hashmap does not support keys with value 0.
return dart::Utils::WordHash(fd + 1);
}
+
+#endif // defined(TARGET_OS_ANDROID)
diff --git a/runtime/bin/eventhandler_linux.cc b/runtime/bin/eventhandler_linux.cc
index 869e8bb..ce99057 100644
--- a/runtime/bin/eventhandler_linux.cc
+++ b/runtime/bin/eventhandler_linux.cc
@@ -2,35 +2,28 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_LINUX)
+
#include "bin/eventhandler.h"
-#include <errno.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/epoll.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <unistd.h>
+#include <errno.h> // NOLINT
+#include <pthread.h> // NOLINT
+#include <stdio.h> // NOLINT
+#include <string.h> // NOLINT
+#include <sys/epoll.h> // NOLINT
+#include <sys/stat.h> // NOLINT
+#include <unistd.h> // NOLINT
#include "bin/dartutils.h"
#include "bin/fdutils.h"
#include "bin/log.h"
+#include "bin/utils.h"
#include "platform/hashmap.h"
#include "platform/thread.h"
#include "platform/utils.h"
-int64_t GetCurrentTimeMilliseconds() {
- struct timeval tv;
- if (gettimeofday(&tv, NULL) < 0) {
- UNREACHABLE();
- return 0;
- }
- return ((static_cast<int64_t>(tv.tv_sec) * 1000000) + tv.tv_usec) / 1000;
-}
-
-
static const int kInterruptMessageSize = sizeof(InterruptMessage);
static const int kInfinityTimeout = -1;
static const int kTimerId = -1;
@@ -223,9 +216,13 @@
socket_map_.Remove(GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd));
delete sd;
} else {
- // Setup events to wait for.
- sd->SetPortAndMask(msg.dart_port, msg.data);
- UpdateEpollInstance(epoll_fd_, sd);
+ if ((msg.data & (1 << kInEvent)) != 0 && sd->IsClosedRead()) {
+ DartUtils::PostInt32(msg.dart_port, 1 << kCloseEvent);
+ } else {
+ // Setup events to wait for.
+ sd->SetPortAndMask(msg.dart_port, msg.data);
+ UpdateEpollInstance(epoll_fd_, sd);
+ }
}
}
}
@@ -268,8 +265,9 @@
}
} else {
// Prioritize data events over close and error events.
- if ((events & EPOLLIN) != 0) {
- if (FDUtils::AvailableBytes(sd->fd()) != 0) {
+ if ((events & (EPOLLIN | EPOLLHUP | EPOLLERR)) != 0) {
+ // If we have EPOLLIN and we have available bytes, report that.
+ if ((events & EPOLLIN) && FDUtils::AvailableBytes(sd->fd()) != 0) {
event_mask = (1 << kInEvent);
} else if ((events & EPOLLHUP) != 0) {
// If both EPOLLHUP and EPOLLERR are reported treat it as an
@@ -309,16 +307,6 @@
}
}
- // On pipes EPOLLHUP is reported without EPOLLIN when there is no
- // more data to read.
- if (sd->IsPipe()) {
- if (((events & EPOLLIN) == 0) &&
- ((events & EPOLLHUP) != 0)) {
- event_mask = (1 << kCloseEvent);
- sd->MarkClosedRead();
- }
- }
-
if ((events & EPOLLOUT) != 0) {
if ((events & EPOLLERR) != 0) {
event_mask = (1 << kErrorEvent);
@@ -350,6 +338,8 @@
}
}
}
+ // Handle after socket events, so we avoid closing a socket before we handle
+ // the current events.
HandleInterruptFd();
}
@@ -358,14 +348,14 @@
if (timeout_ == kInfinityTimeout) {
return kInfinityTimeout;
}
- intptr_t millis = timeout_ - GetCurrentTimeMilliseconds();
+ intptr_t millis = timeout_ - TimerUtils::GetCurrentTimeMilliseconds();
return (millis < 0) ? 0 : millis;
}
void EventHandlerImplementation::HandleTimeout() {
if (timeout_ != kInfinityTimeout) {
- intptr_t millis = timeout_ - GetCurrentTimeMilliseconds();
+ intptr_t millis = timeout_ - TimerUtils::GetCurrentTimeMilliseconds();
if (millis <= 0) {
DartUtils::PostNull(timeout_port_);
timeout_ = kInfinityTimeout;
@@ -431,3 +421,5 @@
// The hashmap does not support keys with value 0.
return dart::Utils::WordHash(fd + 1);
}
+
+#endif // defined(TARGET_OS_LINUX)
diff --git a/runtime/bin/eventhandler_macos.cc b/runtime/bin/eventhandler_macos.cc
index 51f1e70..fbced64 100644
--- a/runtime/bin/eventhandler_macos.cc
+++ b/runtime/bin/eventhandler_macos.cc
@@ -2,34 +2,27 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_MACOS)
+
#include "bin/eventhandler.h"
-#include <errno.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/event.h>
-#include <sys/time.h>
-#include <unistd.h>
+#include <errno.h> // NOLINT
+#include <pthread.h> // NOLINT
+#include <stdio.h> // NOLINT
+#include <string.h> // NOLINT
+#include <sys/event.h> // NOLINT
+#include <unistd.h> // NOLINT
#include "bin/dartutils.h"
#include "bin/fdutils.h"
#include "bin/log.h"
+#include "bin/utils.h"
#include "platform/hashmap.h"
#include "platform/thread.h"
#include "platform/utils.h"
-int64_t GetCurrentTimeMilliseconds() {
- struct timeval tv;
- if (gettimeofday(&tv, NULL) < 0) {
- UNREACHABLE();
- return 0;
- }
- return ((static_cast<int64_t>(tv.tv_sec) * 1000000) + tv.tv_usec) / 1000;
-}
-
-
static const int kInterruptMessageSize = sizeof(InterruptMessage);
static const int kInfinityTimeout = -1;
static const int kTimerId = -1;
@@ -145,8 +138,8 @@
EventHandlerImplementation::~EventHandlerImplementation() {
- TEMP_FAILURE_RETRY(close(interrupt_fds_[0]));
- TEMP_FAILURE_RETRY(close(interrupt_fds_[1]));
+ VOID_TEMP_FAILURE_RETRY(close(interrupt_fds_[0]));
+ VOID_TEMP_FAILURE_RETRY(close(interrupt_fds_[1]));
}
@@ -234,9 +227,13 @@
socket_map_.Remove(GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd));
delete sd;
} else {
- // Setup events to wait for.
- sd->SetPortAndMask(msg.dart_port, msg.data);
- UpdateKqueue(kqueue_fd_, sd);
+ if ((msg.data & (1 << kInEvent)) != 0 && sd->IsClosedRead()) {
+ DartUtils::PostInt32(msg.dart_port, 1 << kCloseEvent);
+ } else {
+ // Setup events to wait for.
+ sd->SetPortAndMask(msg.dart_port, msg.data);
+ UpdateKqueue(kqueue_fd_, sd);
+ }
}
}
}
@@ -276,6 +273,8 @@
}
}
if (event_mask == 0) event_mask |= (1 << kInEvent);
+ } else {
+ UNREACHABLE();
}
} else {
// Prioritize data events over close and error events.
@@ -290,9 +289,7 @@
}
sd->MarkClosedRead();
}
- }
-
- if (event->filter == EVFILT_WRITE) {
+ } else if (event->filter == EVFILT_WRITE) {
if ((event->flags & EV_EOF) != 0) {
if (event->fflags != 0) {
event_mask |= (1 << kErrorEvent);
@@ -307,6 +304,8 @@
} else {
event_mask |= (1 << kOutEvent);
}
+ } else {
+ UNREACHABLE();
}
}
@@ -343,14 +342,14 @@
if (timeout_ == kInfinityTimeout) {
return kInfinityTimeout;
}
- intptr_t millis = timeout_ - GetCurrentTimeMilliseconds();
+ intptr_t millis = timeout_ - TimerUtils::GetCurrentTimeMilliseconds();
return (millis < 0) ? 0 : millis;
}
void EventHandlerImplementation::HandleTimeout() {
if (timeout_ != kInfinityTimeout) {
- intptr_t millis = timeout_ - GetCurrentTimeMilliseconds();
+ intptr_t millis = timeout_ - TimerUtils::GetCurrentTimeMilliseconds();
if (millis <= 0) {
DartUtils::PostNull(timeout_port_);
timeout_ = kInfinityTimeout;
@@ -425,3 +424,5 @@
// The hashmap does not support keys with value 0.
return dart::Utils::WordHash(fd + 1);
}
+
+#endif // defined(TARGET_OS_MACOS)
diff --git a/runtime/bin/eventhandler_win.cc b/runtime/bin/eventhandler_win.cc
index 60d0fed..a10dd04 100644
--- a/runtime/bin/eventhandler_win.cc
+++ b/runtime/bin/eventhandler_win.cc
@@ -2,17 +2,21 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_WINDOWS)
+
#include "bin/eventhandler.h"
-#include <process.h>
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#include <mswsock.h>
+#include <process.h> // NOLINT
+#include <winsock2.h> // NOLINT
+#include <ws2tcpip.h> // NOLINT
+#include <mswsock.h> // NOLINT
#include "bin/builtin.h"
#include "bin/dartutils.h"
#include "bin/log.h"
#include "bin/socket.h"
+#include "bin/utils.h"
#include "platform/thread.h"
@@ -20,24 +24,6 @@
static const int kTimeoutId = -1;
static const int kShutdownId = -2;
-
-int64_t GetCurrentTimeMilliseconds() {
- static const int64_t kTimeEpoc = 116444736000000000LL;
-
- // Although win32 uses 64-bit integers for representing timestamps,
- // these are packed into a FILETIME structure. The FILETIME structure
- // is just a struct representing a 64-bit integer. The TimeStamp union
- // allows access to both a FILETIME and an integer representation of
- // the timestamp.
- union TimeStamp {
- FILETIME ft_;
- int64_t t_;
- };
- TimeStamp time;
- GetSystemTimeAsFileTime(&time.ft_);
- return (time.t_ - kTimeEpoc) / 10000;
-}
-
IOBuffer* IOBuffer::AllocateBuffer(int buffer_size, Operation operation) {
IOBuffer* buffer = new(buffer_size) IOBuffer(buffer_size, operation);
return buffer;
@@ -60,6 +46,11 @@
}
+IOBuffer* IOBuffer::AllocateDisconnectBuffer() {
+ return AllocateBuffer(0, kDisconnect);
+}
+
+
void IOBuffer::DisposeBuffer(IOBuffer* buffer) {
delete buffer;
}
@@ -153,25 +144,22 @@
}
-void Handle::close() {
+void Handle::Close() {
ScopedLock lock(this);
if (!IsClosing()) {
// Close the socket and set the closing state. This close method can be
// called again if this socket has pending IO operations in flight.
ASSERT(handle_ != INVALID_HANDLE_VALUE);
MarkClosing();
- // According to the documentation from Microsoft socket handles should
- // not be closed using CloseHandle but using closesocket.
- if (is_socket()) {
- closesocket(reinterpret_cast<SOCKET>(handle_));
- } else {
- CloseHandle(handle_);
- }
- handle_ = INVALID_HANDLE_VALUE;
+ // Perform handle type specific closing.
+ DoClose();
}
+}
- // Perform socket type specific close handling.
- AfterClose();
+
+void Handle::DoClose() {
+ CloseHandle(handle_);
+ handle_ = INVALID_HANDLE_VALUE;
}
@@ -326,10 +314,6 @@
}
-void FileHandle::AfterClose() {
-}
-
-
void SocketHandle::HandleIssueError() {
int error = WSAGetLastError();
if (error == WSAECONNRESET) {
@@ -343,12 +327,6 @@
bool ListenSocket::LoadAcceptEx() {
// Load the AcceptEx function into memory using WSAIoctl.
- // The WSAIoctl function is an extension of the ioctlsocket()
- // function that can use overlapped I/O. The function's 3rd
- // through 6th parameters are input and output buffers where
- // we pass the pointer to our AcceptEx function. This is used
- // so that we can call the AcceptEx function directly, rather
- // than refer to the Mswsock.lib library.
GUID guid_accept_ex = WSAID_ACCEPTEX;
DWORD bytes;
int status = WSAIoctl(socket(),
@@ -416,17 +394,6 @@
SO_UPDATE_ACCEPT_CONTEXT,
reinterpret_cast<char*>(&s), sizeof(s));
if (rc == NO_ERROR) {
- linger l;
- l.l_onoff = 1;
- l.l_linger = 10;
- int status = setsockopt(buffer->client(),
- SOL_SOCKET,
- SO_LINGER,
- reinterpret_cast<char*>(&l),
- sizeof(l));
- if (status != NO_ERROR) {
- FATAL("Failed setting SO_LINGER on socket");
- }
// Insert the accepted socket into the list.
ClientSocket* client_socket = new ClientSocket(buffer->client(), 0);
client_socket->CreateCompletionPort(completion_port);
@@ -449,6 +416,27 @@
}
+void ListenSocket::DoClose() {
+ closesocket(socket());
+ handle_ = INVALID_HANDLE_VALUE;
+ while (CanAccept()) {
+ // Get rid of connections already accepted.
+ ClientSocket *client = Accept();
+ if (client != NULL) {
+ client->Close();
+ } else {
+ break;
+ }
+ }
+}
+
+
+bool ListenSocket::CanAccept() {
+ ScopedLock lock(this);
+ return accepted_head_ != NULL;
+}
+
+
ClientSocket* ListenSocket::Accept() {
ScopedLock lock(this);
if (accepted_head_ == NULL) return NULL;
@@ -473,20 +461,6 @@
}
-void ListenSocket::AfterClose() {
- ScopedLock lock(this);
- while (true) {
- // Get rid of connections already accepted.
- ClientSocket *client = Accept();
- if (client != NULL) {
- client->close();
- } else {
- break;
- }
- }
-}
-
-
bool ListenSocket::IsClosed() {
return IsClosing() && !HasPendingAccept();
}
@@ -540,11 +514,29 @@
}
+bool ClientSocket::LoadDisconnectEx() {
+ // Load the DisconnectEx function into memory using WSAIoctl.
+ GUID guid_disconnect_ex = WSAID_DISCONNECTEX;
+ DWORD bytes;
+ int status = WSAIoctl(socket(),
+ SIO_GET_EXTENSION_FUNCTION_POINTER,
+ &guid_disconnect_ex,
+ sizeof(guid_disconnect_ex),
+ &DisconnectEx_,
+ sizeof(DisconnectEx_),
+ &bytes,
+ NULL,
+ NULL);
+ if (status == SOCKET_ERROR) {
+ Log::PrintErr("Error WSAIoctl failed: %d\n", WSAGetLastError());
+ return false;
+ }
+ return true;
+}
+
+
void ClientSocket::Shutdown(int how) {
int rc = shutdown(socket(), how);
- if (rc == SOCKET_ERROR) {
- Log::PrintErr("shutdown failed: %d %d\n", socket(), WSAGetLastError());
- }
if (how == SD_RECEIVE) MarkClosedRead();
if (how == SD_SEND) MarkClosedWrite();
if (how == SD_BOTH) {
@@ -554,6 +546,13 @@
}
+void ClientSocket::DoClose() {
+ // Always do a suhtdown before initiating a disconnect.
+ shutdown(socket(), SD_BOTH);
+ IssueDisconnect();
+}
+
+
bool ClientSocket::IssueRead() {
ScopedLock lock(this);
ASSERT(completion_port_ != INVALID_HANDLE_VALUE);
@@ -604,6 +603,27 @@
}
+void ClientSocket::IssueDisconnect() {
+ IOBuffer* buffer = IOBuffer::AllocateDisconnectBuffer();
+ BOOL ok = DisconnectEx_(
+ socket(), buffer->GetCleanOverlapped(), TF_REUSE_SOCKET, 0);
+ if (!ok && WSAGetLastError() != WSA_IO_PENDING) {
+ DisconnectComplete(buffer);
+ }
+}
+
+
+void ClientSocket::DisconnectComplete(IOBuffer* buffer) {
+ IOBuffer::DisposeBuffer(buffer);
+ closesocket(socket());
+ if (data_ready_ != NULL) {
+ IOBuffer::DisposeBuffer(data_ready_);
+ }
+ // When disconnect is complete get rid of the object.
+ delete this;
+}
+
+
void ClientSocket::EnsureInitialized(
EventHandlerImplementation* event_handler) {
ScopedLock lock(this);
@@ -615,17 +635,8 @@
}
-void ClientSocket::AfterClose() {
- ScopedLock lock(this);
- if (data_ready_ != NULL) {
- IOBuffer::DisposeBuffer(data_ready_);
- data_ready_ = NULL;
- }
-}
-
-
bool ClientSocket::IsClosed() {
- return IsClosing() && !HasPendingRead() && !HasPendingWrite();
+ return false;
}
@@ -649,49 +660,61 @@
Handle::ScopedLock lock(listen_socket);
- // If incomming connections are requested make sure that pending accepts
- // are issued.
+ // If incomming connections are requested make sure to post already
+ // accepted connections.
if ((msg->data & (1 << kInEvent)) != 0) {
+ if (listen_socket->CanAccept()) {
+ int event_mask = (1 << kInEvent);
+ handle->set_mask(handle->mask() & ~event_mask);
+ DartUtils::PostInt32(handle->port(), event_mask);
+ }
+ // Always keep 5 outstanding accepts going, to enhance performance.
while (listen_socket->pending_accept_count() < 5) {
listen_socket->IssueAccept();
}
}
if ((msg->data & (1 << kCloseCommand)) != 0) {
- listen_socket->close();
+ listen_socket->Close();
if (listen_socket->IsClosed()) {
delete_handle = true;
}
}
} else {
- handle->SetPortAndMask(msg->dart_port, msg->data);
handle->EnsureInitialized(this);
Handle::ScopedLock lock(handle);
if (!handle->IsError()) {
- // If in events (data available events) have been requested, and data
- // is available, post an in event immediately. Otherwise make sure
- // that a pending read is issued, unless the socket is already closed
- // for read.
- if ((msg->data & (1 << kInEvent)) != 0) {
- if (handle->Available() > 0) {
- int event_mask = (1 << kInEvent);
- handle->set_mask(handle->mask() & ~event_mask);
- DartUtils::PostInt32(handle->port(), event_mask);
- } else if (!handle->HasPendingRead() &&
- !handle->IsClosedRead()) {
- handle->IssueRead();
- }
- }
+ if ((msg->data & ((1 << kInEvent) | (1 << kOutEvent))) != 0) {
+ // Only set mask if we turned on kInEvent or kOutEvent.
+ handle->SetPortAndMask(msg->dart_port, msg->data);
- // If out events (can write events) have been requested, and there
- // are no pending writes, post an out event immediately.
- if ((msg->data & (1 << kOutEvent)) != 0) {
- if (!handle->HasPendingWrite()) {
- int event_mask = (1 << kOutEvent);
- handle->set_mask(handle->mask() & ~event_mask);
- DartUtils::PostInt32(handle->port(), event_mask);
+ // If in events (data available events) have been requested, and data
+ // is available, post an in event immediately. Otherwise make sure
+ // that a pending read is issued, unless the socket is already closed
+ // for read.
+ if ((msg->data & (1 << kInEvent)) != 0) {
+ if (handle->Available() > 0) {
+ int event_mask = (1 << kInEvent);
+ handle->set_mask(handle->mask() & ~event_mask);
+ DartUtils::PostInt32(handle->port(), event_mask);
+ } else if (handle->IsClosedRead()) {
+ int event_mask = (1 << kCloseEvent);
+ DartUtils::PostInt32(handle->port(), event_mask);
+ } else if (!handle->HasPendingRead()) {
+ handle->IssueRead();
+ }
+ }
+
+ // If out events (can write events) have been requested, and there
+ // are no pending writes, post an out event immediately.
+ if ((msg->data & (1 << kOutEvent)) != 0) {
+ if (!handle->HasPendingWrite()) {
+ int event_mask = (1 << kOutEvent);
+ handle->set_mask(handle->mask() & ~event_mask);
+ DartUtils::PostInt32(handle->port(), event_mask);
+ }
}
}
@@ -708,7 +731,7 @@
}
if ((msg->data & (1 << kCloseCommand)) != 0) {
- handle->close();
+ handle->Close();
if (handle->IsClosed()) {
delete_handle = true;
}
@@ -807,6 +830,13 @@
}
+void EventHandlerImplementation::HandleDisconnect(
+ ClientSocket* client_socket,
+ int bytes,
+ IOBuffer* buffer) {
+ client_socket->DisconnectComplete(buffer);
+}
+
void EventHandlerImplementation::HandleTimeout() {
// TODO(sgjesse) check if there actually is a timeout.
DartUtils::PostNull(timeout_port_);
@@ -835,6 +865,11 @@
HandleWrite(handle, bytes, buffer);
break;
}
+ case IOBuffer::kDisconnect: {
+ ClientSocket* client_socket = reinterpret_cast<ClientSocket*>(key);
+ HandleDisconnect(client_socket, bytes, buffer);
+ break;
+ }
default:
UNREACHABLE();
}
@@ -858,7 +893,7 @@
if (timeout_ == kInfinityTimeout) {
return kInfinityTimeout;
}
- intptr_t millis = timeout_ - GetCurrentTimeMilliseconds();
+ intptr_t millis = timeout_ - TimerUtils::GetCurrentTimeMilliseconds();
return (millis < 0) ? 0 : millis;
}
@@ -947,3 +982,5 @@
void EventHandlerImplementation::Shutdown() {
SendData(kShutdownId, 0, 0);
}
+
+#endif // defined(TARGET_OS_WINDOWS)
diff --git a/runtime/bin/eventhandler_win.h b/runtime/bin/eventhandler_win.h
index 7b4902f..44d710c 100644
--- a/runtime/bin/eventhandler_win.h
+++ b/runtime/bin/eventhandler_win.h
@@ -36,11 +36,12 @@
// socket for the client.
class IOBuffer {
public:
- enum Operation { kAccept, kRead, kWrite };
+ enum Operation { kAccept, kRead, kWrite, kDisconnect };
static IOBuffer* AllocateAcceptBuffer(int buffer_size);
static IOBuffer* AllocateReadBuffer(int buffer_size);
static IOBuffer* AllocateWriteBuffer(int buffer_size);
+ static IOBuffer* AllocateDisconnectBuffer();
static void DisposeBuffer(IOBuffer* buffer);
// Find the IO buffer from the OVERLAPPED address.
@@ -177,7 +178,8 @@
bool CreateCompletionPort(HANDLE completion_port);
- void close();
+ void Close();
+ virtual void DoClose();
virtual bool IsClosed() = 0;
void SetPortAndMask(Dart_Port port, intptr_t mask) {
@@ -216,7 +218,6 @@
explicit Handle(HANDLE handle);
Handle(HANDLE handle, Dart_Port port);
- virtual void AfterClose() = 0;
virtual void HandleIssueError();
Type type_;
@@ -247,9 +248,6 @@
virtual void EnsureInitialized(EventHandlerImplementation* event_handler);
virtual bool IsClosed();
-
- private:
- virtual void AfterClose();
};
@@ -284,6 +282,7 @@
// Socket interface exposing normal socket operations.
ClientSocket* Accept();
+ bool CanAccept();
// Internal interface used by the event handler.
bool HasPendingAccept() { return pending_accept_count_ > 0; }
@@ -292,13 +291,13 @@
virtual void EnsureInitialized(
EventHandlerImplementation* event_handler);
+ virtual void DoClose();
virtual bool IsClosed();
int pending_accept_count() { return pending_accept_count_; }
private:
bool LoadAcceptEx();
- virtual void AfterClose();
LPFN_ACCEPTEX AcceptEx_;
int pending_accept_count_;
@@ -312,11 +311,17 @@
// Information on connected sockets.
class ClientSocket : public SocketHandle {
public:
- explicit ClientSocket(SOCKET s) : SocketHandle(s), next_(NULL) {
+ explicit ClientSocket(SOCKET s) : SocketHandle(s),
+ DisconnectEx_(NULL),
+ next_(NULL) {
+ LoadDisconnectEx();
type_ = kClientSocket;
}
- ClientSocket(SOCKET s, Dart_Port port) : SocketHandle(s, port), next_(NULL) {
+ ClientSocket(SOCKET s, Dart_Port port) : SocketHandle(s, port),
+ DisconnectEx_(NULL),
+ next_(NULL) {
+ LoadDisconnectEx();
type_ = kClientSocket;
}
@@ -332,17 +337,21 @@
// Internal interface used by the event handler.
virtual bool IssueRead();
virtual bool IssueWrite();
+ void IssueDisconnect();
+ void DisconnectComplete(IOBuffer* buffer);
virtual void EnsureInitialized(
EventHandlerImplementation* event_handler);
+ virtual void DoClose();
virtual bool IsClosed();
ClientSocket* next() { return next_; }
void set_next(ClientSocket* next) { next_ = next; }
private:
- virtual void AfterClose();
+ bool LoadDisconnectEx();
+ LPFN_DISCONNECTEX DisconnectEx_;
ClientSocket* next_;
};
@@ -367,7 +376,9 @@
void HandleError(Handle* handle);
void HandleRead(Handle* handle, int bytes, IOBuffer* buffer);
void HandleWrite(Handle* handle, int bytes, IOBuffer* buffer);
- void HandleClose(ClientSocket* client_socket);
+ void HandleDisconnect(ClientSocket* client_socket,
+ int bytes,
+ IOBuffer* buffer);
void HandleIOCompletion(DWORD bytes, ULONG_PTR key, OVERLAPPED* overlapped);
HANDLE completion_port() { return completion_port_; }
diff --git a/runtime/bin/extensions_android.cc b/runtime/bin/extensions_android.cc
index 8066b13..b136736 100644
--- a/runtime/bin/extensions_android.cc
+++ b/runtime/bin/extensions_android.cc
@@ -2,8 +2,11 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_ANDROID)
+
#include "bin/extensions.h"
-#include <dlfcn.h>
+#include <dlfcn.h> // NOLINT
void* Extensions::LoadExtensionLibrary(const char* library_path,
const char* extension_name) {
@@ -21,3 +24,5 @@
if (dlerror() != NULL) return NULL;
return result;
}
+
+#endif // defined(TARGET_OS_ANDROID)
diff --git a/runtime/bin/extensions_linux.cc b/runtime/bin/extensions_linux.cc
index 8066b13..8321efe 100644
--- a/runtime/bin/extensions_linux.cc
+++ b/runtime/bin/extensions_linux.cc
@@ -2,8 +2,11 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_LINUX)
+
#include "bin/extensions.h"
-#include <dlfcn.h>
+#include <dlfcn.h> // NOLINT
void* Extensions::LoadExtensionLibrary(const char* library_path,
const char* extension_name) {
@@ -21,3 +24,5 @@
if (dlerror() != NULL) return NULL;
return result;
}
+
+#endif // defined(TARGET_OS_LINUX)
diff --git a/runtime/bin/extensions_macos.cc b/runtime/bin/extensions_macos.cc
index 54f5c71..6d7842a 100644
--- a/runtime/bin/extensions_macos.cc
+++ b/runtime/bin/extensions_macos.cc
@@ -2,8 +2,11 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_MACOS)
+
#include "bin/extensions.h"
-#include <dlfcn.h>
+#include <dlfcn.h> // NOLINT
void* Extensions::LoadExtensionLibrary(const char* library_path,
const char* extension_name) {
@@ -21,3 +24,5 @@
if (dlerror() != NULL) return NULL;
return result;
}
+
+#endif // defined(TARGET_OS_MACOS)
diff --git a/runtime/bin/extensions_win.cc b/runtime/bin/extensions_win.cc
index 8590d7b..9e8a653 100644
--- a/runtime/bin/extensions_win.cc
+++ b/runtime/bin/extensions_win.cc
@@ -2,6 +2,9 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_WINDOWS)
+
#include "bin/extensions.h"
#include "bin/utils.h"
@@ -19,3 +22,5 @@
void* Extensions::ResolveSymbol(void* lib_handle, const char* symbol) {
return GetProcAddress(reinterpret_cast<HMODULE>(lib_handle), symbol);
}
+
+#endif // defined(TARGET_OS_WINDOWS)
diff --git a/runtime/bin/fdutils_android.cc b/runtime/bin/fdutils_android.cc
index 8d946e6..18b4ec3 100644
--- a/runtime/bin/fdutils_android.cc
+++ b/runtime/bin/fdutils_android.cc
@@ -2,10 +2,13 @@
// 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 <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/ioctl.h>
+#include "platform/globals.h"
+#if defined(TARGET_OS_ANDROID)
+
+#include <errno.h> // NOLINT
+#include <fcntl.h> // NOLINT
+#include <unistd.h> // NOLINT
+#include <sys/ioctl.h> // NOLINT
#include "bin/fdutils.h"
@@ -132,3 +135,5 @@
}
return count;
}
+
+#endif // defined(TARGET_OS_ANDROID)
diff --git a/runtime/bin/fdutils_linux.cc b/runtime/bin/fdutils_linux.cc
index 8d946e6..84c2194 100644
--- a/runtime/bin/fdutils_linux.cc
+++ b/runtime/bin/fdutils_linux.cc
@@ -2,10 +2,13 @@
// 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 <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/ioctl.h>
+#include "platform/globals.h"
+#if defined(TARGET_OS_LINUX)
+
+#include <errno.h> // NOLINT
+#include <fcntl.h> // NOLINT
+#include <unistd.h> // NOLINT
+#include <sys/ioctl.h> // NOLINT
#include "bin/fdutils.h"
@@ -132,3 +135,5 @@
}
return count;
}
+
+#endif // defined(TARGET_OS_LINUX)
diff --git a/runtime/bin/fdutils_macos.cc b/runtime/bin/fdutils_macos.cc
index 92e63fc..7087add 100644
--- a/runtime/bin/fdutils_macos.cc
+++ b/runtime/bin/fdutils_macos.cc
@@ -2,10 +2,13 @@
// 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 <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/ioctl.h>
+#include "platform/globals.h"
+#if defined(TARGET_OS_MACOS)
+
+#include <errno.h> // NOLINT
+#include <fcntl.h> // NOLINT
+#include <unistd.h> // NOLINT
+#include <sys/ioctl.h> // NOLINT
#include "bin/fdutils.h"
@@ -133,3 +136,5 @@
}
return count;
}
+
+#endif // defined(TARGET_OS_MACOS)
diff --git a/runtime/bin/file.cc b/runtime/bin/file.cc
index bd32b5b..e366fc2 100644
--- a/runtime/bin/file.cc
+++ b/runtime/bin/file.cc
@@ -14,10 +14,11 @@
static const int kMSPerSecond = 1000;
-dart::Mutex File::mutex_;
-int File::service_ports_size_ = 0;
-Dart_Port* File::service_ports_ = NULL;
-int File::service_ports_index_ = 0;
+
+// Forward declaration.
+static void FileService(Dart_Port, Dart_Port, Dart_CObject*);
+
+NativeService File::file_service_("FileService", FileService, 16);
// The file pointer has been passed into Dart as an intptr_t and it is safe
@@ -890,10 +891,10 @@
}
-void FileService(Dart_Port dest_port_id,
+static void FileService(Dart_Port dest_port_id,
Dart_Port reply_port_id,
Dart_CObject* message) {
- CObject* response = CObject::False();
+ CObject* response = CObject::IllegalArgumentError();
CObjectArray request(message);
if (message->type == Dart_CObject::kArray) {
if (request.Length() > 1 && request[0]->IsInt32()) {
@@ -967,27 +968,7 @@
Dart_Port File::GetServicePort() {
- MutexLocker lock(&mutex_);
- if (service_ports_size_ == 0) {
- ASSERT(service_ports_ == NULL);
- service_ports_size_ = 16;
- service_ports_ = new Dart_Port[service_ports_size_];
- service_ports_index_ = 0;
- for (int i = 0; i < service_ports_size_; i++) {
- service_ports_[i] = ILLEGAL_PORT;
- }
- }
-
- Dart_Port result = service_ports_[service_ports_index_];
- if (result == ILLEGAL_PORT) {
- result = Dart_NewNativePort("FileService",
- FileService,
- true);
- ASSERT(result != ILLEGAL_PORT);
- service_ports_[service_ports_index_] = result;
- }
- service_ports_index_ = (service_ports_index_ + 1) % service_ports_size_;
- return result;
+ return file_service_.GetServicePort();
}
diff --git a/runtime/bin/file.h b/runtime/bin/file.h
index 5f3fd00..53a1e44 100644
--- a/runtime/bin/file.h
+++ b/runtime/bin/file.h
@@ -12,6 +12,7 @@
#include "bin/builtin.h"
#include "bin/dartutils.h"
+#include "bin/native_service.h"
#include "platform/globals.h"
#include "platform/thread.h"
@@ -138,10 +139,7 @@
// FileHandle is an OS specific class which stores data about the file.
FileHandle* handle_; // OS specific handle for the file.
- static dart::Mutex mutex_;
- static int service_ports_size_;
- static Dart_Port* service_ports_;
- static int service_ports_index_;
+ static NativeService file_service_;
DISALLOW_COPY_AND_ASSIGN(File);
};
diff --git a/runtime/bin/file_android.cc b/runtime/bin/file_android.cc
index ddcec13..7ae6415 100644
--- a/runtime/bin/file_android.cc
+++ b/runtime/bin/file_android.cc
@@ -2,13 +2,16 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_ANDROID)
+
#include "bin/file.h"
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <libgen.h>
+#include <errno.h> // NOLINT
+#include <fcntl.h> // NOLINT
+#include <sys/stat.h> // NOLINT
+#include <unistd.h> // NOLINT
+#include <libgen.h> // NOLINT
#include "bin/builtin.h"
#include "bin/log.h"
@@ -242,3 +245,5 @@
if (S_ISREG(buf.st_mode)) return kFile;
return kOther;
}
+
+#endif // defined(TARGET_OS_ANDROID)
diff --git a/runtime/bin/file_linux.cc b/runtime/bin/file_linux.cc
index ddcec13..3a73135 100644
--- a/runtime/bin/file_linux.cc
+++ b/runtime/bin/file_linux.cc
@@ -2,13 +2,16 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_LINUX)
+
#include "bin/file.h"
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <libgen.h>
+#include <errno.h> // NOLINT
+#include <fcntl.h> // NOLINT
+#include <sys/stat.h> // NOLINT
+#include <unistd.h> // NOLINT
+#include <libgen.h> // NOLINT
#include "bin/builtin.h"
#include "bin/log.h"
@@ -242,3 +245,5 @@
if (S_ISREG(buf.st_mode)) return kFile;
return kOther;
}
+
+#endif // defined(TARGET_OS_LINUX)
diff --git a/runtime/bin/file_macos.cc b/runtime/bin/file_macos.cc
index 0fd0c8d..14b2fd8 100644
--- a/runtime/bin/file_macos.cc
+++ b/runtime/bin/file_macos.cc
@@ -2,14 +2,17 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_MACOS)
+
#include "bin/file.h"
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <libgen.h>
-#include <limits.h>
+#include <errno.h> // NOLINT
+#include <fcntl.h> // NOLINT
+#include <sys/stat.h> // NOLINT
+#include <unistd.h> // NOLINT
+#include <libgen.h> // NOLINT
+#include <limits.h> // NOLINT
#include "bin/builtin.h"
#include "bin/fdutils.h"
@@ -250,3 +253,5 @@
if (S_ISREG(buf.st_mode)) return kFile;
return kOther;
}
+
+#endif // defined(TARGET_OS_MACOS)
diff --git a/runtime/bin/file_win.cc b/runtime/bin/file_win.cc
index 73d195c..2ce097d 100644
--- a/runtime/bin/file_win.cc
+++ b/runtime/bin/file_win.cc
@@ -2,13 +2,16 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_WINDOWS)
+
#include "bin/file.h"
-#include <fcntl.h>
-#include <io.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
+#include <fcntl.h> // NOLINT
+#include <io.h> // NOLINT
+#include <stdio.h> // NOLINT
+#include <string.h> // NOLINT
+#include <sys/stat.h> // NOLINT
#include "bin/builtin.h"
#include "bin/log.h"
@@ -266,3 +269,5 @@
// socket code will handle the different handle types.
return kPipe;
}
+
+#endif // defined(TARGET_OS_WINDOWS)
diff --git a/runtime/bin/gen_snapshot.cc b/runtime/bin/gen_snapshot.cc
index f5e53ee..091cb69 100644
--- a/runtime/bin/gen_snapshot.cc
+++ b/runtime/bin/gen_snapshot.cc
@@ -422,24 +422,15 @@
Dart_Handle library =
LoadGenericSnapshotCreationScript(Builtin::kBuiltinLibrary);
VerifyLoaded(library);
- library = LoadGenericSnapshotCreationScript(Builtin::kUriLibrary);
- VerifyLoaded(library);
- library = LoadGenericSnapshotCreationScript(Builtin::kUtfLibrary);
- VerifyLoaded(library);
}
static void SetupForGenericSnapshotCreation() {
SetupForUriResolution();
- Dart_Handle library =
- LoadGenericSnapshotCreationScript(Builtin::kJsonLibrary);
- VerifyLoaded(library);
- library = LoadGenericSnapshotCreationScript(Builtin::kCryptoLibrary);
- VerifyLoaded(library);
// TODO(regis): Reenable this code for arm and mips when possible.
#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64)
- library = LoadGenericSnapshotCreationScript(Builtin::kIOLibrary);
+ Dart_Handle library = LoadGenericSnapshotCreationScript(Builtin::kIOLibrary);
VerifyLoaded(library);
#endif
}
diff --git a/runtime/bin/io_buffer.cc b/runtime/bin/io_buffer.cc
index 5f40478..b1bc958 100644
--- a/runtime/bin/io_buffer.cc
+++ b/runtime/bin/io_buffer.cc
@@ -6,7 +6,8 @@
Dart_Handle IOBuffer::Allocate(intptr_t size, uint8_t **buffer) {
uint8_t* data = Allocate(size);
- Dart_Handle result = Dart_NewExternalByteArray(data, size,
+ Dart_Handle result = Dart_NewExternalTypedData(kUint8,
+ data, size,
data, IOBuffer::Finalizer);
if (Dart_IsError(result)) {
Free(data);
diff --git a/runtime/bin/log_android.cc b/runtime/bin/log_android.cc
index 0a6b552..5f878f2 100644
--- a/runtime/bin/log_android.cc
+++ b/runtime/bin/log_android.cc
@@ -2,10 +2,13 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_ANDROID)
+
#include "bin/log.h"
-#include <stdio.h>
-#include <android/log.h>
+#include <stdio.h> // NOLINT
+#include <android/log.h> // NOLINT
// TODO(gram): We should be buffering the data and only outputting
// it when we see a '\n'.
@@ -18,3 +21,4 @@
__android_log_vprint(ANDROID_LOG_ERROR, "Dart", format, args);
}
+#endif // defined(TARGET_OS_ANDROID)
diff --git a/runtime/bin/log_linux.cc b/runtime/bin/log_linux.cc
index 1b99c70..0b825af 100644
--- a/runtime/bin/log_linux.cc
+++ b/runtime/bin/log_linux.cc
@@ -2,9 +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.
+#include "platform/globals.h"
+#if defined(TARGET_OS_LINUX)
+
#include "bin/log.h"
-#include <stdio.h>
+#include <stdio.h> // NOLINT
void Log::VPrint(const char* format, va_list args) {
vfprintf(stdout, format, args);
@@ -16,3 +19,4 @@
fflush(stdout);
}
+#endif // defined(TARGET_OS_LINUX)
diff --git a/runtime/bin/log_macos.cc b/runtime/bin/log_macos.cc
index 2f0d371..87c9075 100644
--- a/runtime/bin/log_macos.cc
+++ b/runtime/bin/log_macos.cc
@@ -2,9 +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.
+#include "platform/globals.h"
+#if defined(TARGET_OS_MACOS)
+
#include "bin/log.h"
-#include <stdio.h>
+#include <stdio.h> // NOLINT
void Log::VPrint(const char* format, va_list args) {
vfprintf(stdout, format, args);
@@ -15,3 +18,5 @@
vfprintf(stderr, format, args);
fflush(stderr);
}
+
+#endif // defined(TARGET_OS_MACOS)
diff --git a/runtime/bin/log_win.cc b/runtime/bin/log_win.cc
index 2f0d371..30c265d 100644
--- a/runtime/bin/log_win.cc
+++ b/runtime/bin/log_win.cc
@@ -2,9 +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.
+#include "platform/globals.h"
+#if defined(TARGET_OS_WINDOWS)
+
#include "bin/log.h"
-#include <stdio.h>
+#include <stdio.h> // NOLINT
void Log::VPrint(const char* format, va_list args) {
vfprintf(stdout, format, args);
@@ -15,3 +18,5 @@
vfprintf(stderr, format, args);
fflush(stderr);
}
+
+#endif // defined(TARGET_OS_WINDOWS)
diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc
index 6d263ef..6a06bf3 100644
--- a/runtime/bin/main.cc
+++ b/runtime/bin/main.cc
@@ -463,7 +463,8 @@
use_script_snapshot = false; // No further usage of script snapshots.
} else {
// Prepare builtin and its dependent libraries for use to resolve URIs.
- Dart_Handle uri_lib = Builtin::LoadAndCheckLibrary(Builtin::kUriLibrary);
+ Dart_Handle uri_url = DartUtils::NewString(DartUtils::kUriLibURL);
+ Dart_Handle uri_lib = Dart_LookupLibrary(uri_url);
CHECK_RESULT(uri_lib);
Dart_Handle builtin_lib =
Builtin::LoadAndCheckLibrary(Builtin::kBuiltinLibrary);
diff --git a/runtime/bin/native_service.cc b/runtime/bin/native_service.cc
new file mode 100644
index 0000000..9d4c3e8
--- /dev/null
+++ b/runtime/bin/native_service.cc
@@ -0,0 +1,40 @@
+// 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.
+
+#include "bin/native_service.h"
+
+#include "platform/globals.h"
+#include "bin/thread.h"
+
+
+NativeService::NativeService(const char* name,
+ Dart_NativeMessageHandler handler,
+ int number_of_ports)
+ : name_(name),
+ handler_(handler),
+ service_ports_size_(number_of_ports),
+ service_ports_index_(0) {
+ service_ports_ = new Dart_Port[service_ports_size_];
+ for (int i = 0; i < service_ports_size_; i++) {
+ service_ports_[i] = ILLEGAL_PORT;
+ }
+}
+
+
+NativeService::~NativeService() {
+ delete[] service_ports_;
+}
+
+
+Dart_Port NativeService::GetServicePort() {
+ MutexLocker lock(&mutex_);
+ Dart_Port result = service_ports_[service_ports_index_];
+ if (result == ILLEGAL_PORT) {
+ result = Dart_NewNativePort(name_, handler_, true);
+ ASSERT(result != ILLEGAL_PORT);
+ service_ports_[service_ports_index_] = result;
+ }
+ service_ports_index_ = (service_ports_index_ + 1) % service_ports_size_;
+ return result;
+}
diff --git a/runtime/bin/native_service.h b/runtime/bin/native_service.h
new file mode 100644
index 0000000..8bb4330
--- /dev/null
+++ b/runtime/bin/native_service.h
@@ -0,0 +1,45 @@
+// 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.
+
+#ifndef BIN_NATIVE_SERVICE_H_
+#define BIN_NATIVE_SERVICE_H_
+
+#include "include/dart_api.h"
+#include "platform/globals.h"
+#include "platform/thread.h"
+
+// Utility class to set up a native service and allocate Dart native
+// ports to interact with it from Dart code. The number of native ports
+// allocated for each service is limited.
+class NativeService {
+ public:
+ // Create a native service with the given name and handler. Allow
+ // the creation of [number_of_ports] native ports for the service.
+ // If GetServicePort is called more than [number_of_ports] times
+ // one of the already allocated native ports will be reused.
+ NativeService(const char* name,
+ Dart_NativeMessageHandler handler,
+ int number_of_ports);
+
+ ~NativeService();
+
+ // Get a Dart native port for this native service.
+ Dart_Port GetServicePort();
+
+ private:
+ // Name and handler for the native service.
+ const char* name_;
+ Dart_NativeMessageHandler handler_;
+
+ // Allocated native ports for the service. Mutex protected since
+ // the service can be used from multiple isolates.
+ dart::Mutex mutex_;
+ int service_ports_size_;
+ Dart_Port* service_ports_;
+ int service_ports_index_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(NativeService);
+};
+
+#endif // BIN_NATIVE_SERVICE_H_
diff --git a/runtime/bin/platform_android.cc b/runtime/bin/platform_android.cc
index a05de34..435f16b 100644
--- a/runtime/bin/platform_android.cc
+++ b/runtime/bin/platform_android.cc
@@ -2,11 +2,14 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_ANDROID)
+
#include "bin/platform.h"
-#include <signal.h>
-#include <string.h>
-#include <unistd.h>
+#include <signal.h> // NOLINT
+#include <string.h> // NOLINT
+#include <unistd.h> // NOLINT
bool Platform::Initialize() {
@@ -65,3 +68,5 @@
void Platform::FreeEnvironment(char** env, intptr_t count) {
delete[] env;
}
+
+#endif // defined(TARGET_OS_ANDROID)
diff --git a/runtime/bin/platform_linux.cc b/runtime/bin/platform_linux.cc
index da91bc5..48f6154 100644
--- a/runtime/bin/platform_linux.cc
+++ b/runtime/bin/platform_linux.cc
@@ -2,11 +2,14 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_LINUX)
+
#include "bin/platform.h"
-#include <signal.h>
-#include <string.h>
-#include <unistd.h>
+#include <signal.h> // NOLINT
+#include <string.h> // NOLINT
+#include <unistd.h> // NOLINT
bool Platform::Initialize() {
@@ -65,3 +68,5 @@
void Platform::FreeEnvironment(char** env, intptr_t count) {
delete[] env;
}
+
+#endif // defined(TARGET_OS_LINUX)
diff --git a/runtime/bin/platform_macos.cc b/runtime/bin/platform_macos.cc
index 87e8c6f..d538eb7 100644
--- a/runtime/bin/platform_macos.cc
+++ b/runtime/bin/platform_macos.cc
@@ -2,12 +2,15 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_MACOS)
+
#include "bin/platform.h"
-#include <crt_externs.h>
-#include <signal.h>
-#include <string.h>
-#include <unistd.h>
+#include <crt_externs.h> // NOLINT
+#include <signal.h> // NOLINT
+#include <string.h> // NOLINT
+#include <unistd.h> // NOLINT
bool Platform::Initialize() {
@@ -70,3 +73,5 @@
void Platform::FreeEnvironment(char** env, intptr_t count) {
delete[] env;
}
+
+#endif // defined(TARGET_OS_MACOS)
diff --git a/runtime/bin/platform_win.cc b/runtime/bin/platform_win.cc
index 090fcf2..a71a455 100644
--- a/runtime/bin/platform_win.cc
+++ b/runtime/bin/platform_win.cc
@@ -2,6 +2,9 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_WINDOWS)
+
#include "bin/platform.h"
#include "bin/log.h"
#include "bin/socket.h"
@@ -25,12 +28,7 @@
bool Platform::LocalHostname(char *buffer, intptr_t buffer_length) {
- static bool socket_initialized = false;
- if (!socket_initialized) {
- // Initialize Socket for gethostname.
- if (!Socket::Initialize()) return false;
- socket_initialized = true;
- }
+ if (!Socket::Initialize()) return false;
return gethostname(buffer, buffer_length) == 0;
}
@@ -60,3 +58,5 @@
for (int i = 0; i < count; i++) free(env[i]);
delete[] env;
}
+
+#endif // defined(TARGET_OS_WINDOWS)
diff --git a/runtime/bin/process.cc b/runtime/bin/process.cc
index 81715ba..ac75854 100644
--- a/runtime/bin/process.cc
+++ b/runtime/bin/process.cc
@@ -58,9 +58,9 @@
void FUNCTION_NAME(Process_Start)(Dart_NativeArguments args) {
Dart_EnterScope();
Dart_Handle process = Dart_GetNativeArgument(args, 0);
- intptr_t in;
- intptr_t out;
- intptr_t err;
+ intptr_t process_stdin;
+ intptr_t process_stdout;
+ intptr_t process_stderr;
intptr_t exit_event;
Dart_Handle status_handle = Dart_GetNativeArgument(args, 9);
Dart_Handle path_handle = Dart_GetNativeArgument(args, 1);
@@ -119,9 +119,9 @@
return;
}
}
- Dart_Handle in_handle = Dart_GetNativeArgument(args, 5);
- Dart_Handle out_handle = Dart_GetNativeArgument(args, 6);
- Dart_Handle err_handle = Dart_GetNativeArgument(args, 7);
+ Dart_Handle stdin_handle = Dart_GetNativeArgument(args, 5);
+ Dart_Handle stdout_handle = Dart_GetNativeArgument(args, 6);
+ Dart_Handle stderr_handle = Dart_GetNativeArgument(args, 7);
Dart_Handle exit_handle = Dart_GetNativeArgument(args, 8);
intptr_t pid = -1;
char* os_error_message = NULL;
@@ -132,16 +132,16 @@
working_directory,
string_environment,
environment_length,
- &in,
- &out,
- &err,
+ &process_stdout,
+ &process_stdin,
+ &process_stderr,
&pid,
&exit_event,
&os_error_message);
if (error_code == 0) {
- Socket::SetSocketIdNativeField(in_handle, in);
- Socket::SetSocketIdNativeField(out_handle, out);
- Socket::SetSocketIdNativeField(err_handle, err);
+ Socket::SetSocketIdNativeField(stdin_handle, process_stdin);
+ Socket::SetSocketIdNativeField(stdout_handle, process_stdout);
+ Socket::SetSocketIdNativeField(stderr_handle, process_stderr);
Socket::SetSocketIdNativeField(exit_handle, exit_event);
Process::SetProcessIdNativeField(process, pid);
} else {
diff --git a/runtime/bin/process_android.cc b/runtime/bin/process_android.cc
index f38e100..066539a 100644
--- a/runtime/bin/process_android.cc
+++ b/runtime/bin/process_android.cc
@@ -2,17 +2,20 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_ANDROID)
+
#include "bin/process.h"
-#include <errno.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/wait.h>
-#include <unistd.h>
+#include <errno.h> // NOLINT
+#include <fcntl.h> // NOLINT
+#include <poll.h> // NOLINT
+#include <signal.h> // NOLINT
+#include <stdio.h> // NOLINT
+#include <stdlib.h> // NOLINT
+#include <string.h> // NOLINT
+#include <sys/wait.h> // NOLINT
+#include <unistd.h> // NOLINT
#include "bin/fdutils.h"
#include "bin/log.h"
@@ -566,3 +569,5 @@
intptr_t Process::CurrentProcessId() {
return static_cast<intptr_t>(getpid());
}
+
+#endif // defined(TARGET_OS_ANDROID)
diff --git a/runtime/bin/process_linux.cc b/runtime/bin/process_linux.cc
index cd71267..3522385 100644
--- a/runtime/bin/process_linux.cc
+++ b/runtime/bin/process_linux.cc
@@ -2,17 +2,20 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_LINUX)
+
#include "bin/process.h"
-#include <errno.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/wait.h>
-#include <unistd.h>
+#include <errno.h> // NOLINT
+#include <fcntl.h> // NOLINT
+#include <poll.h> // NOLINT
+#include <signal.h> // NOLINT
+#include <stdio.h> // NOLINT
+#include <stdlib.h> // NOLINT
+#include <string.h> // NOLINT
+#include <sys/wait.h> // NOLINT
+#include <unistd.h> // NOLINT
#include "bin/fdutils.h"
#include "bin/log.h"
@@ -562,3 +565,5 @@
intptr_t Process::CurrentProcessId() {
return static_cast<intptr_t>(getpid());
}
+
+#endif // defined(TARGET_OS_LINUX)
diff --git a/runtime/bin/process_macos.cc b/runtime/bin/process_macos.cc
index f2d5ca3..67a7102 100644
--- a/runtime/bin/process_macos.cc
+++ b/runtime/bin/process_macos.cc
@@ -2,16 +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.
+#include "platform/globals.h"
+#if defined(TARGET_OS_MACOS)
+
#include "bin/process.h"
-#include <errno.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
+#include <errno.h> // NOLINT
+#include <fcntl.h> // NOLINT
+#include <poll.h> // NOLINT
+#include <signal.h> // NOLINT
+#include <stdio.h> // NOLINT
+#include <stdlib.h> // NOLINT
+#include <string.h> // NOLINT
+#include <unistd.h> // NOLINT
#include "bin/fdutils.h"
#include "bin/log.h"
@@ -293,7 +296,7 @@
FDUtils::WriteToBlocking(
exec_control_fd, os_error_message, strlen(os_error_message) + 1);
}
- TEMP_FAILURE_RETRY(close(exec_control_fd));
+ VOID_TEMP_FAILURE_RETRY(close(exec_control_fd));
exit(1);
}
@@ -335,8 +338,8 @@
result = TEMP_FAILURE_RETRY(pipe(read_err));
if (result < 0) {
SetChildOsErrorMessage(os_error_message);
- TEMP_FAILURE_RETRY(close(read_in[0]));
- TEMP_FAILURE_RETRY(close(read_in[1]));
+ VOID_TEMP_FAILURE_RETRY(close(read_in[0]));
+ VOID_TEMP_FAILURE_RETRY(close(read_in[1]));
Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message);
return errno;
}
@@ -344,10 +347,10 @@
result = TEMP_FAILURE_RETRY(pipe(write_out));
if (result < 0) {
SetChildOsErrorMessage(os_error_message);
- TEMP_FAILURE_RETRY(close(read_in[0]));
- TEMP_FAILURE_RETRY(close(read_in[1]));
- TEMP_FAILURE_RETRY(close(read_err[0]));
- TEMP_FAILURE_RETRY(close(read_err[1]));
+ VOID_TEMP_FAILURE_RETRY(close(read_in[0]));
+ VOID_TEMP_FAILURE_RETRY(close(read_in[1]));
+ VOID_TEMP_FAILURE_RETRY(close(read_err[0]));
+ VOID_TEMP_FAILURE_RETRY(close(read_err[1]));
Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message);
return errno;
}
@@ -355,12 +358,12 @@
result = TEMP_FAILURE_RETRY(pipe(exec_control));
if (result < 0) {
SetChildOsErrorMessage(os_error_message);
- TEMP_FAILURE_RETRY(close(read_in[0]));
- TEMP_FAILURE_RETRY(close(read_in[1]));
- TEMP_FAILURE_RETRY(close(read_err[0]));
- TEMP_FAILURE_RETRY(close(read_err[1]));
- TEMP_FAILURE_RETRY(close(write_out[0]));
- TEMP_FAILURE_RETRY(close(write_out[1]));
+ VOID_TEMP_FAILURE_RETRY(close(read_in[0]));
+ VOID_TEMP_FAILURE_RETRY(close(read_in[1]));
+ VOID_TEMP_FAILURE_RETRY(close(read_err[0]));
+ VOID_TEMP_FAILURE_RETRY(close(read_err[1]));
+ VOID_TEMP_FAILURE_RETRY(close(write_out[0]));
+ VOID_TEMP_FAILURE_RETRY(close(write_out[1]));
Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message);
return errno;
}
@@ -372,14 +375,14 @@
TEMP_FAILURE_RETRY(fcntl(exec_control[1], F_GETFD)) | FD_CLOEXEC));
if (result < 0) {
SetChildOsErrorMessage(os_error_message);
- TEMP_FAILURE_RETRY(close(read_in[0]));
- TEMP_FAILURE_RETRY(close(read_in[1]));
- TEMP_FAILURE_RETRY(close(read_err[0]));
- TEMP_FAILURE_RETRY(close(read_err[1]));
- TEMP_FAILURE_RETRY(close(write_out[0]));
- TEMP_FAILURE_RETRY(close(write_out[1]));
- TEMP_FAILURE_RETRY(close(exec_control[0]));
- TEMP_FAILURE_RETRY(close(exec_control[1]));
+ VOID_TEMP_FAILURE_RETRY(close(read_in[0]));
+ VOID_TEMP_FAILURE_RETRY(close(read_in[1]));
+ VOID_TEMP_FAILURE_RETRY(close(read_err[0]));
+ VOID_TEMP_FAILURE_RETRY(close(read_err[1]));
+ VOID_TEMP_FAILURE_RETRY(close(write_out[0]));
+ VOID_TEMP_FAILURE_RETRY(close(write_out[1]));
+ VOID_TEMP_FAILURE_RETRY(close(exec_control[0]));
+ VOID_TEMP_FAILURE_RETRY(close(exec_control[1]));
Log::PrintErr("fcntl failed: %s\n", *os_error_message);
return errno;
}
@@ -411,14 +414,14 @@
if (pid < 0) {
SetChildOsErrorMessage(os_error_message);
delete[] program_arguments;
- TEMP_FAILURE_RETRY(close(read_in[0]));
- TEMP_FAILURE_RETRY(close(read_in[1]));
- TEMP_FAILURE_RETRY(close(read_err[0]));
- TEMP_FAILURE_RETRY(close(read_err[1]));
- TEMP_FAILURE_RETRY(close(write_out[0]));
- TEMP_FAILURE_RETRY(close(write_out[1]));
- TEMP_FAILURE_RETRY(close(exec_control[0]));
- TEMP_FAILURE_RETRY(close(exec_control[1]));
+ VOID_TEMP_FAILURE_RETRY(close(read_in[0]));
+ VOID_TEMP_FAILURE_RETRY(close(read_in[1]));
+ VOID_TEMP_FAILURE_RETRY(close(read_err[0]));
+ VOID_TEMP_FAILURE_RETRY(close(read_err[1]));
+ VOID_TEMP_FAILURE_RETRY(close(write_out[0]));
+ VOID_TEMP_FAILURE_RETRY(close(write_out[1]));
+ VOID_TEMP_FAILURE_RETRY(close(exec_control[0]));
+ VOID_TEMP_FAILURE_RETRY(close(exec_control[1]));
return errno;
} else if (pid == 0) {
// Wait for parent process before setting up the child process.
@@ -429,25 +432,25 @@
exit(1);
}
- TEMP_FAILURE_RETRY(close(write_out[1]));
- TEMP_FAILURE_RETRY(close(read_in[0]));
- TEMP_FAILURE_RETRY(close(read_err[0]));
- TEMP_FAILURE_RETRY(close(exec_control[0]));
+ VOID_TEMP_FAILURE_RETRY(close(write_out[1]));
+ VOID_TEMP_FAILURE_RETRY(close(read_in[0]));
+ VOID_TEMP_FAILURE_RETRY(close(read_err[0]));
+ VOID_TEMP_FAILURE_RETRY(close(exec_control[0]));
if (TEMP_FAILURE_RETRY(dup2(write_out[0], STDIN_FILENO)) == -1) {
ReportChildError(exec_control[1]);
}
- TEMP_FAILURE_RETRY(close(write_out[0]));
+ VOID_TEMP_FAILURE_RETRY(close(write_out[0]));
if (TEMP_FAILURE_RETRY(dup2(read_in[1], STDOUT_FILENO)) == -1) {
ReportChildError(exec_control[1]);
}
- TEMP_FAILURE_RETRY(close(read_in[1]));
+ VOID_TEMP_FAILURE_RETRY(close(read_in[1]));
if (TEMP_FAILURE_RETRY(dup2(read_err[1], STDERR_FILENO)) == -1) {
ReportChildError(exec_control[1]);
}
- TEMP_FAILURE_RETRY(close(read_err[1]));
+ VOID_TEMP_FAILURE_RETRY(close(read_err[1]));
if (working_directory != NULL &&
TEMP_FAILURE_RETRY(chdir(working_directory)) == -1) {
@@ -458,7 +461,7 @@
environ = program_environment;
}
- TEMP_FAILURE_RETRY(
+ VOID_TEMP_FAILURE_RETRY(
execvp(path, const_cast<char* const*>(program_arguments)));
ReportChildError(exec_control[1]);
@@ -473,12 +476,12 @@
result = TEMP_FAILURE_RETRY(pipe(event_fds));
if (result < 0) {
SetChildOsErrorMessage(os_error_message);
- TEMP_FAILURE_RETRY(close(read_in[0]));
- TEMP_FAILURE_RETRY(close(read_in[1]));
- TEMP_FAILURE_RETRY(close(read_err[0]));
- TEMP_FAILURE_RETRY(close(read_err[1]));
- TEMP_FAILURE_RETRY(close(write_out[0]));
- TEMP_FAILURE_RETRY(close(write_out[1]));
+ VOID_TEMP_FAILURE_RETRY(close(read_in[0]));
+ VOID_TEMP_FAILURE_RETRY(close(read_in[1]));
+ VOID_TEMP_FAILURE_RETRY(close(read_err[0]));
+ VOID_TEMP_FAILURE_RETRY(close(read_err[1]));
+ VOID_TEMP_FAILURE_RETRY(close(write_out[0]));
+ VOID_TEMP_FAILURE_RETRY(close(write_out[1]));
Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message);
return errno;
}
@@ -499,7 +502,7 @@
// Read exec result from child. If no data is returned the exec was
// successful and the exec call closed the pipe. Otherwise the errno
// is written to the pipe.
- TEMP_FAILURE_RETRY(close(exec_control[1]));
+ VOID_TEMP_FAILURE_RETRY(close(exec_control[1]));
int child_errno;
int bytes_read = -1;
ASSERT(sizeof(child_errno) == sizeof(errno));
@@ -515,16 +518,16 @@
message[kMaxMessageSize - 1] = '\0';
*os_error_message = message;
}
- TEMP_FAILURE_RETRY(close(exec_control[0]));
+ VOID_TEMP_FAILURE_RETRY(close(exec_control[0]));
// Return error code if any failures.
if (bytes_read != 0) {
- TEMP_FAILURE_RETRY(close(read_in[0]));
- TEMP_FAILURE_RETRY(close(read_in[1]));
- TEMP_FAILURE_RETRY(close(read_err[0]));
- TEMP_FAILURE_RETRY(close(read_err[1]));
- TEMP_FAILURE_RETRY(close(write_out[0]));
- TEMP_FAILURE_RETRY(close(write_out[1]));
+ VOID_TEMP_FAILURE_RETRY(close(read_in[0]));
+ VOID_TEMP_FAILURE_RETRY(close(read_in[1]));
+ VOID_TEMP_FAILURE_RETRY(close(read_err[0]));
+ VOID_TEMP_FAILURE_RETRY(close(read_err[1]));
+ VOID_TEMP_FAILURE_RETRY(close(write_out[0]));
+ VOID_TEMP_FAILURE_RETRY(close(write_out[1]));
if (bytes_read == -1) {
return errno; // Read failed.
} else {
@@ -534,13 +537,13 @@
FDUtils::SetNonBlocking(read_in[0]);
*in = read_in[0];
- TEMP_FAILURE_RETRY(close(read_in[1]));
+ VOID_TEMP_FAILURE_RETRY(close(read_in[1]));
FDUtils::SetNonBlocking(write_out[1]);
*out = write_out[1];
- TEMP_FAILURE_RETRY(close(write_out[0]));
+ VOID_TEMP_FAILURE_RETRY(close(write_out[0]));
FDUtils::SetNonBlocking(read_err[0]);
*err = read_err[0];
- TEMP_FAILURE_RETRY(close(read_err[1]));
+ VOID_TEMP_FAILURE_RETRY(close(read_err[1]));
*id = pid;
return 0;
@@ -560,3 +563,5 @@
intptr_t Process::CurrentProcessId() {
return static_cast<intptr_t>(getpid());
}
+
+#endif // defined(TARGET_OS_MACOS)
diff --git a/runtime/bin/process_patch.dart b/runtime/bin/process_patch.dart
index edc29e2..29913b1 100644
--- a/runtime/bin/process_patch.dart
+++ b/runtime/bin/process_patch.dart
@@ -25,7 +25,7 @@
/* patch */ static Future<ProcessResult> run(String executable,
List<String> arguments,
[ProcessOptions options]) {
- return new _NonInteractiveProcess(executable, arguments, options)._result;
+ return _runNonInteractiveProcess(executable, arguments, options);
}
}
@@ -88,13 +88,15 @@
});
}
- _in = new _Socket._internalReadOnly(); // stdout coming from process.
- _out = new _Socket._internalWriteOnly(); // stdin going to process.
- _err = new _Socket._internalReadOnly(); // stderr coming from process.
- _exitHandler = new _Socket._internalReadOnly();
+ // stdin going to process.
+ _stdin = new _Socket._writePipe();
+ // stdout coming from process.
+ _stdout = new _Socket._readPipe();
+ // stderr coming from process.
+ _stderr = new _Socket._readPipe();
+ _exitHandler = new _Socket._readPipe();
_ended = false;
_started = false;
- _onExit = null;
}
String _windowsArgumentEscape(String argument) {
@@ -160,16 +162,12 @@
_arguments,
_workingDirectory,
_environment,
- _in,
- _out,
- _err,
- _exitHandler,
+ _stdin._nativeSocket,
+ _stdout._nativeSocket,
+ _stderr._nativeSocket,
+ _exitHandler._nativeSocket,
status);
if (!success) {
- _in.close();
- _out.close();
- _err.close();
- _exitHandler.close();
completer.completeError(
new ProcessException(_path,
_arguments,
@@ -179,23 +177,12 @@
}
_started = true;
- _in._closed = false;
- _out._closed = false;
- _err._closed = false;
- _exitHandler._closed = false;
-
- // Make sure to activate socket handlers now that the file
- // descriptors have been set.
- _in._activateHandlers();
- _out._activateHandlers();
- _err._activateHandlers();
-
// Setup an exit handler to handle internal cleanup and possible
// callback when a process terminates.
int exitDataRead = 0;
final int EXIT_DATA_SIZE = 8;
List<int> exitDataBuffer = new List<int>.fixedLength(EXIT_DATA_SIZE);
- _exitHandler.inputStream.onData = () {
+ _exitHandler.listen((data) {
int exitCode(List<int> ints) {
var code = _intFromBytes(ints, 0);
@@ -206,18 +193,17 @@
void handleExit() {
_ended = true;
- _exitCode = exitCode(exitDataBuffer);
- if (_onExit != null) _onExit(_exitCode);
- _out.close();
+ _exitCode.complete(exitCode(exitDataBuffer));
+ // Kill stdin, helping hand if the user forgot to do it.
+ _stdin.destroy();
}
- exitDataRead += _exitHandler.inputStream.readInto(
- exitDataBuffer, exitDataRead, EXIT_DATA_SIZE - exitDataRead);
+ exitDataBuffer.setRange(exitDataRead, data.length, data);
+ exitDataRead += data.length;
if (exitDataRead == EXIT_DATA_SIZE) {
- _exitHandler.close();
handleExit();
}
- };
+ });
completer.complete(this);
});
@@ -228,24 +214,29 @@
List<String> arguments,
String workingDirectory,
List<String> environment,
- Socket input,
- Socket output,
- Socket error,
- Socket exitHandler,
+ _NativeSocket stdin,
+ _NativeSocket stdout,
+ _NativeSocket stderr,
+ _NativeSocket exitHandler,
_ProcessStartStatus status) native "Process_Start";
- InputStream get stdout {
- return _in.inputStream;
+ Stream<List<int>> get stdout {
+ // TODO(ajohnsen): Get stream object only.
+ return _stdout;
}
- InputStream get stderr {
- return _err.inputStream;
+ Stream<List<int>> get stderr {
+ // TODO(ajohnsen): Get stream object only.
+ return _stderr;
}
- OutputStream get stdin {
- return _out.outputStream;
+ IOSink get stdin {
+ // TODO(ajohnsen): Get consumer object only.
+ return _stdin;
}
+ Future<int> get exitCode => _exitCode.future;
+
bool kill([ProcessSignal signal = ProcessSignal.SIGTERM]) {
if (signal is! ProcessSignal) {
throw new ArgumentError(
@@ -258,24 +249,18 @@
bool _kill(Process p, int signal) native "Process_Kill";
- void set onExit(void callback(int exitCode)) {
- if (_ended) callback(_exitCode);
- _onExit = callback;
- }
-
String _path;
List<String> _arguments;
String _workingDirectory;
List<String> _environment;
- // Private methods of _Socket are used by _in, _out, and _err.
- _Socket _in;
- _Socket _out;
- _Socket _err;
+ // Private methods of Socket are used by _in, _out, and _err.
+ Socket _stdin;
+ Socket _stdout;
+ Socket _stderr;
Socket _exitHandler;
- int _exitCode;
bool _ended;
bool _started;
- Function _onExit;
+ final Completer<int> _exitCode = new Completer<int>();
}
@@ -283,88 +268,59 @@
// that buffers output so it can be delivered when the process exits.
// _NonInteractiveProcess is used to implement the Process.run
// method.
-class _NonInteractiveProcess {
- _NonInteractiveProcess(String path,
- List<String> arguments,
- ProcessOptions options) {
- _completer = new Completer<ProcessResult>();
- // Extract output encoding options and verify arguments.
- var stdoutEncoding = Encoding.SYSTEM;
- var stderrEncoding = Encoding.SYSTEM;
- if (options != null) {
- if (options.stdoutEncoding != null) {
- stdoutEncoding = options.stdoutEncoding;
- if (stdoutEncoding is !Encoding) {
- throw new ArgumentError(
- 'stdoutEncoding option is not an encoding: $stdoutEncoding');
- }
- }
- if (options.stderrEncoding != null) {
- stderrEncoding = options.stderrEncoding;
- if (stderrEncoding is !Encoding) {
- throw new ArgumentError(
- 'stderrEncoding option is not an encoding: $stderrEncoding');
- }
+Future<ProcessResult> _runNonInteractiveProcess(String path,
+ List<String> arguments,
+ ProcessOptions options) {
+ // Extract output encoding options and verify arguments.
+ var stdoutEncoding = Encoding.SYSTEM;
+ var stderrEncoding = Encoding.SYSTEM;
+ if (options != null) {
+ if (options.stdoutEncoding != null) {
+ stdoutEncoding = options.stdoutEncoding;
+ if (stdoutEncoding is !Encoding) {
+ throw new ArgumentError(
+ 'stdoutEncoding option is not an encoding: $stdoutEncoding');
}
}
+ if (options.stderrEncoding != null) {
+ stderrEncoding = options.stderrEncoding;
+ if (stderrEncoding is !Encoding) {
+ throw new ArgumentError(
+ 'stderrEncoding option is not an encoding: $stderrEncoding');
+ }
+ }
+ }
- // Start the underlying process.
- var processFuture = new _ProcessImpl(path, arguments, options)._start();
+ // Start the underlying process.
+ return Process.start(path, arguments, options).then((Process p) {
+ // Make sure the process stdin is closed.
+ p.stdin.close();
- processFuture.then((Process p) {
- // Make sure the process stdin is closed.
- p.stdin.close();
+ // Setup stdout handling.
+ Future<StringBuffer> stdout = p.stdout
+ .transform(new StringDecoder(stdoutEncoding))
+ .reduce(
+ new StringBuffer(),
+ (buf, data) {
+ buf.add(data);
+ return buf;
+ });
- // Setup process exit handling.
- p.onExit = (exitCode) {
- _exitCode = exitCode;
- _checkDone();
- };
+ Future<StringBuffer> stderr = p.stderr
+ .transform(new StringDecoder(stderrEncoding))
+ .reduce(
+ new StringBuffer(),
+ (buf, data) {
+ buf.add(data);
+ return buf;
+ });
- // Setup stdout handling.
- _stdoutBuffer = new StringBuffer();
- var stdoutStream = new StringInputStream(p.stdout, stdoutEncoding);
- stdoutStream.onData = () {
- var data = stdoutStream.read();
- if (data != null) _stdoutBuffer.add(data);
- };
- stdoutStream.onClosed = () {
- _stdoutClosed = true;
- _checkDone();
- };
-
- // Setup stderr handling.
- _stderrBuffer = new StringBuffer();
- var stderrStream = new StringInputStream(p.stderr, stderrEncoding);
- stderrStream.onData = () {
- var data = stderrStream.read();
- if (data != null) _stderrBuffer.add(data);
- };
- stderrStream.onClosed = () {
- _stderrClosed = true;
- _checkDone();
- };
- }).catchError((error) {
- _completer.completeError(error.error);
+ return Future.wait([p.exitCode, stdout, stderr]).then((result) {
+ return new _ProcessResult(result[0],
+ result[1].toString(),
+ result[2].toString());
});
- }
-
- void _checkDone() {
- if (_exitCode != null && _stderrClosed && _stdoutClosed) {
- _completer.complete(new _ProcessResult(_exitCode,
- _stdoutBuffer.toString(),
- _stderrBuffer.toString()));
- }
- }
-
- Future<ProcessResult> get _result => _completer.future;
-
- Completer<ProcessResult> _completer;
- StringBuffer _stdoutBuffer;
- StringBuffer _stderrBuffer;
- int _exitCode;
- bool _stdoutClosed = false;
- bool _stderrClosed = false;
+ });
}
diff --git a/runtime/bin/process_win.cc b/runtime/bin/process_win.cc
index a073717..407dd37 100644
--- a/runtime/bin/process_win.cc
+++ b/runtime/bin/process_win.cc
@@ -2,7 +2,10 @@
// 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 <process.h>
+#include "platform/globals.h"
+#if defined(TARGET_OS_WINDOWS)
+
+#include <process.h> // NOLINT
#include "bin/builtin.h"
#include "bin/process.h"
@@ -10,7 +13,6 @@
#include "bin/log.h"
#include "bin/thread.h"
#include "bin/utils.h"
-#include "platform/globals.h"
static const int kReadHandle = 0;
static const int kWriteHandle = 1;
@@ -660,3 +662,5 @@
intptr_t Process::CurrentProcessId() {
return static_cast<intptr_t>(GetCurrentProcessId());
}
+
+#endif // defined(TARGET_OS_WINDOWS)
diff --git a/runtime/bin/secure_socket.cc b/runtime/bin/secure_socket.cc
index 603a4c0..0f9140b 100644
--- a/runtime/bin/secure_socket.cc
+++ b/runtime/bin/secure_socket.cc
@@ -312,7 +312,7 @@
Dart_NewPersistentHandle(Dart_ListGetAt(dart_buffers_object, i)));
buffers_[i] = new uint8_t[size];
Dart_Handle data = ThrowIfError(
- Dart_NewExternalByteArray(buffers_[i], size, NULL, NULL));
+ Dart_NewExternalTypedData(kUint8, buffers_[i], size, NULL, NULL));
ThrowIfError(Dart_SetField(dart_buffer_objects_[i],
data_identifier,
data));
@@ -340,7 +340,6 @@
bool report_duplicate_initialization) {
MutexLocker locker(&mutex_);
if (!library_initialized_) {
- library_initialized_ = true;
password_ = strdup(password); // This one copy persists until Dart exits.
PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
// TODO(whesse): Verify there are no UTF-8 issues here.
@@ -362,20 +361,25 @@
SECMOD_DB,
init_flags);
if (status != SECSuccess) {
+ mutex_.Unlock(); // MutexLocker destructor not called when throwing.
ThrowPRException("Failed NSS_Init call.");
}
+ library_initialized_ = true;
status = NSS_SetDomesticPolicy();
if (status != SECSuccess) {
+ mutex_.Unlock(); // MutexLocker destructor not called when throwing.
ThrowPRException("Failed NSS_SetDomesticPolicy call.");
}
// Enable TLS, as well as SSL3 and SSL2.
status = SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE);
if (status != SECSuccess) {
+ mutex_.Unlock(); // MutexLocker destructor not called when throwing.
ThrowPRException("Failed SSL_OptionSetDefault enable TLS call.");
}
status = SSL_ConfigServerSessionIDCache(0, 0, 0, NULL);
if (status != SECSuccess) {
+ mutex_.Unlock(); // MutexLocker destructor not called when throwing.
ThrowPRException("Failed SSL_ConfigServerSessionIDCache call.");
}
@@ -442,17 +446,28 @@
SECStatus status;
if (is_server) {
PK11_SetPasswordFunc(PasswordCallback);
- CERTCertDBHandle* certificate_database = CERT_GetDefaultCertDB();
- if (certificate_database == NULL) {
- ThrowPRException("Certificate database cannot be loaded");
- }
- // TODO(whesse): Switch to a function that looks up certs by nickname,
- // so that server and client uses of certificateName agree.
- CERTCertificate* certificate = CERT_FindCertByNameString(
- certificate_database,
- const_cast<char*>(certificate_name));
- if (certificate == NULL) {
- ThrowPRException("Cannot find server certificate by name");
+
+ CERTCertificate* certificate = NULL;
+ if (strstr(certificate_name, "CN=") != NULL) {
+ // Look up certificate using the distinguished name (DN) certificate_name.
+ CERTCertDBHandle* certificate_database = CERT_GetDefaultCertDB();
+ if (certificate_database == NULL) {
+ ThrowPRException("Certificate database cannot be loaded");
+ }
+ certificate = CERT_FindCertByNameString(certificate_database,
+ const_cast<char*>(certificate_name));
+ if (certificate == NULL) {
+ ThrowPRException(
+ "Cannot find server certificate by distinguished name");
+ }
+ } else {
+ // Look up certificate using the nickname certificate_name.
+ certificate = PK11_FindCertFromNickname(
+ const_cast<char*>(certificate_name),
+ static_cast<void*>(const_cast<char*>(password_)));
+ if (certificate == NULL) {
+ ThrowPRException("Cannot find server certificate by nickname");
+ }
}
SECKEYPrivateKey* key = PK11_FindKeyByAnyCert(
certificate,
diff --git a/runtime/bin/secure_socket_patch.dart b/runtime/bin/secure_socket_patch.dart
index 9e79c86..a52647c 100644
--- a/runtime/bin/secure_socket_patch.dart
+++ b/runtime/bin/secure_socket_patch.dart
@@ -28,8 +28,8 @@
extends NativeFieldWrapperClass1
implements _SecureFilter {
_SecureFilterImpl() {
- buffers = new List<_ExternalBuffer>(_SecureSocket.NUM_BUFFERS);
- for (int i = 0; i < _SecureSocket.NUM_BUFFERS; ++i) {
+ buffers = new List<_ExternalBuffer>(_RawSecureSocket.NUM_BUFFERS);
+ for (int i = 0; i < _RawSecureSocket.NUM_BUFFERS; ++i) {
buffers[i] = new _ExternalBuffer();
}
}
diff --git a/runtime/bin/socket.cc b/runtime/bin/socket.cc
index dfde8f3..d9c20e7 100644
--- a/runtime/bin/socket.cc
+++ b/runtime/bin/socket.cc
@@ -29,7 +29,8 @@
if (DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 2), &port)) {
intptr_t socket = Socket::CreateConnect(host, port);
if (socket >= 0) {
- Socket::SetSocketIdNativeField(socket_obj, socket);
+ Dart_Handle err = Socket::SetSocketIdNativeField(socket_obj, socket);
+ if (Dart_IsError(err)) Dart_PropagateError(err);
Dart_SetReturnValue(args, Dart_True());
} else {
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
@@ -48,7 +49,8 @@
Dart_EnterScope();
Dart_Handle socket_obj = Dart_GetNativeArgument(args, 0);
intptr_t socket = 0;
- Socket::GetSocketIdNativeField(socket_obj, &socket);
+ Dart_Handle err = Socket::GetSocketIdNativeField(socket_obj, &socket);
+ if (Dart_IsError(err)) Dart_PropagateError(err);
intptr_t available = Socket::Available(socket);
if (available >= 0) {
Dart_SetReturnValue(args, Dart_NewInteger(available));
@@ -63,7 +65,8 @@
Dart_EnterScope();
Dart_Handle socket_obj = Dart_GetNativeArgument(args, 0);
intptr_t socket = 0;
- Socket::GetSocketIdNativeField(socket_obj, &socket);
+ Dart_Handle err = Socket::GetSocketIdNativeField(socket_obj, &socket);
+ if (Dart_IsError(err)) Dart_PropagateError(err);
intptr_t available = Socket::Available(socket);
if (available > 0) {
int64_t length = 0;
@@ -110,7 +113,8 @@
static bool short_socket_reads = Dart_IsVMFlagSet("short_socket_read");
Dart_Handle socket_obj = Dart_GetNativeArgument(args, 0);
intptr_t socket = 0;
- Socket::GetSocketIdNativeField(socket_obj, &socket);
+ Dart_Handle err = Socket::GetSocketIdNativeField(socket_obj, &socket);
+ if (Dart_IsError(err)) Dart_PropagateError(err);
Dart_Handle buffer_obj = Dart_GetNativeArgument(args, 1);
int64_t offset = 0;
int64_t length = 0;
@@ -160,7 +164,8 @@
static bool short_socket_writes = Dart_IsVMFlagSet("short_socket_write");
Dart_Handle socket_obj = Dart_GetNativeArgument(args, 0);
intptr_t socket = 0;
- Socket::GetSocketIdNativeField(socket_obj, &socket);
+ Dart_Handle err = Socket::GetSocketIdNativeField(socket_obj, &socket);
+ if (Dart_IsError(err)) Dart_PropagateError(err);
Dart_Handle buffer_obj = Dart_GetNativeArgument(args, 1);
ASSERT(Dart_IsList(buffer_obj));
intptr_t offset =
@@ -180,20 +185,21 @@
intptr_t total_bytes_written = 0;
intptr_t bytes_written = 0;
- if (Dart_IsByteArrayExternal(buffer_obj)) {
- void* buffer = NULL;
- result = Dart_ExternalByteArrayGetData(buffer_obj, &buffer);
- if (Dart_IsError(result)) {
- Dart_PropagateError(result);
- }
- buffer = static_cast<void*>((static_cast<uint8_t*>(buffer) + offset));
+ Dart_TypedData_Type type;
+ uint8_t* buffer = NULL;
+ intptr_t len;
+ result = Dart_TypedDataAcquireData(buffer_obj, &type,
+ reinterpret_cast<void**>(&buffer), &len);
+ if (!Dart_IsError(result)) {
+ buffer += offset;
bytes_written = Socket::Write(socket, buffer, length);
if (bytes_written > 0) total_bytes_written = bytes_written;
+ Dart_TypedDataReleaseData(buffer_obj);
} else {
// Send data in chunks of maximum 16KB.
const intptr_t max_chunk_length =
dart::Utils::Minimum(length, static_cast<intptr_t>(16 * KB));
- uint8_t* buffer = new uint8_t[max_chunk_length];
+ buffer = new uint8_t[max_chunk_length];
do {
intptr_t chunk_length =
dart::Utils::Minimum(max_chunk_length, length - total_bytes_written);
@@ -224,7 +230,8 @@
Dart_EnterScope();
Dart_Handle socket_obj = Dart_GetNativeArgument(args, 0);
intptr_t socket = 0;
- Socket::GetSocketIdNativeField(socket_obj, &socket);
+ Dart_Handle err = Socket::GetSocketIdNativeField(socket_obj, &socket);
+ if (Dart_IsError(err)) Dart_PropagateError(err);
OSError os_error;
intptr_t port = Socket::GetPort(socket);
if (port > 0) {
@@ -240,7 +247,8 @@
Dart_EnterScope();
Dart_Handle socket_obj = Dart_GetNativeArgument(args, 0);
intptr_t socket = 0;
- Socket::GetSocketIdNativeField(socket_obj, &socket);
+ Dart_Handle err = Socket::GetSocketIdNativeField(socket_obj, &socket);
+ if (Dart_IsError(err)) Dart_PropagateError(err);
OSError os_error;
intptr_t port = 0;
char host[INET_ADDRSTRLEN];
@@ -260,7 +268,8 @@
Dart_EnterScope();
Dart_Handle socket_obj = Dart_GetNativeArgument(args, 0);
intptr_t socket = 0;
- Socket::GetSocketIdNativeField(socket_obj, &socket);
+ Dart_Handle err = Socket::GetSocketIdNativeField(socket_obj, &socket);
+ if (Dart_IsError(err)) Dart_PropagateError(err);
OSError os_error;
Socket::GetError(socket, &os_error);
Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
@@ -291,7 +300,8 @@
DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 1));
ASSERT(num == 0 || num == 1 || num == 2);
intptr_t socket = Socket::GetStdioHandle(num);
- Socket::SetSocketIdNativeField(socket_obj, socket);
+ Dart_Handle err = Socket::SetSocketIdNativeField(socket_obj, socket);
+ if (Dart_IsError(err)) Dart_PropagateError(err);
Dart_SetReturnValue(args, Dart_NewBoolean(socket >= 0));
Dart_ExitScope();
}
@@ -312,7 +322,8 @@
intptr_t socket =
ServerSocket::CreateBindListen(bind_address, port, backlog);
if (socket >= 0) {
- Socket::SetSocketIdNativeField(socket_obj, socket);
+ Dart_Handle err = Socket::SetSocketIdNativeField(socket_obj, socket);
+ if (Dart_IsError(err)) Dart_PropagateError(err);
Dart_SetReturnValue(args, Dart_True());
} else {
if (socket == -5) {
@@ -336,11 +347,14 @@
Dart_EnterScope();
Dart_Handle socket_obj = Dart_GetNativeArgument(args, 0);
intptr_t socket = 0;
- Socket::GetSocketIdNativeField(socket_obj, &socket);
+ Dart_Handle err = Socket::GetSocketIdNativeField(socket_obj, &socket);
+ if (Dart_IsError(err)) Dart_PropagateError(err);
Dart_Handle result_socket_obj = Dart_GetNativeArgument(args, 1);
intptr_t new_socket = ServerSocket::Accept(socket);
if (new_socket >= 0) {
- Socket::SetSocketIdNativeField(result_socket_obj, new_socket);
+ Dart_Handle err = Socket::SetSocketIdNativeField(result_socket_obj,
+ new_socket);
+ if (Dart_IsError(err)) Dart_PropagateError(err);
Dart_SetReturnValue(args, Dart_True());
} else if (new_socket == ServerSocket::kTemporaryFailure) {
Dart_SetReturnValue(args, Dart_False());
@@ -374,7 +388,7 @@
void SocketService(Dart_Port dest_port_id,
Dart_Port reply_port_id,
Dart_CObject* message) {
- CObject* response = CObject::False();
+ CObject* response = CObject::IllegalArgumentError();
CObjectArray request(message);
if (message->type == Dart_CObject::kArray) {
if (request.Length() > 1 && request[0]->IsInt32()) {
diff --git a/runtime/bin/socket_android.cc b/runtime/bin/socket_android.cc
index 3e41242..8bd9683 100644
--- a/runtime/bin/socket_android.cc
+++ b/runtime/bin/socket_android.cc
@@ -2,11 +2,14 @@
// 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 <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
+#include "platform/globals.h"
+#if defined(TARGET_OS_ANDROID)
+
+#include <errno.h> // NOLINT
+#include <stdio.h> // NOLINT
+#include <stdlib.h> // NOLINT
+#include <string.h> // NOLINT
+#include <unistd.h> // NOLINT
#include "bin/socket.h"
#include "bin/fdutils.h"
@@ -260,3 +263,5 @@
Log::PrintErr("%s\n", error_message);
}
}
+
+#endif // defined(TARGET_OS_ANDROID)
diff --git a/runtime/bin/socket_linux.cc b/runtime/bin/socket_linux.cc
index ff6c191..746adcf 100644
--- a/runtime/bin/socket_linux.cc
+++ b/runtime/bin/socket_linux.cc
@@ -2,12 +2,15 @@
// 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 <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
+#include "platform/globals.h"
+#if defined(TARGET_OS_LINUX)
+
+#include <errno.h> // NOLINT
+#include <stdio.h> // NOLINT
+#include <stdlib.h> // NOLINT
+#include <string.h> // NOLINT
+#include <sys/stat.h> // NOLINT
+#include <unistd.h> // NOLINT
#include "bin/fdutils.h"
#include "bin/file.h"
@@ -201,10 +204,7 @@
}
fd = TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_STREAM, 0));
- if (fd < 0) {
- Log::PrintErr("Error CreateBind: %s\n", strerror(errno));
- return -1;
- }
+ if (fd < 0) return -1;
FDUtils::SetCloseOnExec(fd);
@@ -222,12 +222,11 @@
reinterpret_cast<struct sockaddr *>(&server_address),
sizeof(server_address))) < 0) {
TEMP_FAILURE_RETRY(close(fd));
- Log::PrintErr("Error Bind: %s\n", strerror(errno));
return -1;
}
- if (TEMP_FAILURE_RETRY(listen(fd, backlog)) != 0) {
- Log::PrintErr("Error Listen: %s\n", strerror(errno));
+ if (TEMP_FAILURE_RETRY(listen(fd, backlog > 0 ? backlog : SOMAXCONN)) != 0) {
+ TEMP_FAILURE_RETRY(close(fd));
return -1;
}
@@ -276,3 +275,5 @@
Log::PrintErr("%s\n", error_message);
}
}
+
+#endif // defined(TARGET_OS_LINUX)
diff --git a/runtime/bin/socket_macos.cc b/runtime/bin/socket_macos.cc
index 20364cb..50ad16f 100644
--- a/runtime/bin/socket_macos.cc
+++ b/runtime/bin/socket_macos.cc
@@ -2,12 +2,15 @@
// 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 <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
+#include "platform/globals.h"
+#if defined(TARGET_OS_MACOS)
+
+#include <errno.h> // NOLINT
+#include <stdio.h> // NOLINT
+#include <stdlib.h> // NOLINT
+#include <string.h> // NOLINT
+#include <sys/stat.h> // NOLINT
+#include <unistd.h> // NOLINT
#include "bin/fdutils.h"
#include "bin/file.h"
@@ -37,7 +40,7 @@
server = gethostbyname(host);
if (server == NULL) {
- TEMP_FAILURE_RETRY(close(fd));
+ VOID_TEMP_FAILURE_RETRY(close(fd));
Log::PrintErr("Error CreateConnect: %s\n", strerror(errno));
return -1;
}
@@ -197,15 +200,12 @@
}
fd = TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_STREAM, 0));
- if (fd < 0) {
- Log::PrintErr("Error CreateBind: %s\n", strerror(errno));
- return -1;
- }
+ if (fd < 0) return -1;
FDUtils::SetCloseOnExec(fd);
int optval = 1;
- TEMP_FAILURE_RETRY(
+ VOID_TEMP_FAILURE_RETRY(
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)));
server_address.sin_family = AF_INET;
@@ -217,13 +217,12 @@
bind(fd,
reinterpret_cast<struct sockaddr *>(&server_address),
sizeof(server_address))) < 0) {
- TEMP_FAILURE_RETRY(close(fd));
- Log::PrintErr("Error Bind: %s\n", strerror(errno));
+ VOID_TEMP_FAILURE_RETRY(close(fd));
return -1;
}
- if (TEMP_FAILURE_RETRY(listen(fd, backlog)) != 0) {
- Log::PrintErr("Error Listen: %s\n", strerror(errno));
+ if (TEMP_FAILURE_RETRY(listen(fd, backlog > 0 ? backlog : SOMAXCONN)) != 0) {
+ TEMP_FAILURE_RETRY(close(fd));
return -1;
}
@@ -262,3 +261,5 @@
Log::PrintErr("%s\n", error_message);
}
}
+
+#endif // defined(TARGET_OS_MACOS)
diff --git a/runtime/bin/socket_patch.dart b/runtime/bin/socket_patch.dart
index bc91a32..15dd4c7 100644
--- a/runtime/bin/socket_patch.dart
+++ b/runtime/bin/socket_patch.dart
@@ -1,20 +1,25 @@
-// Copyright (c) 2012, 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.
-patch class ServerSocket {
- /* patch */ factory ServerSocket(String bindAddress, int port, int backlog) {
- return new _ServerSocket(bindAddress, port, backlog);
+patch class RawServerSocket {
+ /* patch */ static Future<RawServerSocket> bind([String address = "127.0.0.1",
+ int port = 0,
+ int backlog = 0]) {
+ return _RawServerSocket.bind(address, port, backlog);
}
}
-patch class Socket {
- /* patch */ factory Socket(String host, int port) => new _Socket(host, port);
+patch class RawSocket {
+ /* patch */ static Future<RawSocket> connect(String host, int port) {
+ return _RawSocket.connect(host, port);
+ }
}
-class _SocketBase extends NativeFieldWrapperClass1 {
+// The _NativeSocket class encapsulates an OS socket.
+class _NativeSocket extends NativeFieldWrapperClass1 {
// Bit flags used when communicating between the eventhandler and
// dart code. The EVENT flags are used to indicate events of
// interest when sending a message from dart code to the
@@ -24,584 +29,895 @@
// eventhandler. COMMAND flags are never received from the
// eventhandler. Additional flags are used to communicate other
// information.
- static const int _IN_EVENT = 0;
- static const int _OUT_EVENT = 1;
- static const int _ERROR_EVENT = 2;
- static const int _CLOSE_EVENT = 3;
+ static const int READ_EVENT = 0;
+ static const int WRITE_EVENT = 1;
+ static const int ERROR_EVENT = 2;
+ static const int CLOSED_EVENT = 3;
+ static const int FIRST_EVENT = READ_EVENT;
+ static const int LAST_EVENT = CLOSED_EVENT;
+ static const int EVENT_COUNT = LAST_EVENT - FIRST_EVENT + 1;
- static const int _CLOSE_COMMAND = 8;
- static const int _SHUTDOWN_READ_COMMAND = 9;
- static const int _SHUTDOWN_WRITE_COMMAND = 10;
+ static const int CLOSE_COMMAND = 8;
+ static const int SHUTDOWN_READ_COMMAND = 9;
+ static const int SHUTDOWN_WRITE_COMMAND = 10;
+ static const int FIRST_COMMAND = CLOSE_COMMAND;
+ static const int LAST_COMMAND = SHUTDOWN_WRITE_COMMAND;
- // Flag send to the eventhandler providing additional information on
- // the type of the file descriptor.
- static const int _LISTENING_SOCKET = 16;
- static const int _PIPE = 17;
+ // Type flag send to the eventhandler providing additional
+ // information on the type of the file descriptor.
+ static const int LISTENING_SOCKET = 16;
+ static const int PIPE_SOCKET = 17;
+ static const int TYPE_NORMAL_SOCKET = 0;
+ static const int TYPE_LISTENING_SOCKET = 1 << LISTENING_SOCKET;
+ static const int TYPE_PIPE = 1 << PIPE_SOCKET;
- static const int _FIRST_EVENT = _IN_EVENT;
- static const int _LAST_EVENT = _CLOSE_EVENT;
-
- static const int _FIRST_COMMAND = _CLOSE_COMMAND;
- static const int _LAST_COMMAND = _SHUTDOWN_WRITE_COMMAND;
-
- _SocketBase () {
- _handlerMap = new List.fixedLength(_LAST_EVENT + 1);
- _handlerMask = 0;
- _canActivateHandlers = true;
- _closed = true;
- _EventHandler._start();
- _hashCode = _nextHashCode;
- _nextHashCode = (_nextHashCode + 1) & 0xFFFFFFF;
- }
-
- // Multiplexes socket events to the socket handlers.
- void _multiplex(int event_mask) {
- _canActivateHandlers = false;
- for (int i = _FIRST_EVENT; i <= _LAST_EVENT; i++) {
- if (((event_mask & (1 << i)) != 0)) {
- if ((i == _CLOSE_EVENT) && this is _Socket && !_closed) {
- _closedRead = true;
- if (_closedWrite) _close();
- }
-
- var eventHandler = _handlerMap[i];
- if (eventHandler != null || i == _ERROR_EVENT) {
- // Unregister the out handler before executing it. There is
- // no need to notify the eventhandler as handlers are
- // disabled while the event is handled.
- if (i == _OUT_EVENT) _setHandler(i, null, notifyEventhandler: false);
-
- // Don't call the in handler if there is no data available
- // after all.
- if ((i == _IN_EVENT) && (this is _Socket) && (available() == 0)) {
- continue;
- }
- if (i == _ERROR_EVENT) {
- _reportError(_getError(), "");
- close();
- } else {
- eventHandler();
- }
- }
- }
- }
- _canActivateHandlers = true;
- _activateHandlers();
- }
-
- void _setHandler(int event,
- Function callback,
- {bool notifyEventhandler: true}) {
- if (callback == null) {
- _handlerMask &= ~(1 << event);
- } else {
- _handlerMask |= (1 << event);
- }
- _handlerMap[event] = callback;
- // If the socket is only for writing then close the receive port
- // when not waiting for any events.
- if (this is _Socket &&
- _closedRead &&
- _handlerMask == 0 &&
- _handler != null) {
- _handler.close();
- _handler = null;
- } else {
- if (notifyEventhandler) _activateHandlers();
- }
- }
-
- OSError _getError() native "Socket_GetError";
-
- int _getPort() native "Socket_GetPort";
-
- void set onError(void callback(e)) {
- _setHandler(_ERROR_EVENT, callback);
- }
-
- void _activateHandlers() {
- if (_canActivateHandlers && !_closed) {
- if (_handlerMask == 0) {
- if (_handler != null) {
- _handler.close();
- _handler = null;
- }
- return;
- }
- int data = _handlerMask;
- if (_isListenSocket()) {
- data |= (1 << _LISTENING_SOCKET);
- } else {
- if (_closedRead) { data &= ~(1 << _IN_EVENT); }
- if (_closedWrite) { data &= ~(1 << _OUT_EVENT); }
- if (_isPipe()) data |= (1 << _PIPE);
- }
- _sendToEventHandler(data);
- }
- }
-
- int get port {
- if (_port == null) {
- _port = _getPort();
- }
- return _port;
- }
-
- void close([bool halfClose = false]) {
- if (!_closed) {
- if (halfClose) {
- _closeWrite();
- } else {
- _close();
- }
- } else if (_handler != null) {
- // This is to support closing sockets created but never assigned
- // any actual socket.
- _handler.close();
- _handler = null;
- }
- }
-
- void _closeWrite() {
- if (!_closed) {
- if (_closedRead) {
- _close();
- } else {
- _sendToEventHandler(1 << _SHUTDOWN_WRITE_COMMAND);
- }
- _closedWrite = true;
- }
- }
-
- void _closeRead() {
- if (!_closed) {
- if (_closedWrite) {
- _close();
- } else {
- _sendToEventHandler(1 << _SHUTDOWN_READ_COMMAND);
- }
- _closedRead = true;
- }
- }
-
- void _close() {
- if (!_closed) {
- _sendToEventHandler(1 << _CLOSE_COMMAND);
- _handler.close();
- _handler = null;
- _closed = true;
- }
- }
-
- void _sendToEventHandler(int data) {
- if (_handler == null) {
- _handler = new ReceivePort();
- _handler.receive((var message, ignored) { _multiplex(message); });
- }
- assert(!_closed);
- _EventHandler._sendData(this, _handler, data);
- }
-
- bool _reportError(error, String message) {
- void doReportError(Exception e) {
- // Invoke the socket error callback if any.
- bool reported = false;
- if (_handlerMap[_ERROR_EVENT] != null) {
- _handlerMap[_ERROR_EVENT](e);
- reported = true;
- }
- // Propagate the error to any additional listeners.
- reported = reported || _propagateError(e);
- if (!reported) throw e;
- }
-
- // For all errors we close the socket, call the error handler and
- // disable further calls of the error handler.
- close();
- if (error is OSError) {
- doReportError(new SocketIOException(message, error));
- } else if (error is List) {
- assert(_isErrorResponse(error));
- switch (error[0]) {
- case _ILLEGAL_ARGUMENT_RESPONSE:
- doReportError(new ArgumentError());
- break;
- case _OSERROR_RESPONSE:
- doReportError(new SocketIOException(
- message, new OSError(error[2], error[1])));
- break;
- default:
- doReportError(new Exception("Unknown error"));
- break;
- }
- } else {
- doReportError(new SocketIOException(message));
- }
- }
-
- int get hashCode => _hashCode;
-
- bool _propagateError(Exception e) => false;
-
- bool _isListenSocket();
- bool _isPipe();
-
- // Is this socket closed.
- bool _closed;
-
- // Dedicated ReceivePort for socket events.
- ReceivePort _handler;
-
- // Poll event to handler map.
- List _handlerMap;
-
- // Indicates for which poll events the socket registered handlers.
- int _handlerMask;
-
- // Indicates if native interrupts can be activated.
- bool _canActivateHandlers;
-
- // Holds the port of the socket, null if not known.
- int _port;
-
- // Hash code for the socket. Currently this is just a counter.
- int _hashCode;
- static int _nextHashCode = 0;
- bool _closedRead = false;
- bool _closedWrite = false;
-}
-
-
-class _ServerSocket extends _SocketBase implements ServerSocket {
- // Constructor for server socket. First a socket object is allocated
- // in which the native socket is stored. After that _createBind
- // is called which creates a file descriptor and binds the given address
- // and port to the socket. Null is returned if file descriptor creation or
- // bind failed.
- factory _ServerSocket(String bindAddress, int port, int backlog) {
- _ServerSocket socket = new _ServerSocket._internal();
- var result = socket._createBindListen(bindAddress, port, backlog);
- if (result is OSError) {
- socket.close();
- throw new SocketIOException("Failed to create server socket", result);
- }
- socket._closed = false;
- assert(result);
- if (port != 0) {
- socket._port = port;
- }
- return socket;
- }
-
- _ServerSocket._internal();
-
- _accept(Socket socket) native "ServerSocket_Accept";
-
- _createBindListen(String bindAddress, int port, int backlog)
- native "ServerSocket_CreateBindListen";
-
- void set onConnection(void callback(Socket connection)) {
- _clientConnectionHandler = callback;
- _setHandler(_SocketBase._IN_EVENT,
- _clientConnectionHandler != null ? _connectionHandler : null);
- }
-
- void _connectionHandler() {
- if (!_closed) {
- _Socket socket = new _Socket._internal();
- var result = _accept(socket);
- if (result is OSError) {
- _reportError(result, "Accept failed");
- } else if (result) {
- socket._closed = false;
- _clientConnectionHandler(socket);
- } else {
- // Temporary failure accepting the connection. Ignoring
- // temporary failures lets us retry when we wake up with data
- // on the listening socket again.
- }
- }
- }
-
- bool _isListenSocket() => true;
- bool _isPipe() => false;
-
- var _clientConnectionHandler;
-}
-
-
-class _Socket extends _SocketBase implements Socket {
+ // Native port messages.
static const HOST_NAME_LOOKUP = 0;
- // Constructs a new socket. During the construction an asynchronous
- // host name lookup is initiated. The returned socket is not yet
- // connected but ready for registration of callbacks.
- factory _Socket(String host, int port) {
- Socket socket = new _Socket._internal();
- _ensureSocketService();
- List request = new List.fixedLength(2);
- request[0] = HOST_NAME_LOOKUP;
- request[1] = host;
- _socketService.call(request).then((response) {
- if (socket._isErrorResponse(response)) {
- socket._reportError(response, "Failed host name lookup");
- } else{
- var result = socket._createConnect(response, port);
+ // Socket close state
+ bool isClosed = false;
+ bool isClosedRead = false;
+ bool isClosedWrite = false;
+ Completer closeCompleter = new Completer();
+
+ // Handlers and receive port for socket events from the event handler.
+ int eventMask = 0;
+ List eventHandlers;
+ ReceivePort eventPort;
+
+ // Indicates if native interrupts can be activated.
+ bool canActivateEvents = true;
+
+ // The type flags for this socket.
+ final int typeFlags;
+
+ // Holds the port of the socket, null if not known.
+ int localPort;
+
+ // Native port for socket services.
+ static SendPort socketService;
+
+ static Future<_NativeSocket> connect(String host, int port) {
+ var completer = new Completer();
+ ensureSocketService();
+ socketService.call([HOST_NAME_LOOKUP, host]).then((response) {
+ if (isErrorResponse(response)) {
+ completer.completeError(
+ createError(response, "Failed host name lookup"));
+ } else {
+ var socket = new _NativeSocket.normal();
+ var result = socket.nativeCreateConnect(response, port);
if (result is OSError) {
- socket.close();
- socket._reportError(result, "Connection failed");
+ completer.completeError(createError(result, "Connection failed"));
} else {
- socket._closed = false;
- socket._activateHandlers();
+ // Setup handlers for receiving the first write event which
+ // indicate that the socket is fully connected.
+ socket.setHandlers(
+ write: () {
+ socket.setListening(read: false, write: false);
+ completer.complete(socket);
+ },
+ error: (e) {
+ socket.close();
+ completer.completeError(createError(e, "Connection failed"));
+ }
+ );
+ socket.setListening(read: false, write: true);
}
}
});
- return socket;
+ return completer.future;
}
- _Socket._internal();
- _Socket._internalReadOnly() : _pipe = true { super._closedWrite = true; }
- _Socket._internalWriteOnly() : _pipe = true { super._closedRead = true; }
+ static Future<_NativeSocket> bind(String address,
+ int port,
+ int backlog) {
+ var socket = new _NativeSocket.listen();
+ var result = socket.nativeCreateBindListen(address, port, backlog);
+ if (result is OSError) {
+ return new Future.immediateError(
+ new SocketIOException("Failed to create server socket", result));
+ }
+ if (port != 0) socket.localPort = port;
+ return new Future.immediate(socket);
+ }
+
+ _NativeSocket.normal() : typeFlags = TYPE_NORMAL_SOCKET {
+ eventHandlers = new List.fixedLength(EVENT_COUNT + 1);
+ _EventHandler._start();
+ }
+
+ _NativeSocket.listen() : typeFlags = TYPE_LISTENING_SOCKET {
+ eventHandlers = new List.fixedLength(EVENT_COUNT + 1);
+ _EventHandler._start();
+ }
+
+ _NativeSocket.pipe() : typeFlags = TYPE_PIPE {
+ eventHandlers = new List.fixedLength(EVENT_COUNT + 1);
+ _EventHandler._start();
+ }
int available() {
- if (!_closed) {
- var result = _available();
- if (result is OSError) {
- _reportError(result, "Available failed");
- return 0;
- } else {
- return result;
- }
+ if (isClosed) return 0;
+ var result = nativeAvailable();
+ if (result is OSError) {
+ reportError(result, "Available failed");
+ return 0;
+ } else {
+ return result;
}
- throw new
- SocketIOException("Error: available failed - invalid socket handle");
}
- _available() native "Socket_Available";
-
- List<int> read([int len]) {
+ List<int> read(int len) {
if (len != null && len <= 0) {
- throw new SocketIOException("Illegal length $len");
+ throw new ArgumentError("Illegal length $len");
}
- var result = _read(len == null ? -1 : len);
+ var result = nativeRead(len == null ? -1 : len);
if (result is OSError) {
- _reportError(result, "Read failed");
+ reportError(result, "Read failed");
return null;
}
return result;
}
- _read(int len) native "Socket_Read";
-
- int readList(List<int> buffer, int offset, int bytes) {
- if (!_closed) {
- if (bytes == 0) {
- return 0;
- }
- if (offset < 0) {
- throw new RangeError.value(offset);
- }
- if (bytes < 0) {
- throw new RangeError.value(bytes);
- }
- if ((offset + bytes) > buffer.length) {
- throw new RangeError.value(offset + bytes);
- }
- var result = _readList(buffer, offset, bytes);
- if (result is OSError) {
- _reportError(result, "Read failed");
- return -1;
- }
- return result;
+ int write(List<int> buffer, int offset, int bytes) {
+ if (buffer is! List) throw new ArgumentError();
+ if (offset == null) offset = 0;
+ if (bytes == null) bytes = buffer.length;
+ if (offset < 0) throw new RangeError.value(offset);
+ if (bytes < 0) throw new RangeError.value(bytes);
+ if ((offset + bytes) > buffer.length) {
+ throw new RangeError.value(offset + bytes);
}
- throw new
- SocketIOException("Error: readList failed - invalid socket handle");
- }
-
- _readList(List<int> buffer, int offset, int bytes) native "Socket_ReadList";
-
- int writeList(List<int> buffer, int offset, int bytes) {
- if (buffer is! List || offset is! int || bytes is! int) {
- throw new ArgumentError(
- "Invalid arguments to writeList on Socket");
+ if (offset is! int || bytes is! int) {
+ throw new ArgumentError("Invalid arguments to write on Socket");
}
- if (!_closed) {
- if (bytes == 0) {
- return 0;
- }
- if (offset < 0) {
- throw new RangeError.value(offset);
- }
- if (bytes < 0) {
- throw new RangeError.value(bytes);
- }
- if ((offset + bytes) > buffer.length) {
- throw new RangeError.value(offset + bytes);
- }
- _BufferAndOffset bufferAndOffset =
- _ensureFastAndSerializableBuffer(buffer, offset, bytes);
- var result =
- _writeList(bufferAndOffset.buffer, bufferAndOffset.offset, bytes);
- if (result is OSError) {
- _reportError(result, "Write failed");
- // If writing fails we return 0 as the number of bytes and
- // report the error on the error handler.
- result = 0;
- }
- return result;
+ if (isClosed) return 0;
+ if (bytes == 0) return 0;
+ _BufferAndOffset bufferAndOffset =
+ _ensureFastAndSerializableBuffer(buffer, offset, bytes);
+ var result =
+ nativeWrite(bufferAndOffset.buffer, bufferAndOffset.offset, bytes);
+ if (result is OSError) {
+ reportError(result, "Write failed");
+ result = 0;
}
- throw new SocketIOException("writeList failed - invalid socket handle");
+ return result;
}
- _writeList(List<int> buffer, int offset, int bytes) native "Socket_WriteList";
-
- bool _isErrorResponse(response) {
- return response is List && response[0] != _SUCCESS_RESPONSE;
+ _NativeSocket accept() {
+ var socket = new _NativeSocket.normal();
+ if (nativeAccept(socket) != true) return null;
+ return socket;
}
- bool _createConnect(String host, int port) native "Socket_CreateConnect";
-
- void set onWrite(void callback()) {
- if (_outputStream != null) throw new StreamException(
- "Cannot set write handler when output stream is used");
- _clientWriteHandler = callback;
- _updateOutHandler();
- }
-
- void set onConnect(void callback()) {
- if (_seenFirstOutEvent) {
- throw new StreamException(
- "Cannot set connect handler when already connected");
- }
- _clientConnectHandler = callback;
- _updateOutHandler();
- }
-
- void set onData(void callback()) {
- if (_inputStream != null) throw new StreamException(
- "Cannot set data handler when input stream is used");
- _onData = callback;
- }
-
- void set onClosed(void callback()) {
- if (_inputStream != null) throw new StreamException(
- "Cannot set close handler when input stream is used");
- _onClosed = callback;
- }
-
- bool _isListenSocket() => false;
-
- bool _isPipe() => _pipe;
-
- InputStream get inputStream {
- if (_inputStream == null) {
- if (_handlerMap[_SocketBase._IN_EVENT] != null ||
- _handlerMap[_SocketBase._CLOSE_EVENT] != null) {
- throw new StreamException(
- "Cannot get input stream when socket handlers are used");
- }
- _inputStream = new _SocketInputStream(this);
- }
- return _inputStream;
- }
-
- OutputStream get outputStream {
- if (_outputStream == null) {
- if (_clientWriteHandler != null) {
- throw new StreamException(
- "Cannot get output stream when socket handlers are used");
- }
- _outputStream = new _SocketOutputStream(this);
- }
- return _outputStream;
- }
-
- void set _onWrite(void callback()) {
- _setHandler(_SocketBase._OUT_EVENT, callback);
- }
-
- void set _onData(void callback()) {
- _setHandler(_SocketBase._IN_EVENT, callback);
- }
-
- void set _onClosed(void callback()) {
- _setHandler(_SocketBase._CLOSE_EVENT, callback);
- }
-
- bool _propagateError(Exception e) {
- bool reported = false;
- if (_inputStream != null) {
- reported = reported || _inputStream._onSocketError(e);
- }
- if (_outputStream != null) {
- reported = reported || _outputStream._onSocketError(e);
- }
- return reported;
- }
-
- void _updateOutHandler() {
- void firstWriteHandler() {
- assert(!_seenFirstOutEvent);
- _seenFirstOutEvent = true;
-
- // From now on the write handler is only the client write
- // handler (connect handler cannot be called again). Change this
- // before calling any handlers as handlers can change the
- // handlers.
- if (_clientWriteHandler == null) _onWrite = _clientWriteHandler;
-
- // First out event is socket connected event.
- if (_clientConnectHandler != null) _clientConnectHandler();
- _clientConnectHandler = null;
-
- // Always (even for the first out event) call the write handler.
- if (_clientWriteHandler != null) _clientWriteHandler();
- }
-
- if (_clientConnectHandler == null && _clientWriteHandler == null) {
- _onWrite = null;
- } else {
- if (_seenFirstOutEvent) {
- _onWrite = _clientWriteHandler;
- } else {
- _onWrite = firstWriteHandler;
- }
- }
+ int get port {
+ if (localPort != null) return localPort;
+ return localPort = nativeGetPort();
}
int get remotePort {
- if (_remotePort == null) {
- remoteHost;
- }
- return _remotePort;
+ return nativeGetRemotePeer()[1];
}
String get remoteHost {
- if (_remoteHost == null) {
- List peer = _getRemotePeer();
- _remoteHost = peer[0];
- _remotePort = peer[1];
- }
- return _remoteHost;
+ return nativeGetRemotePeer()[0];
}
- List _getRemotePeer() native "Socket_GetRemotePeer";
+ // Multiplexes socket events to the socket handlers.
+ void multiplex(int events) {
+ canActivateEvents = false;
+ for (int i = FIRST_EVENT; i <= LAST_EVENT; i++) {
+ if (((events & (1 << i)) != 0)) {
+ if (i == CLOSED_EVENT &&
+ typeFlags != TYPE_LISTENING_SOCKET &&
+ !isClosed) {
+ isClosedRead = true;
+ }
- static SendPort _newServicePort() native "Socket_NewServicePort";
+ var handler = eventHandlers[i];
+ assert(handler != null);
+ if (i == WRITE_EVENT) {
+ // If the event was disabled before we had a chance to fire the event,
+ // discard it. If we register again, we'll get a new one.
+ if ((eventMask & (1 << i)) == 0) continue;
+ // Unregister the out handler before executing it. There is
+ // no need to notify the eventhandler as handlers are
+ // disabled while the event is handled.
+ eventMask &= ~(1 << i);
+ }
- static void _ensureSocketService() {
- if (_socketService == null) {
- _socketService = _Socket._newServicePort();
+ // Don't call the in handler if there is no data available
+ // after all.
+ if (i == READ_EVENT &&
+ typeFlags != TYPE_LISTENING_SOCKET &&
+ available() == 0) {
+ continue;
+ }
+ if (i == ERROR_EVENT) {
+ reportError(nativeGetError(), "");
+ } else if (!isClosed) {
+ handler();
+ }
+ }
+ }
+ if (isClosedRead && isClosedWrite) close();
+ canActivateEvents = true;
+ activateHandlers();
+ }
+
+ void setHandlers({read: null, write: null, error: null, closed: null}) {
+ eventHandlers[READ_EVENT] = read;
+ eventHandlers[WRITE_EVENT] = write;
+ eventHandlers[ERROR_EVENT] = error;
+ eventHandlers[CLOSED_EVENT] = closed;
+ }
+
+ void setListening({read: true, write: true}) {
+ eventMask = (1 << CLOSED_EVENT) | (1 << ERROR_EVENT);
+ if (read) eventMask |= (1 << READ_EVENT);
+ if (write) eventMask |= (1 << WRITE_EVENT);
+ activateHandlers();
+ }
+
+ Future get closeFuture => closeCompleter.future;
+
+ void activateHandlers() {
+ if (canActivateEvents && !isClosed) {
+ // If we don't listen for either read or write, disconnect as we won't
+ // get close and error events anyway.
+ if ((eventMask & ((1 << READ_EVENT) | (1 << WRITE_EVENT))) == 0) {
+ if (eventPort != null) disconnectFromEventHandler();
+ } else {
+ int data = eventMask;
+ data |= typeFlags;
+ if (isClosedRead) data &= ~(1 << READ_EVENT);
+ if (isClosedWrite) data &= ~(1 << WRITE_EVENT);
+ sendToEventHandler(data);
+ }
}
}
- bool _seenFirstOutEvent = false;
- bool _pipe = false;
- Function _clientConnectHandler;
- Function _clientWriteHandler;
- _SocketInputStream _inputStream;
- _SocketOutputStream _outputStream;
- String _remoteHost;
- int _remotePort;
- static SendPort _socketService;
+ void close() {
+ if (!isClosed) {
+ sendToEventHandler(1 << CLOSE_COMMAND);
+ isClosed = true;
+ closeCompleter.complete(this);
+ }
+ // Outside the if support closing sockets created but never
+ // assigned any actual socket.
+ disconnectFromEventHandler();
+ }
+
+ void shutdown(SocketDirection direction) {
+ if (!isClosed) {
+ switch (direction) {
+ case SocketDirection.RECEIVE:
+ shutdownRead();
+ break;
+ case SocketDirection.SEND:
+ shutdownWrite();
+ break;
+ case SocketDirection.BOTH:
+ close();
+ break;
+ default:
+ throw new ArgumentError(direction);
+ }
+ }
+ }
+
+ void shutdownWrite() {
+ if (!isClosed) {
+ if (isClosedRead) {
+ close();
+ } else {
+ sendToEventHandler(1 << SHUTDOWN_WRITE_COMMAND);
+ }
+ isClosedWrite = true;
+ }
+ }
+
+ void shutdownRead() {
+ if (!isClosed) {
+ if (isClosedWrite) {
+ close();
+ } else {
+ sendToEventHandler(1 << SHUTDOWN_READ_COMMAND);
+ }
+ isClosedRead = true;
+ }
+ }
+
+ void sendToEventHandler(int data) {
+ connectToEventHandler();
+ assert(!isClosed);
+ _EventHandler._sendData(this, eventPort, data);
+ }
+
+ void connectToEventHandler() {
+ if (eventPort == null) {
+ eventPort = new ReceivePort();
+ eventPort.receive ((var message, _) => multiplex(message));
+ }
+ }
+
+ void disconnectFromEventHandler() {
+ if (eventPort != null) {
+ eventPort.close();
+ eventPort = null;
+ }
+ }
+
+ static void ensureSocketService() {
+ if (socketService == null) {
+ socketService = _NativeSocket.newServicePort();
+ }
+ }
+
+ // Check whether this is an error response from a native port call.
+ static bool isErrorResponse(response) {
+ return response is List && response[0] != _SUCCESS_RESPONSE;
+ }
+
+ // Create the appropriate error/exception from different returned
+ // error objects.
+ static createError(error, String message) {
+ if (error is OSError) {
+ return new SocketIOException(message, error);
+ } else if (error is List) {
+ assert(isErrorResponse(error));
+ switch (error[0]) {
+ case _ILLEGAL_ARGUMENT_RESPONSE:
+ return new ArgumentError();
+ case _OSERROR_RESPONSE:
+ return new SocketIOException(
+ message, new OSError(error[2], error[1]));
+ default:
+ return new Exception("Unknown error");
+ }
+ } else {
+ return new SocketIOException(message);
+ }
+ }
+
+ void reportError(error, String message) {
+ var e = createError(error, message);
+ // Invoke the error handler if any.
+ if (eventHandlers[ERROR_EVENT] != null) {
+ eventHandlers[ERROR_EVENT](e);
+ }
+ // For all errors we close the socket
+ close();
+ }
+
+ nativeAvailable() native "Socket_Available";
+ nativeRead(int len) native "Socket_Read";
+ nativeWrite(List<int> buffer, int offset, int bytes)
+ native "Socket_WriteList";
+ bool nativeCreateConnect(String host, int port) native "Socket_CreateConnect";
+ nativeCreateBindListen(String address, int port, int backlog)
+ native "ServerSocket_CreateBindListen";
+ nativeAccept(_NativeSocket socket) native "ServerSocket_Accept";
+ int nativeGetPort() native "Socket_GetPort";
+ List nativeGetRemotePeer() native "Socket_GetRemotePeer";
+ OSError nativeGetError() native "Socket_GetError";
+
+ static SendPort newServicePort() native "Socket_NewServicePort";
+}
+
+
+class _RawServerSocket extends Stream<RawSocket>
+ implements RawServerSocket {
+ final _NativeSocket _socket;
+ StreamController<RawSocket> _controller;
+
+ static Future<_RawServerSocket> bind(String address,
+ int port,
+ int backlog) {
+ if (port < 0 || port > 0xFFFF)
+ throw new ArgumentError("Invalid port $port");
+ if (backlog < 0) throw new ArgumentError("Invalid backlog $backlog");
+ return _NativeSocket.bind(address, port, backlog)
+ .then((socket) => new _RawServerSocket(socket));
+ }
+
+ _RawServerSocket(this._socket) {
+ _controller = new StreamController(
+ onSubscriptionStateChange: _onSubscriptionStateChange,
+ onPauseStateChange: _onPauseStateChange);
+ _socket.closeFuture.then((_) => _controller.close());
+ _socket.setHandlers(
+ read: () {
+ var socket = _socket.accept();
+ if (socket != null) _controller.add(new _RawSocket(socket));
+ },
+ error: (e) {
+ _controller.signalError(new AsyncError(e));
+ _controller.close();
+ }
+ );
+ }
+
+ StreamSubscription<RawSocket> listen(void onData(RawSocket event),
+ {void onError(AsyncError error),
+ void onDone(),
+ bool unsubscribeOnError}) {
+ return _controller.stream.listen(
+ onData,
+ onError: onError,
+ onDone: onDone,
+ unsubscribeOnError: unsubscribeOnError);
+ }
+
+ int get port => _socket.port;
+
+ void close() => _socket.close();
+
+ void _pause() {
+ _socket.setListening(read: false, write: false);
+ }
+
+ void _resume() {
+ _socket.setListening(read: true, write: false);
+ }
+
+ void _onSubscriptionStateChange() {
+ if (_controller.hasSubscribers) {
+ _resume();
+ } else {
+ close();
+ }
+ }
+ void _onPauseStateChange() {
+ if (_controller.isPaused) {
+ _pause();
+ } else {
+ _resume();
+ }
+ }
+}
+
+
+class _RawSocket extends Stream<RawSocketEvent>
+ implements RawSocket {
+ final _NativeSocket _socket;
+ StreamController<RawSocketEvent> _controller;
+ bool _readEventsEnabled = true;
+ bool _writeEventsEnabled = true;
+
+ static Future<RawSocket> connect(String host, int port) {
+ return _NativeSocket.connect(host, port)
+ .then((socket) => new _RawSocket(socket));
+ }
+
+ _RawSocket(this._socket) {
+ _controller = new StreamController(
+ onSubscriptionStateChange: _onSubscriptionStateChange,
+ onPauseStateChange: _onPauseStateChange);
+ _socket.closeFuture.then((_) => _controller.close());
+ _socket.setHandlers(
+ read: () => _controller.add(RawSocketEvent.READ),
+ write: () {
+ // The write event handler is automatically disabled by the
+ // event handler when it fires.
+ _writeEventsEnabled = false;
+ _controller.add(RawSocketEvent.WRITE);
+ },
+ closed: () => _controller.add(RawSocketEvent.READ_CLOSED),
+ error: (e) {
+ _controller.signalError(new AsyncError(e));
+ close();
+ }
+ );
+ }
+
+ factory _RawSocket._writePipe(int fd) {
+ var native = new _NativeSocket.pipe();
+ native.isClosedRead = true;
+ if (fd != null) _getStdioHandle(native, fd);
+ return new _RawSocket(native);
+ }
+
+ factory _RawSocket._readPipe(int fd) {
+ var native = new _NativeSocket.pipe();
+ native.isClosedWrite = true;
+ if (fd != null) _getStdioHandle(native, fd);
+ return new _RawSocket(native);
+ }
+
+ StreamSubscription<RawSocketEvent> listen(void onData(RawSocketEvent event),
+ {void onError(AsyncError error),
+ void onDone(),
+ bool unsubscribeOnError}) {
+ return _controller.stream.listen(
+ onData,
+ onError: onError,
+ onDone: onDone,
+ unsubscribeOnError: unsubscribeOnError);
+ }
+
+ int available() => _socket.available();
+
+ List<int> read([int len]) => _socket.read(len);
+
+ int write(List<int> buffer, [int offset, int count]) =>
+ _socket.write(buffer, offset, count);
+
+ void close() => _socket.close();
+
+ void shutdown(SocketDirection direction) => _socket.shutdown(direction);
+
+ int get port => _socket.port;
+
+ int get remotePort => _socket.remotePort;
+
+ String get remoteHost => _socket.remoteHost;
+
+ bool get readEventsEnabled => _readEventsEnabled;
+ void set readEventsEnabled(bool value) {
+ if (value != _readEventsEnabled) {
+ _readEventsEnabled = value;
+ if (!_controller.isPaused) _resume();
+ }
+ }
+
+ bool get writeEventsEnabled => _writeEventsEnabled;
+ void set writeEventsEnabled(bool value) {
+ if (value != _writeEventsEnabled) {
+ _writeEventsEnabled = value;
+ if (!_controller.isPaused) _resume();
+ }
+ }
+
+ _pause() {
+ _socket.setListening(read: false, write: false);
+ }
+
+ void _resume() {
+ _socket.setListening(read: _readEventsEnabled, write: _writeEventsEnabled);
+ }
+
+ void _onPauseStateChange() {
+ if (_controller.isPaused) {
+ _pause();
+ } else {
+ _resume();
+ }
+ }
+
+ void _onSubscriptionStateChange() {
+ if (_controller.hasSubscribers) {
+ _resume();
+ } else {
+ close();
+ }
+ }
+}
+
+
+patch class ServerSocket {
+ /* patch */ static Future<ServerSocket> bind([String address = "127.0.0.1",
+ int port = 0,
+ int backlog = 0]) {
+ return _ServerSocket.bind(address, port, backlog);
+ }
+}
+
+class _ServerSocket extends Stream<Socket>
+ implements ServerSocket {
+ final _socket;
+
+ static Future<_ServerSocket> bind(String address,
+ int port,
+ int backlog) {
+ return _RawServerSocket.bind(address, port, backlog)
+ .then((socket) => new _ServerSocket(socket));
+ }
+
+ _ServerSocket(this._socket);
+
+ StreamSubscription<Socket> listen(void onData(Socket event),
+ {void onError(AsyncError error),
+ void onDone(),
+ bool unsubscribeOnError}) {
+ return _socket.map((rawSocket) => new _Socket(rawSocket)).listen(
+ onData,
+ onError: onError,
+ onDone: onDone,
+ unsubscribeOnError: unsubscribeOnError);
+ }
+
+ int get port => _socket.port;
+
+ void close() => _socket.close();
+}
+
+
+patch class Socket {
+ /* patch */ static Future<Socket> connect(String host, int port) {
+ return RawSocket.connect(host, port).then(
+ (socket) => new _Socket(socket));
+ }
+}
+
+
+patch class SecureSocket {
+ /* patch */ factory SecureSocket._(RawSecureSocket rawSocket) =>
+ new _SecureSocket(rawSocket);
+}
+
+
+class _SocketStreamConsumer extends StreamConsumer<List<int>, Socket> {
+ StreamSubscription subscription;
+ final _Socket socket;
+ int offset;
+ List<int> buffer;
+ bool paused = false;
+
+ _SocketStreamConsumer(this.socket);
+
+ Future<Socket> consume(Stream<List<int>> stream) {
+ subscription = stream.listen(
+ (data) {
+ assert(!paused);
+ assert(buffer == null);
+ buffer = data;
+ offset = 0;
+ write();
+ },
+ onDone: () {
+ socket._consumerDone();
+ });
+ return socket._doneFuture;
+ }
+
+ void write() {
+ try {
+ if (subscription == null) return;
+ assert(buffer != null);
+ // Write as much as possible.
+ offset += socket._write(buffer, offset, buffer.length - offset);
+ if (offset < buffer.length) {
+ if (!paused) {
+ paused = true;
+ // TODO(ajohnsen): It would be nice to avoid this check.
+ // Some info: socket._write can emit an event, if it fails to write.
+ // If the user closes the socket in that event, stop() will be called
+ // before we get a change to pause.
+ if (subscription == null) return;
+ subscription.pause();
+ }
+ socket._enableWriteEvent();
+ } else {
+ buffer = null;
+ if (paused) {
+ paused = false;
+ subscription.resume();
+ }
+ }
+ } catch (e) {
+ socket._consumerDone(e);
+ }
+ }
+
+ void stop() {
+ if (subscription == null) return;
+ subscription.cancel();
+ subscription = null;
+ socket._disableWriteEvent();
+ }
+}
+
+
+class _Socket extends Stream<List<int>> implements Socket {
+ RawSocket _raw; // Set to null when the raw socket is closed.
+ bool _closed = false; // Set to true when the raw socket is closed.
+ StreamController _controller;
+ bool _controllerClosed = false;
+ _SocketStreamConsumer _consumer;
+ IOSink<Socket> _sink;
+ Completer _doneCompleter;
+ var _subscription;
+
+ _Socket(RawSocket this._raw) {
+ _controller = new StreamController<List<int>>(
+ onSubscriptionStateChange: _onSubscriptionStateChange,
+ onPauseStateChange: _onPauseStateChange);
+ _consumer = new _SocketStreamConsumer(this);
+ _sink = new IOSink(_consumer);
+
+ // Disable read events until there is a subscription.
+ _raw.readEventsEnabled = false;
+
+ // Disable write events until the consumer needs it for pending writes.
+ _raw.writeEventsEnabled = false;
+ }
+
+ factory _Socket._writePipe([int fd]) {
+ return new _Socket(new _RawSocket._writePipe(fd));
+ }
+
+ factory _Socket._readPipe([int fd]) {
+ return new _Socket(new _RawSocket._readPipe(fd));
+ }
+
+ _NativeSocket get _nativeSocket => _raw._socket;
+
+ StreamSubscription<List<int>> listen(void onData(List<int> event),
+ {void onError(AsyncError error),
+ void onDone(),
+ bool unsubscribeOnError}) {
+ return _controller.stream.listen(
+ onData,
+ onError: onError,
+ onDone: onDone,
+ unsubscribeOnError: unsubscribeOnError);
+ }
+
+ Future<Socket> consume(Stream<List<int>> stream) {
+ return _sink.consume(stream);
+ }
+
+ Future<Socket> addStream(Stream<List<int>> stream) {
+ return _sink.addStream(stream);
+ }
+
+ void add(List<int> data) {
+ return _sink.add(data);
+ }
+
+ void addString(String string, [Encoding encoding = Encoding.UTF_8]) {
+ return _sink.addString(string, encoding);
+ }
+
+ close() => _sink.close();
+
+ Future<Socket> get done => _sink.done;
+
+ void destroy() {
+ // Destroy can always be called to get rid of a socket.
+ if (_raw == null) return;
+ _closeRawSocket();
+ _consumer.stop();
+ _controllerClosed = true;
+ _controller.close();
+ }
+
+ int get port => _raw.port;
+ String get remoteHost => _raw.remoteHost;
+ int get remotePort => _raw.remotePort;
+
+ // Ensure a subscription on the raw socket. Both the stream and the
+ // consumer needs a subscription as they share the error and done
+ // events from the raw socket.
+ void _ensureRawSocketSubscription() {
+ if (_subscription == null) {
+ _subscription = _raw.listen(_onData,
+ onError: _onError,
+ onDone: _onDone,
+ unsubscribeOnError: true);
+ }
+ }
+
+ _closeRawSocket() {
+ var tmp = _raw;
+ _raw = null;
+ _closed = true;
+ tmp.close();
+ }
+
+ void _onSubscriptionStateChange() {
+ if (_controller.hasSubscribers) {
+ _ensureRawSocketSubscription();
+ // Enable read events for providing data to subscription.
+ if (_raw != null) {
+ _raw.readEventsEnabled = true;
+ }
+ } else {
+ _controllerClosed = true;
+ if (_raw != null) {
+ _raw.shutdown(SocketDirection.RECEIVE);
+ }
+ }
+ }
+
+ void _onPauseStateChange() {
+ if (_raw != null) {
+ _raw.readEventsEnabled = !_controller.isPaused;
+ }
+ }
+
+ void _onData(event) {
+ switch (event) {
+ case RawSocketEvent.READ:
+ var buffer = _raw.read();
+ if (buffer != null) _controller.add(buffer);
+ break;
+ case RawSocketEvent.WRITE:
+ _consumer.write();
+ break;
+ case RawSocketEvent.READ_CLOSED:
+ _controllerClosed = true;
+ _controller.close();
+ break;
+ }
+ }
+
+ void _onDone() {
+ if (!_controllerClosed) {
+ _controllerClosed = true;
+ _controller.close();
+ }
+ _done();
+ }
+
+ void _onError(error) {
+ if (!_controllerClosed) {
+ _controllerClosed = true;
+ _controller.signalError(error);
+ _controller.close();
+ }
+ _done(error);
+ }
+
+ get _doneFuture {
+ if (_doneCompleter == null) {
+ _ensureRawSocketSubscription();
+ _doneCompleter = new Completer();
+ }
+ return _doneCompleter.future;
+ }
+
+ void _done([error]) {
+ if (_doneCompleter != null) {
+ var tmp = _doneCompleter;
+ _doneCompleter = null;
+ if (error != null) {
+ tmp.completeError(error);
+ } else {
+ tmp.complete(this);
+ }
+ }
+ }
+
+ int _write(List<int> data, int offset, int length) =>
+ _raw.write(data, offset, length);
+
+ void _enableWriteEvent() {
+ _raw.writeEventsEnabled = true;
+ }
+
+ void _disableWriteEvent() {
+ if (_raw != null) {
+ _raw.writeEventsEnabled = false;
+ }
+ }
+
+ void _consumerDone([error]) {
+ if (_raw != null) {
+ _raw.shutdown(SocketDirection.SEND);
+ _disableWriteEvent();
+ }
+ _done(error);
+ }
+}
+
+
+class _SecureSocket extends _Socket implements SecureSocket {
+ _SecureSocket(RawSecureSocket raw) : super(raw);
+
+ void set onBadCertificate(bool callback(X509Certificate certificate)) {
+ if (_raw == null) {
+ throw new StateError("onBadCertificate called on destroyed SecureSocket");
+ }
+ _raw.onBadCertificate = callback;
+ }
+
+ X509Certificate get peerCertificate {
+ if (_raw == null) {
+ throw new StateError("peerCertificate called on destroyed SecureSocket");
+ }
+ return _raw.peerCertificate;
+ }
}
diff --git a/runtime/bin/socket_win.cc b/runtime/bin/socket_win.cc
index a33d703..a60f1e4 100644
--- a/runtime/bin/socket_win.cc
+++ b/runtime/bin/socket_win.cc
@@ -2,6 +2,9 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_WINDOWS)
+
#include "bin/builtin.h"
#include "bin/eventhandler.h"
#include "bin/file.h"
@@ -9,11 +12,15 @@
#include "bin/socket.h"
bool Socket::Initialize() {
+ static bool socket_initialized = false;
+ if (socket_initialized) return true;
int err;
WSADATA winsock_data;
- WORD version_requested = MAKEWORD(1, 0);
+ WORD version_requested = MAKEWORD(2, 2);
err = WSAStartup(version_requested, &winsock_data);
- if (err != 0) {
+ if (err == 0) {
+ socket_initialized = true;
+ } else {
Log::PrintErr("Unable to initialize Winsock: %d\n", WSAGetLastError());
}
return err == 0;
@@ -187,6 +194,7 @@
const char* Socket::LookupIPv4Address(char* host, OSError** os_error) {
// Perform a name lookup for an IPv4 address.
+ Initialize();
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
@@ -262,7 +270,7 @@
return -1;
}
- status = listen(s, backlog);
+ status = listen(s, backlog > 0 ? backlog : SOMAXCONN);
if (status == SOCKET_ERROR) {
DWORD rc = WSAGetLastError();
closesocket(s);
@@ -277,5 +285,7 @@
void Socket::Close(intptr_t fd) {
ClientSocket* client_socket = reinterpret_cast<ClientSocket*>(fd);
- client_socket->close();
+ client_socket->Close();
}
+
+#endif // defined(TARGET_OS_WINDOWS)
diff --git a/runtime/bin/stdio_patch.dart b/runtime/bin/stdio_patch.dart
index 9ec2d52..38c488d 100644
--- a/runtime/bin/stdio_patch.dart
+++ b/runtime/bin/stdio_patch.dart
@@ -3,45 +3,39 @@
// BSD-style license that can be found in the LICENSE file.
patch class _StdIOUtils {
- static InputStream _getStdioInputStream() {
+ static Stream<List<int>> _getStdioInputStream() {
switch (_getStdioHandleType(0)) {
case _STDIO_HANDLE_TYPE_TERMINAL:
case _STDIO_HANDLE_TYPE_PIPE:
case _STDIO_HANDLE_TYPE_SOCKET:
- Socket s = new _Socket._internalReadOnly();
- _getStdioHandle(s, 0);
- s._closed = false;
- return s.inputStream;
+ return new _Socket._readPipe(0);
case _STDIO_HANDLE_TYPE_FILE:
- return new _FileInputStream.fromStdio(0);
+ return new _FileStream.forStdin();
default:
throw new FileIOException("Unsupported stdin type");
}
}
- static OutputStream _getStdioOutputStream(int fd) {
+ static IOSink _getStdioOutputStream(int fd) {
assert(fd == 1 || fd == 2);
switch (_getStdioHandleType(fd)) {
case _STDIO_HANDLE_TYPE_TERMINAL:
case _STDIO_HANDLE_TYPE_PIPE:
case _STDIO_HANDLE_TYPE_SOCKET:
- Socket s = new _Socket._internalWriteOnly();
- _getStdioHandle(s, fd);
- s._closed = false;
- return s.outputStream;
+ return new _Socket._writePipe(fd);
case _STDIO_HANDLE_TYPE_FILE:
- return new _FileOutputStream.fromStdio(fd);
+ return new IOSink(new _FileStreamConsumer.fromStdio(fd));
default:
throw new FileIOException("Unsupported stdin type");
}
}
- static int _socketType(Socket socket) {
- return _getSocketType(socket);
+ static int _socketType(nativeSocket) {
+ return _getSocketType(nativeSocket);
}
}
-_getStdioHandle(Socket socket, int num) native "Socket_GetStdioHandle";
+_getStdioHandle(_NativeSocket socket, int num) native "Socket_GetStdioHandle";
_getStdioHandleType(int num) native "File_GetStdioHandleType";
-_getSocketType(Socket socket) native "Socket_GetType";
+_getSocketType(_NativeSocket nativeSocket) native "Socket_GetType";
diff --git a/runtime/bin/test_extension_dllmain_win.cc b/runtime/bin/test_extension_dllmain_win.cc
index fdbd17e..ba2199a 100644
--- a/runtime/bin/test_extension_dllmain_win.cc
+++ b/runtime/bin/test_extension_dllmain_win.cc
@@ -2,11 +2,16 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_WINDOWS)
+
#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
+#include <windows.h> // NOLINT
BOOL APIENTRY DllMain(HMODULE module,
DWORD reason,
LPVOID reserved) {
return true;
}
+
+#endif // defined(TARGET_OS_WINDOWS)
diff --git a/runtime/bin/utf_sources.gypi b/runtime/bin/utf_sources.gypi
deleted file mode 100644
index a8fc95b..0000000
--- a/runtime/bin/utf_sources.gypi
+++ /dev/null
@@ -1,23 +0,0 @@
-# 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.
-
-# This file contains all sources for the dart:utf library.
-#
-# TODO(ager): ../lib/utf/utf_vm.dart should be removed when the
-# VM can use the #source directive for libraries. At that point
-# ../../sdk/lib/utf/utf.dart should be the only utf library file.
-{
- 'sources': [
- # The utf_vm.dart file needs to be the first source file. It contains
- # the library and import directives for the dart:utf library. The
- # dart:utf library is created by concatenating the files listed here
- # in the order they are listed.
- '../lib/utf/utf_vm.dart',
-
- '../../sdk/lib/utf/utf_core.dart',
- '../../sdk/lib/utf/utf8.dart',
- '../../sdk/lib/utf/utf16.dart',
- '../../sdk/lib/utf/utf32.dart',
- ],
-}
diff --git a/runtime/bin/utils.h b/runtime/bin/utils.h
index ba236eb..470f26a 100644
--- a/runtime/bin/utils.h
+++ b/runtime/bin/utils.h
@@ -78,4 +78,10 @@
static void FreeUnicodeArgv(wchar_t** argv);
};
+class TimerUtils {
+ public:
+ static int64_t GetCurrentTimeMicros();
+ static int64_t GetCurrentTimeMilliseconds();
+};
+
#endif // BIN_UTILS_H_
diff --git a/runtime/bin/utils_android.cc b/runtime/bin/utils_android.cc
index 81b96179..89becdc 100644
--- a/runtime/bin/utils_android.cc
+++ b/runtime/bin/utils_android.cc
@@ -2,8 +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.
-#include <errno.h>
-#include <netdb.h>
+#include "platform/globals.h"
+#if defined(TARGET_OS_ANDROID)
+
+#include <errno.h> // NOLINT
+#include <netdb.h> // NOLINT
+#include <sys/time.h> // NOLINT
#include "bin/utils.h"
#include "platform/assert.h"
@@ -69,3 +73,18 @@
void ShellUtils::FreeUnicodeArgv(wchar_t** argv) {
}
+
+int64_t TimerUtils::GetCurrentTimeMilliseconds() {
+ return GetCurrentTimeMicros() / 1000;
+}
+
+int64_t TimerUtils::GetCurrentTimeMicros() {
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL) < 0) {
+ UNREACHABLE();
+ return 0;
+ }
+ return (static_cast<int64_t>(tv.tv_sec) * 1000000) + tv.tv_usec;
+}
+
+#endif // defined(TARGET_OS_ANDROID)
diff --git a/runtime/bin/utils_linux.cc b/runtime/bin/utils_linux.cc
index 81b96179..39dcce6 100644
--- a/runtime/bin/utils_linux.cc
+++ b/runtime/bin/utils_linux.cc
@@ -2,8 +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.
-#include <errno.h>
-#include <netdb.h>
+#include "platform/globals.h"
+#if defined(TARGET_OS_LINUX)
+
+#include <errno.h> // NOLINT
+#include <netdb.h> // NOLINT
+#include <sys/time.h> // NOLINT
#include "bin/utils.h"
#include "platform/assert.h"
@@ -69,3 +73,18 @@
void ShellUtils::FreeUnicodeArgv(wchar_t** argv) {
}
+
+int64_t TimerUtils::GetCurrentTimeMilliseconds() {
+ return GetCurrentTimeMicros() / 1000;
+}
+
+int64_t TimerUtils::GetCurrentTimeMicros() {
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL) < 0) {
+ UNREACHABLE();
+ return 0;
+ }
+ return (static_cast<int64_t>(tv.tv_sec) * 1000000) + tv.tv_usec;
+}
+
+#endif // defined(TARGET_OS_LINUX)
diff --git a/runtime/bin/utils_macos.cc b/runtime/bin/utils_macos.cc
index 81b96179..1c79c0d 100644
--- a/runtime/bin/utils_macos.cc
+++ b/runtime/bin/utils_macos.cc
@@ -2,8 +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.
-#include <errno.h>
-#include <netdb.h>
+#include "platform/globals.h"
+#if defined(TARGET_OS_MACOS)
+
+#include <errno.h> // NOLINT
+#include <netdb.h> // NOLINT
+#include <sys/time.h> // NOLINT
#include "bin/utils.h"
#include "platform/assert.h"
@@ -69,3 +73,18 @@
void ShellUtils::FreeUnicodeArgv(wchar_t** argv) {
}
+
+int64_t TimerUtils::GetCurrentTimeMilliseconds() {
+ return GetCurrentTimeMicros() / 1000;
+}
+
+int64_t TimerUtils::GetCurrentTimeMicros() {
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL) < 0) {
+ UNREACHABLE();
+ return 0;
+ }
+ return (static_cast<int64_t>(tv.tv_sec) * 1000000) + tv.tv_usec;
+}
+
+#endif // defined(TARGET_OS_MACOS)
diff --git a/runtime/bin/utils_win.cc b/runtime/bin/utils_win.cc
index 75cc496..2c41278 100644
--- a/runtime/bin/utils_win.cc
+++ b/runtime/bin/utils_win.cc
@@ -2,7 +2,11 @@
// 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 <errno.h>
+#include "platform/globals.h"
+#if defined(TARGET_OS_WINDOWS)
+
+#include <errno.h> // NOLINT
+#include <time.h> // NOLINT
#include "bin/utils.h"
#include "bin/log.h"
@@ -113,3 +117,28 @@
void ShellUtils::FreeUnicodeArgv(wchar_t** argv) {
LocalFree(argv);
}
+
+int64_t TimerUtils::GetCurrentTimeMilliseconds() {
+ return GetCurrentTimeMicros() / 1000;
+}
+
+int64_t TimerUtils::GetCurrentTimeMicros() {
+ static const int64_t kTimeEpoc = 116444736000000000LL;
+ static const int64_t kTimeScaler = 10; // 100 ns to us.
+
+ // Although win32 uses 64-bit integers for representing timestamps,
+ // these are packed into a FILETIME structure. The FILETIME
+ // structure is just a struct representing a 64-bit integer. The
+ // TimeStamp union allows access to both a FILETIME and an integer
+ // representation of the timestamp. The Windows timestamp is in
+ // 100-nanosecond intervals since January 1, 1601.
+ union TimeStamp {
+ FILETIME ft_;
+ int64_t t_;
+ };
+ TimeStamp time;
+ GetSystemTimeAsFileTime(&time.ft_);
+ return (time.t_ - kTimeEpoc) / kTimeScaler;
+}
+
+#endif // defined(TARGET_OS_WINDOWS)
diff --git a/runtime/dart-runtime.gyp b/runtime/dart-runtime.gyp
index 45f4967..db2f10f 100644
--- a/runtime/dart-runtime.gyp
+++ b/runtime/dart-runtime.gyp
@@ -9,7 +9,6 @@
'bin/bin.gypi',
'third_party/double-conversion/src/double-conversion.gypi',
'third_party/jscre/jscre.gypi',
- '../tools/gyp/source_filter.gypi',
],
'variables': {
'version_in_cc_file': 'vm/version_in.cc',
diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h
index 2e53b34..96ca096 100755
--- a/runtime/include/dart_api.h
+++ b/runtime/include/dart_api.h
@@ -1631,10 +1631,10 @@
uint8_t* native_array,
intptr_t length);
-// --- Scalar Lists ---
+// --- Typed Data ---
typedef enum {
- kByteArray = 0,
+ kByteData = 0,
kInt8,
kUint8,
kUint8Clamped,
@@ -1645,92 +1645,73 @@
kInt64,
kUint64,
kFloat32,
- kFloat64
-} Dart_Scalar_Type;
+ kFloat64,
+ kInvalid
+} Dart_TypedData_Type;
/**
- * Is this object a ByteArray?
+ * Return type if this object is a TypedData object.
+ *
+ * \return kInvalid if the object is not a TypedData object or the appropriate
+ * Dart_TypedData_Type.
*/
-DART_EXPORT bool Dart_IsByteArray(Dart_Handle object);
+DART_EXPORT Dart_TypedData_Type Dart_GetTypeOfTypedData(Dart_Handle object);
/**
- * Is this object an external ByteArray?
+ * Return type if this object is an external TypedData object.
*
- * An external ByteArray is a ByteArray which references a fixed array of
- * bytes which is external to the Dart heap.
+ * \return kInvalid if the object is not an external TypedData object or
+ * the appropriate Dart_TypedData_Type.
*/
-DART_EXPORT bool Dart_IsByteArrayExternal(Dart_Handle object);
+DART_EXPORT Dart_TypedData_Type Dart_GetTypeOfExternalTypedData(
+ Dart_Handle object);
/**
- * Returns a ByteArray of the desired length.
+ * Returns a TypedData object of the desired length and type.
*
- * \param length The length of the array.
+ * \param type The type of the TypedData object.
+ * \param length The length of the TypedData object (length in type units).
*
- * \return The ByteArray object if no error occurs. Otherwise returns
+ * \return The TypedData object if no error occurs. Otherwise returns
* an error handle.
*/
-DART_EXPORT Dart_Handle Dart_NewByteArray(intptr_t length);
+DART_EXPORT Dart_Handle Dart_NewTypedData(Dart_TypedData_Type type,
+ intptr_t length);
/**
- * Returns a ByteArray which references an external array of 8-bit bytes.
+ * Returns a TypedData object which references an external data array.
*
- * \param value An array of 8-bit bytes. This array must not move.
- * \param length The length of the array.
- * \param peer An external pointer to associate with this byte array.
+ * \param type The type of the data array.
+ * \param value A data array. This array must not move.
+ * \param length The length of the data array (length in type units).
+ * \param peer An external pointer to associate with this array.
*
- * \return The ByteArray object if no error occurs. Otherwise returns
- * an error handle. The ByteArray object is returned in a
+ * \return The TypedData object if no error occurs. Otherwise returns
+ * an error handle. The TypedData object is returned in a
* WeakPersistentHandle which needs to be deleted in the specified callback
* using Dart_DeletePersistentHandle.
*/
-DART_EXPORT Dart_Handle Dart_NewExternalByteArray(
- uint8_t* data,
+DART_EXPORT Dart_Handle Dart_NewExternalTypedData(
+ Dart_TypedData_Type type,
+ void* data,
intptr_t length,
void* peer,
Dart_WeakPersistentHandleFinalizer callback);
/**
- * Returns a clamped ByteArray which references an external array of
- * 8-bit bytes.
- * A clamped ByteArray differs from ByteArray above in the indexed store
- * operation where negative values are clamped to 0 and values above 255 are
- * clamped to 255.
- *
- * \param value An array of 8-bit bytes. This array must not move.
- * \param length The length of the array.
- * \param peer An external pointer to associate with this byte array.
- *
- * \return The clamped ByteArray object if no error occurs. Otherwise returns
- * an error handle. The clamped ByteArray object is returned in a
- * WeakPersistentHandle which needs to be deleted in the specified callback
- * using Dart_DeletePersistentHandle.
+ * Retrieves the peer pointer associated with an external TypedData object.
*/
-DART_EXPORT Dart_Handle Dart_NewExternalClampedByteArray(
- uint8_t* data,
- intptr_t length,
- void* peer,
- Dart_WeakPersistentHandleFinalizer callback);
-
-/**
- * Retrieves the data pointer associated with an external ByteArray.
- */
-DART_EXPORT Dart_Handle Dart_ExternalByteArrayGetData(Dart_Handle object,
- void** data);
-
-/**
- * Retrieves the peer pointer associated with an external ByteArray.
- */
-DART_EXPORT Dart_Handle Dart_ExternalByteArrayGetPeer(Dart_Handle object,
+DART_EXPORT Dart_Handle Dart_ExternalTypedDataGetPeer(Dart_Handle object,
void** peer);
/**
- * Acquires access to the internal data address of a scalar list object.
+ * Acquires access to the internal data address of a TypedData object.
*
- * \param array The scalar list object whose internal data address is to
+ * \param object The typed data object whose internal data address is to
* be accessed.
- * \param type The scalar type of the object is returned here.
+ * \param type The type of the object is returned here.
* \param data The internal data address is returned here.
- * \param len Size of the byte array is returned here.
+ * \param len Size of the typed array is returned here.
*
* Note: When the internal address of the object is acquired any calls to a
* Dart API function that could potentially allocate an object or run
@@ -1739,22 +1720,22 @@
* \return Success if the internal data address is acquired successfully.
* Otherwise, returns an error handle.
*/
-DART_EXPORT Dart_Handle Dart_ScalarListAcquireData(Dart_Handle array,
- Dart_Scalar_Type* type,
- void** data,
- intptr_t* len);
+DART_EXPORT Dart_Handle Dart_TypedDataAcquireData(Dart_Handle object,
+ Dart_TypedData_Type* type,
+ void** data,
+ intptr_t* len);
/**
* Releases access to the internal data address that was acquired earlier using
- * Dart_ByteArrayAcquireData.
+ * Dart_TypedDataAcquireData.
*
- * \param array The scalar list object whose internal data address is to be
+ * \param object The typed data object whose internal data address is to be
* released.
*
* \return Success if the internal data address is released successfully.
* Otherwise, returns an error handle.
*/
-DART_EXPORT Dart_Handle Dart_ScalarListReleaseData(Dart_Handle array);
+DART_EXPORT Dart_Handle Dart_TypedDataReleaseData(Dart_Handle array);
// --- Closures ---
diff --git a/runtime/lib/async_sources.gypi b/runtime/lib/async_sources.gypi
index d1c0711..a64243d 100644
--- a/runtime/lib/async_sources.gypi
+++ b/runtime/lib/async_sources.gypi
@@ -6,5 +6,6 @@
{
'sources': [
'timer_patch.dart',
+ 'deferred_load_patch.dart',
],
}
diff --git a/runtime/lib/deferred_load_patch.dart b/runtime/lib/deferred_load_patch.dart
new file mode 100644
index 0000000..8d0a9ff
--- /dev/null
+++ b/runtime/lib/deferred_load_patch.dart
@@ -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.
+
+final Set<String> _loadedLibraries = new Set<String>();
+
+patch class DeferredLibrary {
+ /* patch */ Future<bool> load() {
+ // Dummy implementation that should eventually be replaced by real
+ // implementation.
+ Future future =
+ new Future<bool>.immediate(!_loadedLibraries.contains(libraryName));
+ _loadedLibraries.add(libraryName);
+ return future;
+ }
+}
diff --git a/runtime/lib/errors_patch.dart b/runtime/lib/errors_patch.dart
index 5fd3e50..783a17d 100644
--- a/runtime/lib/errors_patch.dart
+++ b/runtime/lib/errors_patch.dart
@@ -14,6 +14,7 @@
// unresolved method.
static void _throwNew(Object receiver,
String memberName,
+ int invocation_type,
List arguments,
List argumentNames,
List existingArgumentNames) {
@@ -31,10 +32,121 @@
var arg_value = arguments[numPositionalArguments + i];
namedArguments[argumentNames[i]] = arg_value;
}
- throw new NoSuchMethodError(receiver,
+ throw new NoSuchMethodError._withType(receiver,
memberName,
+ invocation_type,
positionalArguments,
namedArguments,
existingArgumentNames);
}
+
+ // Remember the type from the invocation mirror or static compilation
+ // analysis when thrown directly with _throwNew. A negative value means
+ // that no information is available.
+ final int _invocation_type;
+
+ const NoSuchMethodError(Object this._receiver,
+ String this._memberName,
+ List this._arguments,
+ Map<String,dynamic> this._namedArguments,
+ [List existingArgumentNames = null])
+ : this._existingArgumentNames = existingArgumentNames,
+ this._invocation_type = -1;
+
+ const NoSuchMethodError._withType(Object this._receiver,
+ String this._memberName,
+ this._invocation_type,
+ List this._arguments,
+ Map<String,dynamic> this._namedArguments,
+ [List existingArgumentNames = null])
+ : this._existingArgumentNames = existingArgumentNames;
+
+
+ String _developerMessage(args_mismatch) {
+ if (_invocation_type < 0) {
+ return "";
+ }
+ var type = _invocation_type & _InvocationMirror._TYPE_MASK;
+ var level = (_invocation_type >> _InvocationMirror._CALL_SHIFT) &
+ _InvocationMirror._CALL_MASK;
+ var type_str =
+ (const ["method", "getter", "setter", "getter or setter"])[type];
+ var args_message = args_mismatch ? " with matching arguments" : "";
+ var msg;
+ switch (level) {
+ case _InvocationMirror._DYNAMIC: {
+ if (_receiver == null) {
+ msg = "The null object does not have a $type_str '$_memberName'"
+ "$args_message.";
+ } else {
+ msg = "Class '${_receiver.runtimeType}' has no instance $type_str "
+ "'$_memberName'$args_message.";
+ }
+ break;
+ }
+ case _InvocationMirror._STATIC: {
+ msg = "No static $type_str '$_memberName' declared in class "
+ "'$_receiver'.";
+ break;
+ }
+ case _InvocationMirror._CONSTRUCTOR: {
+ msg = "No constructor '$_memberName' declared in class '$_receiver'.";
+ break;
+ }
+ case _InvocationMirror._TOP_LEVEL: {
+ msg = "No top-level $type_str '$_memberName' declared.";
+ break;
+ }
+ }
+ return "$msg\n\n";
+ }
+
+ /* patch */ String toString() {
+ StringBuffer actual_buf = new StringBuffer();
+ int i = 0;
+ if (_arguments != null) {
+ for (; i < _arguments.length; i++) {
+ if (i > 0) {
+ actual_buf.add(", ");
+ }
+ actual_buf.add(Error.safeToString(_arguments[i]));
+ }
+ }
+ if (_namedArguments != null) {
+ _namedArguments.forEach((String key, var value) {
+ if (i > 0) {
+ actual_buf.add(", ");
+ }
+ actual_buf.add(key);
+ actual_buf.add(": ");
+ actual_buf.add(Error.safeToString(value));
+ i++;
+ });
+ }
+ var args_mismatch = _existingArgumentNames != null;
+ StringBuffer msg_buf = new StringBuffer(_developerMessage(args_mismatch));
+ if (!args_mismatch) {
+ msg_buf.add(
+ "NoSuchMethodError : method not found: '$_memberName'\n"
+ "Receiver: ${Error.safeToString(_receiver)}\n"
+ "Arguments: [$actual_buf]");
+ } else {
+ String actualParameters = actual_buf.toString();
+ StringBuffer formal_buf = new StringBuffer();
+ for (int i = 0; i < _existingArgumentNames.length; i++) {
+ if (i > 0) {
+ formal_buf.add(", ");
+ }
+ formal_buf.add(_existingArgumentNames[i]);
+ }
+ String formalParameters = formal_buf.toString();
+ msg_buf.add(
+ "NoSuchMethodError: incorrect number of arguments passed to "
+ "method named '$_memberName'\n"
+ "Receiver: ${Error.safeToString(_receiver)}\n"
+ "Tried calling: $_memberName($actualParameters)\n"
+ "Found: $_memberName($formalParameters)");
+ }
+ return msg_buf.toString();
+ }
}
diff --git a/runtime/lib/invocation_mirror.h b/runtime/lib/invocation_mirror.h
new file mode 100644
index 0000000..af0d24c
--- /dev/null
+++ b/runtime/lib/invocation_mirror.h
@@ -0,0 +1,48 @@
+// 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.
+
+#ifndef LIB_INVOCATION_MIRROR_H_
+#define LIB_INVOCATION_MIRROR_H_
+
+#include "vm/allocation.h"
+
+namespace dart {
+
+class InvocationMirror : public AllStatic {
+ public:
+ // These enum correspond to the constants in invocation_mirror_patch.dart.
+ // It is used to communicate the reason for statically thrown
+ // NoSuchMethodErrors by the compiler.
+ enum Type {
+ // Constants describing the invocation type.
+ // kField cannot be generated by regular invocation mirrors.
+ kMethod = 0,
+ kGetter = 1,
+ kSetter = 2,
+ kField = 3,
+ kTypeShift = 0,
+ kTypeBits = 2,
+ kTypeMask = (1 << kTypeBits) - 1
+ };
+
+ enum Call {
+ // These values, except kDynamic, are only used when throwing
+ // NoSuchMethodError for compile-time resolution failures.
+ kDynamic = 0,
+ kStatic = 1,
+ kConstructor = 2,
+ kTopLevel = 3,
+ kCallShift = kTypeBits,
+ kCallBits = 2,
+ kCallMask = (1 << kCallBits) - 1
+ };
+
+ static int EncodeType(Call call, Type type) {
+ return (call << kCallShift) | type;
+ }
+};
+
+} // namespace dart
+
+#endif // LIB_INVOCATION_MIRROR_H_
diff --git a/runtime/lib/invocation_mirror_patch.dart b/runtime/lib/invocation_mirror_patch.dart
index 840b6f0..3f53feb 100644
--- a/runtime/lib/invocation_mirror_patch.dart
+++ b/runtime/lib/invocation_mirror_patch.dart
@@ -4,9 +4,24 @@
class _InvocationMirror implements InvocationMirror {
// Constants describing the invocation type.
- static final int _METHOD = 0;
- static final int _GETTER = 1;
- static final int _SETTER = 2;
+ // _FIELD cannot be generated by regular invocation mirrors.
+ static const int _METHOD = 0;
+ static const int _GETTER = 1;
+ static const int _SETTER = 2;
+ static const int _FIELD = 3;
+ static const int _TYPE_SHIFT = 0;
+ static const int _TYPE_BITS = 2;
+ static const int _TYPE_MASK = (1 << _TYPE_BITS) - 1;
+
+ // These values, except _DYNAMIC, are only used when throwing
+ // NoSuchMethodError for compile-time resolution failures.
+ static const int _DYNAMIC = 0;
+ static const int _STATIC = 1;
+ static const int _CONSTRUCTOR = 2;
+ static const int _TOP_LEVEL = 3;
+ static const int _CALL_SHIFT = _TYPE_BITS;
+ static const int _CALL_BITS = 2;
+ static const int _CALL_MASK = (1 << _CALL_BITS) - 1;
// Internal representation of the invocation mirror.
final String _functionName;
diff --git a/runtime/lib/lib_sources.gypi b/runtime/lib/lib_sources.gypi
index 18b74fe..f5b6c04 100644
--- a/runtime/lib/lib_sources.gypi
+++ b/runtime/lib/lib_sources.gypi
@@ -31,6 +31,7 @@
'integers.dart',
'integers_patch.dart',
'invocation_mirror.cc',
+ 'invocation_mirror.h',
'invocation_mirror_patch.dart',
'map_patch.dart',
'object.cc',
diff --git a/runtime/lib/object.cc b/runtime/lib/object.cc
index fc50c04..48dacdf 100644
--- a/runtime/lib/object.cc
+++ b/runtime/lib/object.cc
@@ -19,18 +19,20 @@
}
-DEFINE_NATIVE_ENTRY(Object_noSuchMethod, 5) {
+DEFINE_NATIVE_ENTRY(Object_noSuchMethod, 6) {
const Instance& instance = Instance::CheckedHandle(arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(Bool, is_method, arguments->NativeArgAt(1));
GET_NON_NULL_NATIVE_ARGUMENT(String, member_name, arguments->NativeArgAt(2));
- GET_NON_NULL_NATIVE_ARGUMENT(Instance, func_args, arguments->NativeArgAt(3));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, invocation_type, arguments->NativeArgAt(3));
+ GET_NON_NULL_NATIVE_ARGUMENT(Instance, func_args, arguments->NativeArgAt(4));
GET_NON_NULL_NATIVE_ARGUMENT(
- Instance, func_named_args, arguments->NativeArgAt(4));
- const Array& dart_arguments = Array::Handle(Array::New(5));
+ Instance, func_named_args, arguments->NativeArgAt(5));
+ const Array& dart_arguments = Array::Handle(Array::New(6));
dart_arguments.SetAt(0, instance);
dart_arguments.SetAt(1, member_name);
- dart_arguments.SetAt(2, func_args);
- dart_arguments.SetAt(3, func_named_args);
+ dart_arguments.SetAt(2, invocation_type);
+ dart_arguments.SetAt(3, func_args);
+ dart_arguments.SetAt(4, func_named_args);
if (is_method.value()) {
// Report if a function with same name (but different arguments) has been
@@ -50,7 +52,7 @@
for (int i = 1; i < total_num_parameters; i++) {
array.SetAt(i - 1, String::Handle(function.ParameterNameAt(i)));
}
- dart_arguments.SetAt(4, array);
+ dart_arguments.SetAt(5, array);
}
}
Exceptions::ThrowByType(Exceptions::kNoSuchMethod, dart_arguments);
diff --git a/runtime/lib/object_patch.dart b/runtime/lib/object_patch.dart
index a1ebe00..2c0c0a3 100644
--- a/runtime/lib/object_patch.dart
+++ b/runtime/lib/object_patch.dart
@@ -28,6 +28,7 @@
_noSuchMethod(bool isMethod,
String memberName,
+ int type,
List arguments,
Map<String, dynamic> namedArguments)
native "Object_noSuchMethod";
@@ -35,6 +36,7 @@
/* patch */ noSuchMethod(InvocationMirror invocation) {
return _noSuchMethod(invocation.isMethod,
invocation.memberName,
+ invocation._type,
invocation.positionalArguments,
invocation.namedArguments);
}
diff --git a/runtime/lib/utf/utf_vm.dart b/runtime/lib/utf/utf_vm.dart
deleted file mode 100644
index a3d8151..0000000
--- a/runtime/lib/utf/utf_vm.dart
+++ /dev/null
@@ -1,8 +0,0 @@
-// 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.
-
-// TODO(dgrove) - once the VM has a new way to include whole libraries,
-// remove this file and update runtime/bin/utf_sources.gypi .
-
-library utf;
diff --git a/runtime/platform/floating_point_win.cc b/runtime/platform/floating_point_win.cc
index 82aee16..27d6c25 100644
--- a/runtime/platform/floating_point_win.cc
+++ b/runtime/platform/floating_point_win.cc
@@ -2,10 +2,11 @@
// 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 <math.h>
-#include <limits>
-
#include "platform/globals.h"
+#if defined(TARGET_OS_WINDOWS)
+
+#include <math.h> // NOLINT
+#include <limits> // NOLINT
// Taken from third_party/v8/src/platform-win32.cc
double fmod_ieee(double x, double y) {
@@ -44,3 +45,5 @@
return atan2(x, y);
}
}
+
+#endif // defined(TARGET_OS_WINDOWS)
diff --git a/runtime/platform/globals.h b/runtime/platform/globals.h
index 176246e..4c5d053 100644
--- a/runtime/platform/globals.h
+++ b/runtime/platform/globals.h
@@ -61,7 +61,7 @@
// - http://msdn.microsoft.com/en-us/library/b0084kay.aspx
// - with gcc, run: "echo | gcc -E -dM -"
#if defined(__ANDROID__)
-#define TARGET_OS_ANDROID
+#define TARGET_OS_ANDROID 1
#elif defined(__linux__) || defined(__FreeBSD__)
#define TARGET_OS_LINUX 1
#elif defined(__APPLE__)
@@ -391,22 +391,28 @@
#define strtok_r strtok_s
#endif
-#if !defined(TARGET_OS_WINDOWS) && !defined(TEMP_FAILURE_RETRY)
+#if !defined(TARGET_OS_WINDOWS)
+#if !defined(TEMP_FAILURE_RETRY)
// TEMP_FAILURE_RETRY is defined in unistd.h on some platforms. The
// definition below is copied from Linux and adapted to avoid lint
// errors (type long int changed to int64_t and do/while split on
// separate lines with body in {}s).
-# define TEMP_FAILURE_RETRY(expression) \
+#define TEMP_FAILURE_RETRY(expression) \
({ int64_t __result; \
do { \
- __result = (int64_t) (expression); \
+ __result = static_cast<int64_t>(expression); \
} while (__result == -1L && errno == EINTR); \
__result; })
-#endif
+#endif // !defined(TEMP_FAILURE_RETRY)
+// This is a version of TEMP_FAILURE_RETRY which does not use the value
+// returned from the expression.
+#define VOID_TEMP_FAILURE_RETRY(expression) \
+ (static_cast<void>(TEMP_FAILURE_RETRY(expression)))
+
+#endif // !defined(TARGET_OS_WINDOWS)
#if defined(TARGET_OS_LINUX) || defined(TARGET_OS_MACOS)
-//
// Tell the compiler to do printf format string checking if the
// compiler supports it; see the 'format' attribute in
// <http://gcc.gnu.org/onlinedocs/gcc-4.3.0/gcc/Function-Attributes.html>.
@@ -414,7 +420,6 @@
// N.B.: As the GCC manual states, "[s]ince non-static C++ methods
// have an implicit 'this' argument, the arguments of such methods
// should be counted from two, not one."
-//
#define PRINTF_ATTRIBUTE(string_index, first_to_check) \
__attribute__((__format__(__printf__, string_index, first_to_check)))
#else
diff --git a/runtime/platform/thread_android.cc b/runtime/platform/thread_android.cc
index da00de1..e789c01 100644
--- a/runtime/platform/thread_android.cc
+++ b/runtime/platform/thread_android.cc
@@ -2,10 +2,13 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_ANDROID)
+
#include "platform/thread.h"
-#include <errno.h>
-#include <sys/time.h>
+#include <errno.h> // NOLINT
+#include <sys/time.h> // NOLINT
#include "platform/assert.h"
@@ -277,3 +280,5 @@
}
} // namespace dart
+
+#endif // defined(TARGET_OS_ANDROID)
diff --git a/runtime/platform/thread_linux.cc b/runtime/platform/thread_linux.cc
index 8ac046a..dd02a38 100644
--- a/runtime/platform/thread_linux.cc
+++ b/runtime/platform/thread_linux.cc
@@ -2,10 +2,13 @@
// 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 "platform/globals.h"
+#if defined(TARGET_OS_LINUX)
+
#include "platform/thread.h"
-#include <errno.h>
-#include <sys/time.h>
+#include <errno.h> // NOLINT
+#include <sys/time.h> // NOLINT
#include "platform/assert.h"
@@ -279,3 +282,5 @@
}
} // namespace dart
+
+#endif // defined(TARGET_OS_LINUX)
diff --git a/runtime/platform/thread_macos.cc b/runtime/platform/thread_macos.cc
index 71f025d..29059fe 100644
--- a/runtime/platform/thread_macos.cc
+++ b/runtime/platform/thread_macos.cc
@@ -2,9 +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.
+#include "platform/globals.h"
+#if defined(TARGET_OS_MACOS)
+
#include "platform/thread.h"
-#include <sys/errno.h>
+#include <sys/errno.h> // NOLINT
#include "platform/assert.h"
@@ -259,3 +262,5 @@
}
} // namespace dart
+
+#endif // defined(TARGET_OS_MACOS)
diff --git a/runtime/platform/thread_win.cc b/runtime/platform/thread_win.cc
index 6fffe93..18ec6d0 100644
--- a/runtime/platform/thread_win.cc
+++ b/runtime/platform/thread_win.cc
@@ -2,9 +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.
+#include "platform/globals.h"
+#if defined(TARGET_OS_WINDOWS)
+
#include "platform/thread.h"
-#include <process.h>
+#include <process.h> // NOLINT
#include "platform/assert.h"
@@ -336,3 +339,5 @@
}
} // namespace dart
+
+#endif // defined(TARGET_OS_WINDOWS)
diff --git a/runtime/tests/vm/dart/byte_array_test.dart b/runtime/tests/vm/dart/byte_array_test.dart
index 8ebe03c..0eab243 100644
--- a/runtime/tests/vm/dart/byte_array_test.dart
+++ b/runtime/tests/vm/dart/byte_array_test.dart
@@ -179,6 +179,82 @@
testUint8ListImpl(array);
}
+ static testUint8ClampedListImpl(Uint8ClampedList array) {
+ Expect.isTrue(array is List<int>);
+ Expect.equals(10, array.length);
+ Expect.equals(1, array.bytesPerElement());
+ Expect.equals(10, array.lengthInBytes());
+ Expect.listEquals([0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0],
+ array);
+ Expect.throws(() { array[-1] = 0; },
+ (e) { return e is RangeError; });
+ Expect.throws(() { return array[-1]; },
+ (e) { return e is RangeError; });
+ Expect.throws(() { array[10]; },
+ (e) { return e is RangeError; });
+ Expect.throws(() { array[10] = 0; },
+ (e) { return e is RangeError; });
+ Expect.throws(() { array.add(0); },
+ (e) { return e is UnsupportedError; });
+ Expect.throws(() { array.addAll([0]); },
+ (e) { return e is UnsupportedError; });
+ Expect.throws(() { array.addLast(0); },
+ (e) { return e is UnsupportedError; });
+ Expect.throws(() { array.clear(); },
+ (e) { return e is UnsupportedError; });
+ Expect.throws(() { array.insertRange(0, array.length, 0); },
+ (e) { return e is UnsupportedError; });
+ Expect.throws(() { array.length = 0; },
+ (e) { return e is UnsupportedError; });
+ Expect.throws(() { array.removeLast(); },
+ (e) { return e is UnsupportedError; });
+ Expect.throws(() { array.removeRange(0, array.length - 1); },
+ (e) { return e is UnsupportedError; });
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = -1 + i;
+ }
+ Expect.listEquals([0, 0, 1, 2, 3, 4, 5, 6, 7, 8], array);
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = 0x100 + i;
+ }
+ Expect.listEquals([255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
+ array);
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = 0xFF - i;
+ }
+ Expect.listEquals([0xFF, 0xFE, 0xFD, 0xFC, 0xFB,
+ 0xFA, 0xF9, 0xF8, 0xF7, 0xF6],
+ array);
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = i;
+ }
+ var copy = array.getRange(0, array.length);
+ Expect.isFalse(copy === array);
+ Expect.isTrue(copy is Uint8ClampedList);
+ Expect.equals(10, copy.length);
+ Expect.listEquals(array, copy);
+ var empty = array.getRange(array.length, 0);
+ Expect.equals(0, empty.length);
+ var region = array.getRange(3, array.length - 6);
+ Expect.isTrue(copy is Uint8ClampedList);
+ Expect.equals(4, region.length);
+ Expect.listEquals([3, 4, 5, 6], region);
+ array.setRange(3, 4, [257, 0, 1, 255]);
+ Expect.listEquals([0, 1, 2, 255, 0, 1, 255, 7, 8, 9], array);
+ }
+
+ static testUint8ClampedList() {
+ Expect.throws(() { new Uint8ClampedList(-1); },
+ (e) { return e is ArgumentError; });
+ Expect.throws(() { new Uint8ClampedList.transferable(-1); },
+ (e) { return e is ArgumentError; });
+ var array = new Uint8ClampedList(10);
+ testUint8ClampedListImpl(array);
+ array = new Uint8ClampedList.transferable(10);
+ testUint8ClampedListImpl(array);
+ }
+
static testInt16ListImpl(Int16List array) {
Expect.isTrue(array is List<int>);
Expect.equals(10, array.length);
@@ -2382,6 +2458,7 @@
static testMain() {
testInt8List();
testUint8List();
+ testUint8ClampedList();
testInt16List();
testUint16List();
testInt32List();
diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status
index 5d7f315..c4537b6 100644
--- a/runtime/tests/vm/vm.status
+++ b/runtime/tests/vm/vm.status
@@ -58,7 +58,6 @@
[ $arch == simarm ]
# Tests needing an assembler.
-cc/Call: Skip
cc/CallLeafRuntimeStubCode: Skip
cc/CallRuntimeStubCode: Skip
cc/Dart2JSCompileAll: Skip
diff --git a/runtime/vm/assembler_arm.cc b/runtime/vm/assembler_arm.cc
index 183e164..2bd4737 100644
--- a/runtime/vm/assembler_arm.cc
+++ b/runtime/vm/assembler_arm.cc
@@ -7,9 +7,12 @@
#include "vm/assembler.h"
+#include "vm/stub_code.h"
+
namespace dart {
-DEFINE_FLAG(bool, print_stop_message, true, "Print stop message.");
+// TODO(regis): Enable this flag after PrintStopMessage stub is implemented.
+DEFINE_FLAG(bool, print_stop_message, false, "Print stop message.");
// Instruction encoding bits.
@@ -1036,11 +1039,11 @@
const int32_t offset =
Array::data_offset() + 4*AddObject(object) - kHeapObjectTag;
if (Address::CanHoldLoadOffset(kLoadWord, offset)) {
- ldr(rd, Address(CP, offset));
+ ldr(rd, Address(PP, offset));
} else {
int32_t offset12_hi = offset & ~kOffset12Mask; // signed
uint32_t offset12_lo = offset & kOffset12Mask; // unsigned
- AddConstant(rd, CP, offset12_hi);
+ AddConstant(rd, PP, offset12_hi);
ldr(rd, Address(rd, offset12_lo));
}
}
@@ -1167,28 +1170,34 @@
void Assembler::BranchLink(const ExternalLabel* label) {
- // TODO(regis): Make sure that CodePatcher is able to patch the label referred
+ LoadImmediate(IP, label->address()); // Target address is never patched.
+ blx(IP); // Use blx instruction so that the return branch prediction works.
+}
+
+
+void Assembler::BranchLinkPatchable(const ExternalLabel* label) {
+ // Make sure that class CallPattern is able to patch the label referred
// to by this code sequence.
// For added code robustness, use 'blx lr' in a patchable sequence and
// use 'blx ip' in a non-patchable sequence (see other BranchLink flavors).
const int32_t offset =
Array::data_offset() + 4*AddExternalLabel(label) - kHeapObjectTag;
if (Address::CanHoldLoadOffset(kLoadWord, offset)) {
- ldr(LR, Address(CP, offset));
+ ldr(LR, Address(PP, offset));
} else {
int32_t offset12_hi = offset & ~kOffset12Mask; // signed
uint32_t offset12_lo = offset & kOffset12Mask; // unsigned
// Inline a simplified version of AddConstant(LR, CP, offset12_hi).
ShifterOperand shifter_op;
if (ShifterOperand::CanHold(offset12_hi, &shifter_op)) {
- add(LR, CP, shifter_op);
+ add(LR, PP, shifter_op);
} else {
movw(LR, Utils::Low16Bits(offset12_hi));
const uint16_t value_high = Utils::High16Bits(offset12_hi);
if (value_high != 0) {
movt(LR, value_high);
}
- add(LR, CP, ShifterOperand(LR));
+ add(LR, PP, ShifterOperand(LR));
}
ldr(LR, Address(LR, offset12_lo));
}
@@ -1500,7 +1509,11 @@
void Assembler::Stop(const char* message) {
if (FLAG_print_stop_message) {
- UNIMPLEMENTED(); // Emit call to StubCode::PrintStopMessage().
+ PushList((1 << R0) | (1 << IP) | (1 << LR)); // Preserve R0, IP, LR.
+ LoadImmediate(R0, reinterpret_cast<int32_t>(message));
+ // PrintStopMessage() preserves all registers.
+ BranchLink(&StubCode::PrintStopMessageLabel()); // Passing message in R0.
+ PopList((1 << R0) | (1 << IP) | (1 << LR)); // Restore R0, IP, LR.
}
// Emit the message address before the svc instruction, so that we can
// 'unstop' and continue execution in the simulator or jump to the next
@@ -1533,25 +1546,35 @@
int32_t Assembler::AddObject(const Object& obj) {
+ if (object_pool_.IsNull()) {
+ // The object pool cannot be used in the vm isolate.
+ ASSERT(Isolate::Current() != Dart::vm_isolate());
+ object_pool_ = GrowableObjectArray::New();
+ }
for (int i = 0; i < object_pool_.Length(); i++) {
if (object_pool_.At(i) == obj.raw()) {
return i;
}
}
object_pool_.Add(obj);
- return object_pool_.Length();
+ return object_pool_.Length() - 1;
}
int32_t Assembler::AddExternalLabel(const ExternalLabel* label) {
- const uword address = label->address();
+ if (object_pool_.IsNull()) {
+ // The object pool cannot be used in the vm isolate.
+ ASSERT(Isolate::Current() != Dart::vm_isolate());
+ object_pool_ = GrowableObjectArray::New();
+ }
+ const word address = label->address();
ASSERT(Utils::IsAligned(address, 4));
// The address is stored in the object array as a RawSmi.
const Smi& smi = Smi::Handle(Smi::New(address >> kSmiTagShift));
// Do not reuse an existing entry, since each reference may be patched
// independently.
object_pool_.Add(smi);
- return object_pool_.Length();
+ return object_pool_.Length() - 1;
}
} // namespace dart
diff --git a/runtime/vm/assembler_arm.h b/runtime/vm/assembler_arm.h
index 1b45703..76f20a1 100644
--- a/runtime/vm/assembler_arm.h
+++ b/runtime/vm/assembler_arm.h
@@ -73,7 +73,7 @@
class ShifterOperand : public ValueObject {
public:
// Data-processing operands - Uninitialized.
- ShifterOperand() : type_(-1) { }
+ ShifterOperand() : type_(-1), encoding_(-1) { }
// Data-processing operands - Copy constructor.
ShifterOperand(const ShifterOperand& other)
@@ -264,7 +264,7 @@
public:
Assembler()
: buffer_(),
- object_pool_(GrowableObjectArray::Handle(GrowableObjectArray::New())),
+ object_pool_(GrowableObjectArray::Handle()),
prologue_offset_(-1),
comments_() { }
~Assembler() { }
@@ -280,13 +280,10 @@
ASSERT(buffer_.pointer_offsets().length() == 0); // No pointers in code.
return buffer_.pointer_offsets();
}
- const GrowableObjectArray& object_pool() const {
- return object_pool_;
- }
+ const GrowableObjectArray& object_pool() const { return object_pool_; }
void FinalizeInstructions(const MemoryRegion& region) {
buffer_.FinalizeInstructions(region);
- ASSERT(object_pool_.Length() == 0); // TODO(regis): Otherwise, more work.
}
// Debugging and bringup support.
@@ -459,14 +456,20 @@
void blx(Register rm, Condition cond = AL);
// Macros.
- // Branch to an entry address that can be patched at runtime.
+ // Branch to an entry address. Call sequence is never patched.
void Branch(const ExternalLabel* label);
+
+ // Branch and link to an entry address. Call sequence is never patched.
void BranchLink(const ExternalLabel* label);
- // Branch to entry after setting LR and storing LR at ad.
+ // Branch and link to an entry address. Call sequence can be patched.
+ void BranchLinkPatchable(const ExternalLabel* label);
+
+ // Branch and link to entry after storing return address at ad.
+ // Call sequence is never patched.
void BranchLinkStore(const ExternalLabel* label, Address ad);
- // Branch to [base + offset] after setting LR.
+ // Branch and link to [base + offset]. Call sequence is never patched.
void BranchLinkOffset(Register base, int offset);
// Add signed constant value to rd. May clobber IP.
@@ -533,7 +536,7 @@
private:
AssemblerBuffer buffer_; // Contains position independent code.
- const GrowableObjectArray& object_pool_; // Objects and jump targets.
+ GrowableObjectArray& object_pool_; // Objects and patchable jump targets.
int32_t prologue_offset_;
int32_t AddObject(const Object& obj);
diff --git a/runtime/vm/assembler_arm_test.cc b/runtime/vm/assembler_arm_test.cc
index 9d92c48..1c6297e 100644
--- a/runtime/vm/assembler_arm_test.cc
+++ b/runtime/vm/assembler_arm_test.cc
@@ -21,11 +21,902 @@
}
-ASSEMBLER_TEST_RUN(Simple, entry) {
+ASSEMBLER_TEST_RUN(Simple, test) {
typedef int (*SimpleCode)();
- EXPECT_EQ(42, EXECUTE_TEST_CODE_INT32(SimpleCode, entry));
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
}
+
+ASSEMBLER_TEST_GENERATE(MoveNegated, assembler) {
+ __ mvn(R0, ShifterOperand(42));
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(MoveNegated, test) {
+ EXPECT(test != NULL);
+ typedef int (*MoveNegated)();
+ EXPECT_EQ(~42, EXECUTE_TEST_CODE_INT32(MoveNegated, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(MoveRotImm, assembler) {
+ ShifterOperand shifter_op;
+ EXPECT(ShifterOperand::CanHold(0x00550000, &shifter_op));
+ __ mov(R0, shifter_op);
+ EXPECT(ShifterOperand::CanHold(0x30000003, &shifter_op));
+ __ add(R0, R0, shifter_op);
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(MoveRotImm, test) {
+ EXPECT(test != NULL);
+ typedef int (*MoveRotImm)();
+ EXPECT_EQ(0x30550003, EXECUTE_TEST_CODE_INT32(MoveRotImm, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(MovImm16, assembler) {
+ __ movw(R0, 0x5678);
+ __ movt(R0, 0x1234);
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(MovImm16, test) {
+ EXPECT(test != NULL);
+ typedef int (*MovImm16)();
+ EXPECT_EQ(0x12345678, EXECUTE_TEST_CODE_INT32(MovImm16, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediate, assembler) {
+ __ mov(R0, ShifterOperand(0));
+ __ cmp(R0, ShifterOperand(0));
+ __ LoadImmediate(R0, 0x12345678, EQ);
+ __ LoadImmediate(R0, 0x87654321, NE);
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediate, test) {
+ EXPECT(test != NULL);
+ typedef int (*LoadImmediate)();
+ EXPECT_EQ(0x12345678, EXECUTE_TEST_CODE_INT32(LoadImmediate, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Vmov, assembler) {
+ __ mov(R3, ShifterOperand(43));
+ __ mov(R1, ShifterOperand(41));
+ __ vmovsrr(S1, R1, R3); // S1:S2 = 41:43
+ __ vmovs(S0, S2); // S0 = S2, S0:S1 == 43:41
+ __ vmovd(D2, D0); // D2 = D0, S4:S5 == 43:41
+ __ vmovrs(R3, S5); // R3 = S5, R3 == 41
+ __ vmovrrs(R1, R2, S4); // R1:R2 = S4:S5, R1:R2 == 43:41
+ __ vmovdrr(D3, R3, R2); // D3 = R3:R2, S6:S7 == 41:41
+ __ vmovsr(S7, R1); // S7 = R1, S6:S7 == 41:43
+ __ vmovrrd(R0, R1, D3); // R0:R1 = D3, R0:R1 == 41:43
+ __ sub(R0, R1, ShifterOperand(R0)); // 43-41
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(Vmov, test) {
+ EXPECT(test != NULL);
+ typedef int (*Vmov)();
+ EXPECT_EQ(2, EXECUTE_TEST_CODE_INT32(Vmov, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(SingleVLoadStore, assembler) {
+ __ LoadImmediate(R0, bit_cast<int32_t, float>(12.3f));
+ __ mov(R2, ShifterOperand(SP));
+ __ str(R0, Address(SP, (-kWordSize * 30), Address::PreIndex));
+ __ vldrs(S0, Address(R2, (-kWordSize * 30)));
+ __ vadds(S0, S0, S0);
+ __ vstrs(S0, Address(R2, (-kWordSize * 30)));
+ __ ldr(R0, Address(SP, (kWordSize * 30), Address::PostIndex));
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(SingleVLoadStore, test) {
+ EXPECT(test != NULL);
+ typedef float (*SingleVLoadStore)();
+ float res = EXECUTE_TEST_CODE_FLOAT(SingleVLoadStore, test->entry());
+ EXPECT_FLOAT_EQ(2*12.3f, res, 0.001f);
+}
+
+
+ASSEMBLER_TEST_GENERATE(DoubleVLoadStore, assembler) {
+ int64_t value = bit_cast<int64_t, double>(12.3);
+ __ LoadImmediate(R0, Utils::Low32Bits(value));
+ __ LoadImmediate(R1, Utils::High32Bits(value));
+ __ mov(R2, ShifterOperand(SP));
+ __ str(R0, Address(SP, (-kWordSize * 30), Address::PreIndex));
+ __ str(R1, Address(R2, (-kWordSize * 29)));
+ __ vldrd(D0, Address(R2, (-kWordSize * 30)));
+ __ vaddd(D0, D0, D0);
+ __ vstrd(D0, Address(R2, (-kWordSize * 30)));
+ __ ldr(R1, Address(R2, (-kWordSize * 29)));
+ __ ldr(R0, Address(SP, (kWordSize * 30), Address::PostIndex));
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(DoubleVLoadStore, test) {
+ EXPECT(test != NULL);
+ typedef double (*DoubleVLoadStore)();
+ double res = EXECUTE_TEST_CODE_DOUBLE(DoubleVLoadStore, test->entry());
+ EXPECT_FLOAT_EQ(2*12.3, res, 0.001);
+}
+
+
+ASSEMBLER_TEST_GENERATE(SingleFPOperations, assembler) {
+ __ LoadSImmediate(S0, 12.3f);
+ __ LoadSImmediate(S1, 3.4f);
+ __ vnegs(S0, S0); // -12.3f
+ __ vabss(S0, S0); // 12.3f
+ __ vadds(S0, S0, S1); // 15.7f
+ __ vmuls(S0, S0, S1); // 53.38f
+ __ vsubs(S0, S0, S1); // 49.98f
+ __ vdivs(S0, S0, S1); // 14.7f
+ __ vsqrts(S0, S0); // 3.8340579f
+ __ vmovrs(R0, S0);
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(SingleFPOperations, test) {
+ EXPECT(test != NULL);
+ typedef float (*SingleFPOperations)();
+ float res = EXECUTE_TEST_CODE_FLOAT(SingleFPOperations, test->entry());
+ EXPECT_FLOAT_EQ(3.8340579f, res, 0.001f);
+}
+
+
+ASSEMBLER_TEST_GENERATE(DoubleFPOperations, assembler) {
+ __ LoadDImmediate(D0, 12.3, R0);
+ __ LoadDImmediate(D1, 3.4, R0);
+ __ vnegd(D0, D0); // -12.3
+ __ vabsd(D0, D0); // 12.3
+ __ vaddd(D0, D0, D1); // 15.7
+ __ vmuld(D0, D0, D1); // 53.38
+ __ vsubd(D0, D0, D1); // 49.98
+ __ vdivd(D0, D0, D1); // 14.7
+ __ vsqrtd(D0, D0); // 3.8340579
+ __ vmovrrd(R0, R1, D0);
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(DoubleFPOperations, test) {
+ EXPECT(test != NULL);
+ typedef double (*DoubleFPOperations)();
+ double res = EXECUTE_TEST_CODE_DOUBLE(DoubleFPOperations, test->entry());
+ EXPECT_FLOAT_EQ(3.8340579, res, 0.001);
+}
+
+
+ASSEMBLER_TEST_GENERATE(IntToDoubleConversion, assembler) {
+ __ mov(R3, ShifterOperand(6));
+ __ vmovsr(S3, R3);
+ __ vcvtdi(D1, S3);
+ __ vmovrrd(R0, R1, D1);
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(IntToDoubleConversion, test) {
+ typedef double (*IntToDoubleConversionCode)();
+ EXPECT(test != NULL);
+ double res = EXECUTE_TEST_CODE_DOUBLE(IntToDoubleConversionCode,
+ test->entry());
+ EXPECT_FLOAT_EQ(6.0, res, 0.001);
+}
+
+
+ASSEMBLER_TEST_GENERATE(LongToDoubleConversion, assembler) {
+ int64_t value = 60000000000LL;
+ __ LoadImmediate(R0, Utils::Low32Bits(value));
+ __ LoadImmediate(R1, Utils::High32Bits(value));
+ __ vmovsr(S0, R0);
+ __ vmovsr(S2, R1);
+ __ vcvtdu(D0, S0);
+ __ vcvtdi(D1, S2);
+ __ LoadDImmediate(D2, 1.0 * (1LL << 32), R0);
+ __ vmlad(D0, D1, D2);
+ __ vmovrrd(R0, R1, D0);
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(LongToDoubleConversion, test) {
+ typedef double (*LongToDoubleConversionCode)();
+ EXPECT(test != NULL);
+ double res = EXECUTE_TEST_CODE_DOUBLE(LongToDoubleConversionCode,
+ test->entry());
+ EXPECT_FLOAT_EQ(60000000000.0, res, 0.001);
+}
+
+
+ASSEMBLER_TEST_GENERATE(IntToFloatConversion, assembler) {
+ __ mov(R3, ShifterOperand(6));
+ __ vmovsr(S3, R3);
+ __ vcvtsi(S1, S3);
+ __ vmovrs(R0, S1);
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(IntToFloatConversion, test) {
+ typedef float (*IntToFloatConversionCode)();
+ EXPECT(test != NULL);
+ float res = EXECUTE_TEST_CODE_FLOAT(IntToFloatConversionCode, test->entry());
+ EXPECT_FLOAT_EQ(6.0, res, 0.001);
+}
+
+
+ASSEMBLER_TEST_GENERATE(FloatToIntConversion, assembler) {
+ __ vmovsr(S1, R0);
+ __ vcvtis(S0, S1);
+ __ vmovrs(R0, S0);
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(FloatToIntConversion, test) {
+ typedef int (*FloatToIntConversion)(float arg);
+ EXPECT(test != NULL);
+ EXPECT_EQ(12,
+ EXECUTE_TEST_CODE_INT32_F(FloatToIntConversion, test->entry(),
+ 12.8f));
+ EXPECT_EQ(INT_MIN,
+ EXECUTE_TEST_CODE_INT32_F(FloatToIntConversion, test->entry(),
+ -FLT_MAX));
+ EXPECT_EQ(INT_MAX,
+ EXECUTE_TEST_CODE_INT32_F(FloatToIntConversion, test->entry(),
+ FLT_MAX));
+}
+
+
+ASSEMBLER_TEST_GENERATE(DoubleToIntConversion, assembler) {
+ __ vmovdrr(D1, R0, R1);
+ __ vcvtid(S0, D1);
+ __ vmovrs(R0, S0);
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(DoubleToIntConversion, test) {
+ typedef int (*DoubleToIntConversion)(double arg);
+ EXPECT(test != NULL);
+ EXPECT_EQ(12,
+ EXECUTE_TEST_CODE_INT32_D(DoubleToIntConversion, test->entry(),
+ 12.8));
+ EXPECT_EQ(INT_MIN,
+ EXECUTE_TEST_CODE_INT32_D(DoubleToIntConversion, test->entry(),
+ -DBL_MAX));
+ EXPECT_EQ(INT_MAX,
+ EXECUTE_TEST_CODE_INT32_D(DoubleToIntConversion, test->entry(),
+ DBL_MAX));
+}
+
+
+ASSEMBLER_TEST_GENERATE(FloatToDoubleConversion, assembler) {
+ __ LoadSImmediate(S1, 12.8f);
+ __ vcvtds(D2, S1);
+ __ vmovrrd(R0, R1, D2);
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(FloatToDoubleConversion, test) {
+ typedef double (*FloatToDoubleConversionCode)();
+ EXPECT(test != NULL);
+ double res = EXECUTE_TEST_CODE_DOUBLE(FloatToDoubleConversionCode,
+ test->entry());
+ EXPECT_FLOAT_EQ(12.8, res, 0.001);
+}
+
+
+ASSEMBLER_TEST_GENERATE(DoubleToFloatConversion, assembler) {
+ __ LoadDImmediate(D1, 12.8, R0);
+ __ vcvtsd(S3, D1);
+ __ vmovrs(R0, S3);
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(DoubleToFloatConversion, test) {
+ typedef float (*DoubleToFloatConversionCode)();
+ EXPECT(test != NULL);
+ float res = EXECUTE_TEST_CODE_FLOAT(DoubleToFloatConversionCode,
+ test->entry());
+ EXPECT_FLOAT_EQ(12.8, res, 0.001);
+}
+
+
+ASSEMBLER_TEST_GENERATE(FloatCompare, assembler) {
+ // Test 12.3f vs 12.5f.
+ __ LoadSImmediate(S0, 12.3f);
+ __ LoadSImmediate(S1, 12.5f);
+
+ // Count errors in R0. R0 is zero if no errors found.
+ __ mov(R0, ShifterOperand(0));
+ __ vcmps(S0, S1);
+ __ vmstat();
+ __ add(R0, R0, ShifterOperand(1), VS); // Error if unordered (Nan).
+ __ add(R0, R0, ShifterOperand(2), GT); // Error if greater.
+ __ add(R0, R0, ShifterOperand(4), EQ); // Error if equal.
+ __ add(R0, R0, ShifterOperand(8), PL); // Error if not less.
+
+ // Test NaN.
+ // Create NaN by dividing 0.0f/0.0f.
+ __ LoadSImmediate(S1, 0.0f);
+ __ vdivs(S1, S1, S1);
+ __ vcmps(S1, S1);
+ __ vmstat();
+ __ add(R0, R0, ShifterOperand(16), VC); // Error if not unordered (not Nan).
+
+ // R0 is 0 if all tests passed.
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(FloatCompare, test) {
+ EXPECT(test != NULL);
+ typedef int (*FloatCompare)();
+ EXPECT_EQ(0, EXECUTE_TEST_CODE_INT32(FloatCompare, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(DoubleCompare, assembler) {
+ // Test 12.3 vs 12.5.
+ __ LoadDImmediate(D0, 12.3, R1);
+ __ LoadDImmediate(D1, 12.5, R1);
+
+ // Count errors in R0. R0 is zero if no errors found.
+ __ mov(R0, ShifterOperand(0));
+ __ vcmpd(D0, D1);
+ __ vmstat();
+ __ add(R0, R0, ShifterOperand(1), VS); // Error if unordered (Nan).
+ __ add(R0, R0, ShifterOperand(2), GT); // Error if greater.
+ __ add(R0, R0, ShifterOperand(4), EQ); // Error if equal.
+ __ add(R0, R0, ShifterOperand(8), PL); // Error if not less.
+
+ // Test NaN.
+ // Create NaN by dividing 0.0/0.0.
+ __ LoadDImmediate(D1, 0.0, R1);
+ __ vdivd(D1, D1, D1);
+ __ vcmpd(D1, D1);
+ __ vmstat();
+ __ add(R0, R0, ShifterOperand(16), VC); // Error if not unordered (not Nan).
+
+ // R0 is 0 if all tests passed.
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(DoubleCompare, test) {
+ EXPECT(test != NULL);
+ typedef int (*DoubleCompare)();
+ EXPECT_EQ(0, EXECUTE_TEST_CODE_INT32(DoubleCompare, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Loop, assembler) {
+ Label loop_entry;
+ __ mov(R0, ShifterOperand(1));
+ __ mov(R1, ShifterOperand(2));
+ __ Bind(&loop_entry);
+ __ mov(R0, ShifterOperand(R0, LSL, 1));
+ __ movs(R1, ShifterOperand(R1, LSR, 1));
+ __ b(&loop_entry, NE);
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(Loop, test) {
+ EXPECT(test != NULL);
+ typedef int (*Loop)();
+ EXPECT_EQ(4, EXECUTE_TEST_CODE_INT32(Loop, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(ForwardBranch, assembler) {
+ Label skip;
+ __ mov(R0, ShifterOperand(42));
+ __ b(&skip);
+ __ mov(R0, ShifterOperand(11));
+ __ Bind(&skip);
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(ForwardBranch, test) {
+ EXPECT(test != NULL);
+ typedef int (*ForwardBranch)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT32(ForwardBranch, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadStore, assembler) {
+ __ mov(R1, ShifterOperand(123));
+ __ Push(R1);
+ __ Pop(R0);
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(LoadStore, test) {
+ EXPECT(test != NULL);
+ typedef int (*LoadStore)();
+ EXPECT_EQ(123, EXECUTE_TEST_CODE_INT32(LoadStore, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(AddSub, assembler) {
+ __ mov(R1, ShifterOperand(40));
+ __ sub(R1, R1, ShifterOperand(2));
+ __ add(R0, R1, ShifterOperand(4));
+ __ rsbs(R0, R0, ShifterOperand(100));
+ __ rsc(R0, R0, ShifterOperand(100));
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(AddSub, test) {
+ EXPECT(test != NULL);
+ typedef int (*AddSub)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT32(AddSub, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Semaphore, assembler) {
+ __ mov(R0, ShifterOperand(40));
+ __ mov(R1, ShifterOperand(42));
+ __ Push(R0);
+ Label retry;
+ __ Bind(&retry);
+ __ ldrex(R0, SP);
+ __ strex(IP, R1, SP); // IP == 0, success
+ __ tst(IP, ShifterOperand(0));
+ __ b(&retry, NE); // NE if context switch occurred between ldrex and strex.
+ __ Pop(R0); // 42
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(Semaphore, test) {
+ EXPECT(test != NULL);
+ typedef int (*Semaphore)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT32(Semaphore, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(FailedSemaphore, assembler) {
+ __ mov(R0, ShifterOperand(40));
+ __ mov(R1, ShifterOperand(42));
+ __ Push(R0);
+ __ ldrex(R0, SP);
+ __ clrex(); // Simulate a context switch.
+ __ strex(IP, R1, SP); // IP == 1, failure
+ __ Pop(R0); // 40
+ __ add(R0, R0, ShifterOperand(IP));
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(FailedSemaphore, test) {
+ EXPECT(test != NULL);
+ typedef int (*FailedSemaphore)();
+ EXPECT_EQ(41, EXECUTE_TEST_CODE_INT32(FailedSemaphore, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(AndOrr, assembler) {
+ __ mov(R1, ShifterOperand(40));
+ __ mov(R2, ShifterOperand(0));
+ __ and_(R1, R2, ShifterOperand(R1));
+ __ mov(R3, ShifterOperand(42));
+ __ orr(R0, R1, ShifterOperand(R3));
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(AndOrr, test) {
+ EXPECT(test != NULL);
+ typedef int (*AndOrr)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT32(AndOrr, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Orrs, assembler) {
+ __ mov(R0, ShifterOperand(0));
+ __ tst(R0, ShifterOperand(R1)); // Set zero-flag.
+ __ orrs(R0, R0, ShifterOperand(1)); // Clear zero-flag.
+ __ mov(PC, ShifterOperand(LR), EQ);
+ __ mov(R0, ShifterOperand(42));
+ __ mov(PC, ShifterOperand(LR), NE); // Only this return should fire.
+ __ mov(R0, ShifterOperand(2));
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(Orrs, test) {
+ EXPECT(test != NULL);
+ typedef int (*Orrs)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT32(Orrs, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Multiply, assembler) {
+ __ mov(R1, ShifterOperand(20));
+ __ mov(R2, ShifterOperand(40));
+ __ mul(R3, R2, R1);
+ __ mov(R0, ShifterOperand(R3));
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(Multiply, test) {
+ EXPECT(test != NULL);
+ typedef int (*Multiply)();
+ EXPECT_EQ(800, EXECUTE_TEST_CODE_INT32(Multiply, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(QuotientRemainder, assembler) {
+ __ vmovsr(S2, R0);
+ __ vmovsr(S4, R2);
+ __ vcvtdi(D1, S2);
+ __ vcvtdi(D2, S4);
+ __ vdivd(D0, D1, D2);
+ __ vcvtid(S0, D0);
+ __ vmovrs(R1, S0); // r1 = r0/r2
+ __ mls(R0, R1, R2, R0); // r0 = r0 - r1*r2
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(QuotientRemainder, test) {
+ EXPECT(test != NULL);
+ typedef int64_t (*QuotientRemainder)(int64_t dividend, int64_t divisor);
+ EXPECT_EQ(0x1000400000da8LL,
+ EXECUTE_TEST_CODE_INT64_LL(QuotientRemainder, test->entry(),
+ 0x12345678, 0x1234));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LongMultiply, assembler) {
+ __ Push(R4);
+ __ Mov(IP, R0);
+ __ mul(R4, R2, R1);
+ __ umull(R0, R1, R2, IP);
+ __ mla(R2, IP, R3, R4);
+ __ add(R1, R2, ShifterOperand(R1));
+ __ Pop(R4);
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(LongMultiply, test) {
+ EXPECT(test != NULL);
+ typedef int64_t (*LongMultiply)(int64_t operand0, int64_t operand1);
+ EXPECT_EQ(6, EXECUTE_TEST_CODE_INT64_LL(LongMultiply, test->entry(), -3, -2));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Clz, assembler) {
+ Label error;
+
+ __ mov(R0, ShifterOperand(0));
+ __ clz(R1, R0);
+ __ cmp(R1, ShifterOperand(32));
+ __ b(&error, NE);
+ __ mov(R2, ShifterOperand(42));
+ __ clz(R2, R2);
+ __ cmp(R2, ShifterOperand(26));
+ __ b(&error, NE);
+ __ mvn(R0, ShifterOperand(0));
+ __ clz(R1, R0);
+ __ cmp(R1, ShifterOperand(0));
+ __ b(&error, NE);
+ __ Lsr(R0, R0, 3);
+ __ clz(R1, R0);
+ __ cmp(R1, ShifterOperand(3));
+ __ b(&error, NE);
+ __ mov(R0, ShifterOperand(0));
+ __ mov(PC, ShifterOperand(LR));
+ __ Bind(&error);
+ __ mov(R0, ShifterOperand(1));
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(Clz, test) {
+ EXPECT(test != NULL);
+ typedef int (*Clz)();
+ EXPECT_EQ(0, EXECUTE_TEST_CODE_INT32(Clz, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Tst, assembler) {
+ Label skip;
+
+ __ mov(R0, ShifterOperand(42));
+ __ mov(R1, ShifterOperand(40));
+ __ tst(R1, ShifterOperand(0));
+ __ b(&skip, NE);
+ __ mov(R0, ShifterOperand(0));
+ __ Bind(&skip);
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(Tst, test) {
+ EXPECT(test != NULL);
+ typedef int (*Tst)();
+ EXPECT_EQ(0, EXECUTE_TEST_CODE_INT32(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Lsl, assembler) {
+ Label skip;
+
+ __ mov(R0, ShifterOperand(1));
+ __ mov(R0, ShifterOperand(R0, LSL, 1));
+ __ mov(R1, ShifterOperand(1));
+ __ mov(R0, ShifterOperand(R0, LSL, R1));
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(Lsl, test) {
+ EXPECT(test != NULL);
+ typedef int (*Tst)();
+ EXPECT_EQ(4, EXECUTE_TEST_CODE_INT32(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Lsr, assembler) {
+ Label skip;
+
+ __ mov(R0, ShifterOperand(4));
+ __ mov(R0, ShifterOperand(R0, LSR, 1));
+ __ mov(R1, ShifterOperand(1));
+ __ mov(R0, ShifterOperand(R0, LSR, R1));
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(Lsr, test) {
+ EXPECT(test != NULL);
+ typedef int (*Tst)();
+ EXPECT_EQ(1, EXECUTE_TEST_CODE_INT32(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Lsr1, assembler) {
+ Label skip;
+
+ __ mov(R0, ShifterOperand(1));
+ __ Lsl(R0, R0, 31);
+ __ Lsr(R0, R0, 31);
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(Lsr1, test) {
+ EXPECT(test != NULL);
+ typedef int (*Tst)();
+ EXPECT_EQ(1, EXECUTE_TEST_CODE_INT32(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Asr1, assembler) {
+ Label skip;
+
+ __ mov(R0, ShifterOperand(1));
+ __ Lsl(R0, R0, 31);
+ __ Asr(R0, R0, 31);
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(Asr1, test) {
+ EXPECT(test != NULL);
+ typedef int (*Tst)();
+ EXPECT_EQ(-1, EXECUTE_TEST_CODE_INT32(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Rsb, assembler) {
+ __ mov(R3, ShifterOperand(10));
+ __ rsb(R0, R3, ShifterOperand(42));
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(Rsb, test) {
+ EXPECT(test != NULL);
+ typedef int (*Rsb)();
+ EXPECT_EQ(32, EXECUTE_TEST_CODE_INT32(Rsb, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Ldrh, assembler) {
+ Label Test1;
+ Label Test2;
+ Label Done;
+
+ __ mov(R1, ShifterOperand(0x11));
+ __ mov(R2, ShifterOperand(SP));
+ __ str(R1, Address(SP, (-kWordSize * 30), Address::PreIndex));
+ __ ldrh(R0, Address(R2, (-kWordSize * 30)));
+ __ cmp(R0, ShifterOperand(0x11));
+ __ b(&Test1, EQ);
+ __ mov(R0, ShifterOperand(1));
+ __ b(&Done);
+ __ Bind(&Test1);
+
+ __ mov(R0, ShifterOperand(0));
+ __ strh(R0, Address(R2, (-kWordSize * 30)));
+ __ ldrh(R1, Address(R2, (-kWordSize * 30)));
+ __ cmp(R1, ShifterOperand(0));
+ __ b(&Test2, EQ);
+ __ mov(R0, ShifterOperand(1));
+ __ b(&Done);
+ __ Bind(&Test2);
+
+ __ mov(R0, ShifterOperand(0));
+ __ Bind(&Done);
+ __ ldr(R1, Address(SP, (kWordSize * 30), Address::PostIndex));
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(Ldrh, test) {
+ EXPECT(test != NULL);
+ typedef int (*Tst)();
+ EXPECT_EQ(0, EXECUTE_TEST_CODE_INT32(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Ldrsb, assembler) {
+ __ mov(R1, ShifterOperand(0xFF));
+ __ mov(R2, ShifterOperand(SP));
+ __ str(R1, Address(SP, (-kWordSize * 30), Address::PreIndex));
+ __ ldrsb(R0, Address(R2, (-kWordSize * 30)));
+ __ ldr(R1, Address(SP, (kWordSize * 30), Address::PostIndex));
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(Ldrsb, test) {
+ EXPECT(test != NULL);
+ typedef int (*Tst)();
+ EXPECT_EQ(-1, EXECUTE_TEST_CODE_INT32(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Ldrb, assembler) {
+ __ mov(R1, ShifterOperand(0xFF));
+ __ mov(R2, ShifterOperand(SP));
+ __ str(R1, Address(SP, (-kWordSize * 30), Address::PreIndex));
+ __ ldrb(R0, Address(R2, (-kWordSize * 30)));
+ __ ldr(R1, Address(SP, (kWordSize * 30), Address::PostIndex));
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(Ldrb, test) {
+ EXPECT(test != NULL);
+ typedef int (*Tst)();
+ EXPECT_EQ(0xff, EXECUTE_TEST_CODE_INT32(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Ldrsh, assembler) {
+ __ mov(R1, ShifterOperand(0xFF));
+ __ mov(R2, ShifterOperand(SP));
+ __ str(R1, Address(SP, (-kWordSize * 30), Address::PreIndex));
+ __ ldrsh(R0, Address(R2, (-kWordSize * 30)));
+ __ ldr(R1, Address(SP, (kWordSize * 30), Address::PostIndex));
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(Ldrsh, test) {
+ EXPECT(test != NULL);
+ typedef int (*Tst)();
+ EXPECT_EQ(0xff, EXECUTE_TEST_CODE_INT32(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Ldrh1, assembler) {
+ __ mov(R1, ShifterOperand(0xFF));
+ __ mov(R2, ShifterOperand(SP));
+ __ str(R1, Address(SP, (-kWordSize * 30), Address::PreIndex));
+ __ ldrh(R0, Address(R2, (-kWordSize * 30)));
+ __ ldr(R1, Address(SP, (kWordSize * 30), Address::PostIndex));
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(Ldrh1, test) {
+ EXPECT(test != NULL);
+ typedef int (*Tst)();
+ EXPECT_EQ(0xff, EXECUTE_TEST_CODE_INT32(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Ldrd, assembler) {
+ __ Mov(IP, SP);
+ __ strd(R2, Address(SP, (-kWordSize * 30), Address::PreIndex));
+ __ strd(R0, Address(IP, (-kWordSize * 28)));
+ __ ldrd(R2, Address(IP, (-kWordSize * 28)));
+ __ ldrd(R0, Address(SP, (kWordSize * 30), Address::PostIndex));
+ __ sub(R0, R0, ShifterOperand(R2));
+ __ add(R1, R1, ShifterOperand(R3));
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(Ldrd, test) {
+ EXPECT(test != NULL);
+ typedef int64_t (*Tst)(int64_t r0r1, int64_t r2r3);
+ EXPECT_EQ(0x0000444400002222LL, EXECUTE_TEST_CODE_INT64_LL(
+ Tst, test->entry(), 0x0000111100000000LL, 0x0000333300002222LL));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Ldm_stm_da, assembler) {
+ __ mov(R0, ShifterOperand(1));
+ __ mov(R1, ShifterOperand(7));
+ __ mov(R2, ShifterOperand(11));
+ __ mov(R3, ShifterOperand(31));
+ __ Push(R5); // We use R5 as accumulator.
+ __ Push(R5);
+ __ Push(R5);
+ __ Push(R5);
+ __ Push(R5);
+ __ Push(R0); // Make room, so we can decrement after.
+ __ stm(DA_W, SP, (1 << R0 | 1 << R1 | 1 << R2 | 1 << R3));
+ __ str(R2, Address(SP)); // Should be a free slot.
+ __ ldr(R5, Address(SP, 1 * kWordSize)); // R0. R5 = +1.
+ __ ldr(IP, Address(SP, 2 * kWordSize)); // R1.
+ __ sub(R5, R5, ShifterOperand(IP)); // -R1. R5 = -6.
+ __ ldr(IP, Address(SP, 3 * kWordSize)); // R2.
+ __ add(R5, R5, ShifterOperand(IP)); // +R2. R5 = +5.
+ __ ldr(IP, Address(SP, 4 * kWordSize)); // R3.
+ __ sub(R5, R5, ShifterOperand(IP)); // -R3. R5 = -26.
+ __ ldm(IB_W, SP, (1 << R0 | 1 << R1 | 1 << R2 | 1 << R3));
+ // Same operations again. But this time from the restore registers.
+ __ add(R5, R5, ShifterOperand(R0));
+ __ sub(R5, R5, ShifterOperand(R1));
+ __ add(R5, R5, ShifterOperand(R2));
+ __ sub(R0, R5, ShifterOperand(R3)); // R0 = result = -52.
+ __ Pop(R1); // Remove storage slot.
+ __ Pop(R5); // Restore R5.
+ __ Pop(R5); // Restore R5.
+ __ Pop(R5); // Restore R5.
+ __ Pop(R5); // Restore R5.
+ __ Pop(R5); // Restore R5.
+ __ mov(PC, ShifterOperand(LR));
+}
+
+
+ASSEMBLER_TEST_RUN(Ldm_stm_da, test) {
+ EXPECT(test != NULL);
+ typedef int (*Tst)();
+ EXPECT_EQ(-52, EXECUTE_TEST_CODE_INT32(Tst, test->entry()));
+}
+
+
} // namespace dart
#endif // defined TARGET_ARCH_ARM
diff --git a/runtime/vm/assembler_ia32.h b/runtime/vm/assembler_ia32.h
index 9fb8106..8ab5b0c 100644
--- a/runtime/vm/assembler_ia32.h
+++ b/runtime/vm/assembler_ia32.h
@@ -299,7 +299,11 @@
class Assembler : public ValueObject {
public:
- Assembler() : buffer_(), prologue_offset_(-1), comments_() { }
+ Assembler()
+ : buffer_(),
+ object_pool_(GrowableObjectArray::Handle()),
+ prologue_offset_(-1),
+ comments_() { }
~Assembler() { }
static const bool kNearJump = true;
@@ -674,6 +678,7 @@
const ZoneGrowableArray<int>& GetPointerOffsets() const {
return buffer_.pointer_offsets();
}
+ const GrowableObjectArray& object_pool() const { return object_pool_; }
void FinalizeInstructions(const MemoryRegion& region) {
buffer_.FinalizeInstructions(region);
@@ -695,6 +700,7 @@
private:
AssemblerBuffer buffer_;
+ GrowableObjectArray& object_pool_; // Object pool is not used on ia32.
int prologue_offset_;
class CodeComment : public ZoneAllocated {
diff --git a/runtime/vm/assembler_ia32_test.cc b/runtime/vm/assembler_ia32_test.cc
index 2a2c33d..f3da046 100644
--- a/runtime/vm/assembler_ia32_test.cc
+++ b/runtime/vm/assembler_ia32_test.cc
@@ -21,9 +21,9 @@
}
-ASSEMBLER_TEST_RUN(Simple, entry) {
+ASSEMBLER_TEST_RUN(Simple, test) {
typedef int (*SimpleCode)();
- EXPECT_EQ(42, reinterpret_cast<SimpleCode>(entry)());
+ EXPECT_EQ(42, reinterpret_cast<SimpleCode>(test->entry())());
}
@@ -33,10 +33,10 @@
}
-ASSEMBLER_TEST_RUN(ReadArgument, entry) {
+ASSEMBLER_TEST_RUN(ReadArgument, test) {
typedef int (*ReadArgumentCode)(int n);
- EXPECT_EQ(42, reinterpret_cast<ReadArgumentCode>(entry)(42));
- EXPECT_EQ(87, reinterpret_cast<ReadArgumentCode>(entry)(87));
+ EXPECT_EQ(42, reinterpret_cast<ReadArgumentCode>(test->entry())(42));
+ EXPECT_EQ(87, reinterpret_cast<ReadArgumentCode>(test->entry())(87));
}
@@ -98,7 +98,7 @@
}
-ASSEMBLER_TEST_RUN(AddressingModes, entry) {
+ASSEMBLER_TEST_RUN(AddressingModes, test) {
// Avoid running the code since it is constructed to lead to crashes.
}
@@ -124,11 +124,11 @@
}
-ASSEMBLER_TEST_RUN(JumpAroundCrash, entry) {
- Instr* instr = Instr::At(entry);
+ASSEMBLER_TEST_RUN(JumpAroundCrash, test) {
+ Instr* instr = Instr::At(test->entry());
EXPECT(!instr->IsBreakPoint());
typedef void (*JumpAroundCrashCode)();
- reinterpret_cast<JumpAroundCrashCode>(entry)();
+ reinterpret_cast<JumpAroundCrashCode>(test->entry())();
}
@@ -153,9 +153,9 @@
}
-ASSEMBLER_TEST_RUN(NearJumpAroundCrash, entry) {
+ASSEMBLER_TEST_RUN(NearJumpAroundCrash, test) {
typedef void (*NearJumpAroundCrashCode)();
- reinterpret_cast<NearJumpAroundCrashCode>(entry)();
+ reinterpret_cast<NearJumpAroundCrashCode>(test->entry())();
}
@@ -172,9 +172,9 @@
}
-ASSEMBLER_TEST_RUN(SimpleLoop, entry) {
+ASSEMBLER_TEST_RUN(SimpleLoop, test) {
typedef int (*SimpleLoopCode)();
- EXPECT_EQ(2 * 87, reinterpret_cast<SimpleLoopCode>(entry)());
+ EXPECT_EQ(2 * 87, reinterpret_cast<SimpleLoopCode>(test->entry())());
}
@@ -190,9 +190,9 @@
}
-ASSEMBLER_TEST_RUN(Increment, entry) {
+ASSEMBLER_TEST_RUN(Increment, test) {
typedef int (*IncrementCode)();
- EXPECT_EQ(2, reinterpret_cast<IncrementCode>(entry)());
+ EXPECT_EQ(2, reinterpret_cast<IncrementCode>(test->entry())());
}
@@ -208,9 +208,9 @@
}
-ASSEMBLER_TEST_RUN(Decrement, entry) {
+ASSEMBLER_TEST_RUN(Decrement, test) {
typedef int (*DecrementCode)();
- EXPECT_EQ(0, reinterpret_cast<DecrementCode>(entry)());
+ EXPECT_EQ(0, reinterpret_cast<DecrementCode>(test->entry())());
}
@@ -224,9 +224,10 @@
}
-ASSEMBLER_TEST_RUN(AddressBinOp, entry) {
+ASSEMBLER_TEST_RUN(AddressBinOp, test) {
typedef int (*AddressBinOpCode)(int a);
- EXPECT_EQ((2 + 2 + 1 - 2) * 2, reinterpret_cast<AddressBinOpCode>(entry)(2));
+ EXPECT_EQ((2 + 2 + 1 - 2) * 2,
+ reinterpret_cast<AddressBinOpCode>(test->entry())(2));
}
@@ -239,9 +240,9 @@
}
-ASSEMBLER_TEST_RUN(SignedMultiply, entry) {
+ASSEMBLER_TEST_RUN(SignedMultiply, test) {
typedef int (*SignedMultiply)();
- EXPECT_EQ(8000, reinterpret_cast<SignedMultiply>(entry)());
+ EXPECT_EQ(8000, reinterpret_cast<SignedMultiply>(test->entry())());
}
@@ -255,9 +256,9 @@
}
-ASSEMBLER_TEST_RUN(OverflowSignedMultiply, entry) {
+ASSEMBLER_TEST_RUN(OverflowSignedMultiply, test) {
typedef int (*OverflowSignedMultiply)();
- EXPECT_EQ(0, reinterpret_cast<OverflowSignedMultiply>(entry)());
+ EXPECT_EQ(0, reinterpret_cast<OverflowSignedMultiply>(test->entry())());
}
@@ -273,9 +274,9 @@
}
-ASSEMBLER_TEST_RUN(SignedMultiply1, entry) {
+ASSEMBLER_TEST_RUN(SignedMultiply1, test) {
typedef int (*SignedMultiply1)();
- EXPECT_EQ(8000, reinterpret_cast<SignedMultiply1>(entry)());
+ EXPECT_EQ(8000, reinterpret_cast<SignedMultiply1>(test->entry())());
}
@@ -287,9 +288,9 @@
}
-ASSEMBLER_TEST_RUN(Negate, entry) {
+ASSEMBLER_TEST_RUN(Negate, test) {
typedef int (*Negate)();
- EXPECT_EQ(-42, reinterpret_cast<Negate>(entry)());
+ EXPECT_EQ(-42, reinterpret_cast<Negate>(test->entry())());
}
@@ -306,9 +307,9 @@
}
-ASSEMBLER_TEST_RUN(MoveExtend, entry) {
+ASSEMBLER_TEST_RUN(MoveExtend, test) {
typedef int (*MoveExtend)();
- EXPECT_EQ(0xff - 1 + 0xffff, reinterpret_cast<MoveExtend>(entry)());
+ EXPECT_EQ(0xff - 1 + 0xffff, reinterpret_cast<MoveExtend>(test->entry())());
}
@@ -329,9 +330,10 @@
}
-ASSEMBLER_TEST_RUN(MoveExtendMemory, entry) {
+ASSEMBLER_TEST_RUN(MoveExtendMemory, test) {
typedef int (*MoveExtendMemory)();
- EXPECT_EQ(0xff - 1 + 0xffff, reinterpret_cast<MoveExtendMemory>(entry)());
+ EXPECT_EQ(0xff - 1 + 0xffff,
+ reinterpret_cast<MoveExtendMemory>(test->entry())());
}
@@ -351,9 +353,9 @@
}
-ASSEMBLER_TEST_RUN(Bitwise, entry) {
+ASSEMBLER_TEST_RUN(Bitwise, test) {
typedef int (*Bitwise)();
- EXPECT_EQ(256 + 1, reinterpret_cast<Bitwise>(entry)());
+ EXPECT_EQ(256 + 1, reinterpret_cast<Bitwise>(test->entry())());
}
@@ -525,9 +527,9 @@
}
-ASSEMBLER_TEST_RUN(LogicalOps, entry) {
+ASSEMBLER_TEST_RUN(LogicalOps, test) {
typedef int (*LogicalOpsCode)();
- EXPECT_EQ(0, reinterpret_cast<LogicalOpsCode>(entry)());
+ EXPECT_EQ(0, reinterpret_cast<LogicalOpsCode>(test->entry())());
}
@@ -586,9 +588,9 @@
}
-ASSEMBLER_TEST_RUN(LogicalTest, entry) {
+ASSEMBLER_TEST_RUN(LogicalTest, test) {
typedef int (*LogicalTestCode)();
- EXPECT_EQ(0, reinterpret_cast<LogicalTestCode>(entry)());
+ EXPECT_EQ(0, reinterpret_cast<LogicalTestCode>(test->entry())());
}
@@ -604,9 +606,9 @@
}
-ASSEMBLER_TEST_RUN(CompareSwapEQ, entry) {
+ASSEMBLER_TEST_RUN(CompareSwapEQ, test) {
typedef int (*CompareSwapEQCode)();
- EXPECT_EQ(0, reinterpret_cast<CompareSwapEQCode>(entry)());
+ EXPECT_EQ(0, reinterpret_cast<CompareSwapEQCode>(test->entry())());
}
@@ -622,9 +624,9 @@
}
-ASSEMBLER_TEST_RUN(CompareSwapNEQ, entry) {
+ASSEMBLER_TEST_RUN(CompareSwapNEQ, test) {
typedef int (*CompareSwapNEQCode)();
- EXPECT_EQ(4, reinterpret_cast<CompareSwapNEQCode>(entry)());
+ EXPECT_EQ(4, reinterpret_cast<CompareSwapNEQCode>(test->entry())());
}
@@ -638,9 +640,9 @@
}
-ASSEMBLER_TEST_RUN(SignedDivide, entry) {
+ASSEMBLER_TEST_RUN(SignedDivide, test) {
typedef int (*SignedDivide)();
- EXPECT_EQ(-87 / 42, reinterpret_cast<SignedDivide>(entry)());
+ EXPECT_EQ(-87 / 42, reinterpret_cast<SignedDivide>(test->entry())());
}
@@ -653,9 +655,9 @@
}
-ASSEMBLER_TEST_RUN(Exchange, entry) {
+ASSEMBLER_TEST_RUN(Exchange, test) {
typedef int (*Exchange)();
- EXPECT_EQ(987654321 - 123456789, reinterpret_cast<Exchange>(entry)());
+ EXPECT_EQ(987654321 - 123456789, reinterpret_cast<Exchange>(test->entry())());
}
@@ -694,9 +696,9 @@
}
-ASSEMBLER_TEST_RUN(CallSimpleLeaf, entry) {
+ASSEMBLER_TEST_RUN(CallSimpleLeaf, test) {
typedef int (*CallSimpleLeafCode)();
- EXPECT_EQ(42 + 87, reinterpret_cast<CallSimpleLeafCode>(entry)());
+ EXPECT_EQ(42 + 87, reinterpret_cast<CallSimpleLeafCode>(test->entry())());
}
@@ -713,9 +715,9 @@
}
-ASSEMBLER_TEST_RUN(JumpSimpleLeaf, entry) {
+ASSEMBLER_TEST_RUN(JumpSimpleLeaf, test) {
typedef int (*JumpSimpleLeafCode)();
- EXPECT_EQ(42, reinterpret_cast<JumpSimpleLeafCode>(entry)());
+ EXPECT_EQ(42, reinterpret_cast<JumpSimpleLeafCode>(test->entry())());
}
@@ -734,9 +736,10 @@
}
-ASSEMBLER_TEST_RUN(JumpConditionalSimpleLeaf, entry) {
+ASSEMBLER_TEST_RUN(JumpConditionalSimpleLeaf, test) {
typedef int (*JumpConditionalSimpleLeafCode)();
- EXPECT_EQ(42, reinterpret_cast<JumpConditionalSimpleLeafCode>(entry)());
+ EXPECT_EQ(42,
+ reinterpret_cast<JumpConditionalSimpleLeafCode>(test->entry())());
}
@@ -759,9 +762,9 @@
}
-ASSEMBLER_TEST_RUN(SingleFPMoves, entry) {
+ASSEMBLER_TEST_RUN(SingleFPMoves, test) {
typedef float (*SingleFPMovesCode)();
- float res = reinterpret_cast<SingleFPMovesCode>(entry)();
+ float res = reinterpret_cast<SingleFPMovesCode>(test->entry())();
EXPECT_EQ(234.0f, res);
}
@@ -783,9 +786,9 @@
}
-ASSEMBLER_TEST_RUN(SingleFPMoves2, entry) {
+ASSEMBLER_TEST_RUN(SingleFPMoves2, test) {
typedef float (*SingleFPMoves2Code)();
- float res = reinterpret_cast<SingleFPMoves2Code>(entry)();
+ float res = reinterpret_cast<SingleFPMoves2Code>(test->entry())();
EXPECT_EQ(234.0f, res);
}
@@ -803,9 +806,9 @@
}
-ASSEMBLER_TEST_RUN(SingleFPUStackMoves, entry) {
+ASSEMBLER_TEST_RUN(SingleFPUStackMoves, test) {
typedef int (*SingleFPUStackMovesCode)();
- int res = reinterpret_cast<SingleFPUStackMovesCode>(entry)();
+ int res = reinterpret_cast<SingleFPUStackMovesCode>(test->entry())();
EXPECT_EQ(234.0f, (bit_cast<float, int>(res)));
}
@@ -827,9 +830,9 @@
}
-ASSEMBLER_TEST_RUN(SingleFPOperations, entry) {
+ASSEMBLER_TEST_RUN(SingleFPOperations, test) {
typedef float (*SingleFPOperationsCode)();
- float res = reinterpret_cast<SingleFPOperationsCode>(entry)();
+ float res = reinterpret_cast<SingleFPOperationsCode>(test->entry())();
EXPECT_FLOAT_EQ(14.7f, res, 0.001f);
}
@@ -855,9 +858,9 @@
}
-ASSEMBLER_TEST_RUN(PackedFPOperations, entry) {
+ASSEMBLER_TEST_RUN(PackedFPOperations, test) {
typedef float (*PackedFPOperationsCode)();
- float res = reinterpret_cast<PackedFPOperationsCode>(entry)();
+ float res = reinterpret_cast<PackedFPOperationsCode>(test->entry())();
EXPECT_FLOAT_EQ(14.7f, res, 0.001f);
}
@@ -882,9 +885,9 @@
}
-ASSEMBLER_TEST_RUN(PackedFPOperations2, entry) {
+ASSEMBLER_TEST_RUN(PackedFPOperations2, test) {
typedef float (*PackedFPOperations2Code)();
- float res = reinterpret_cast<PackedFPOperations2Code>(entry)();
+ float res = reinterpret_cast<PackedFPOperations2Code>(test->entry())();
EXPECT_FLOAT_EQ(0.0f, res, 0.001f);
}
@@ -902,9 +905,9 @@
}
-ASSEMBLER_TEST_RUN(PackedCompareEQ, entry) {
+ASSEMBLER_TEST_RUN(PackedCompareEQ, test) {
typedef uint32_t (*PackedCompareEQCode)();
- uint32_t res = reinterpret_cast<PackedCompareEQCode>(entry)();
+ uint32_t res = reinterpret_cast<PackedCompareEQCode>(test->entry())();
EXPECT_EQ(static_cast<uword>(0x0), res);
}
@@ -922,9 +925,9 @@
}
-ASSEMBLER_TEST_RUN(PackedCompareNEQ, entry) {
+ASSEMBLER_TEST_RUN(PackedCompareNEQ, test) {
typedef uint32_t (*PackedCompareNEQCode)();
- uint32_t res = reinterpret_cast<PackedCompareNEQCode>(entry)();
+ uint32_t res = reinterpret_cast<PackedCompareNEQCode>(test->entry())();
EXPECT_EQ(static_cast<uword>(0xFFFFFFFF), res);
}
@@ -942,9 +945,9 @@
}
-ASSEMBLER_TEST_RUN(PackedCompareLT, entry) {
+ASSEMBLER_TEST_RUN(PackedCompareLT, test) {
typedef uint32_t (*PackedCompareLTCode)();
- uint32_t res = reinterpret_cast<PackedCompareLTCode>(entry)();
+ uint32_t res = reinterpret_cast<PackedCompareLTCode>(test->entry())();
EXPECT_EQ(static_cast<uword>(0xFFFFFFFF), res);
}
@@ -962,9 +965,9 @@
}
-ASSEMBLER_TEST_RUN(PackedCompareLE, entry) {
+ASSEMBLER_TEST_RUN(PackedCompareLE, test) {
typedef uint32_t (*PackedCompareLECode)();
- uint32_t res = reinterpret_cast<PackedCompareLECode>(entry)();
+ uint32_t res = reinterpret_cast<PackedCompareLECode>(test->entry())();
EXPECT_EQ(static_cast<uword>(0xFFFFFFFF), res);
}
@@ -982,9 +985,9 @@
}
-ASSEMBLER_TEST_RUN(PackedCompareNLT, entry) {
+ASSEMBLER_TEST_RUN(PackedCompareNLT, test) {
typedef uint32_t (*PackedCompareNLTCode)();
- uint32_t res = reinterpret_cast<PackedCompareNLTCode>(entry)();
+ uint32_t res = reinterpret_cast<PackedCompareNLTCode>(test->entry())();
EXPECT_EQ(static_cast<uword>(0x0), res);
}
@@ -1002,9 +1005,9 @@
}
-ASSEMBLER_TEST_RUN(PackedCompareNLE, entry) {
+ASSEMBLER_TEST_RUN(PackedCompareNLE, test) {
typedef uint32_t (*PackedCompareNLECode)();
- uint32_t res = reinterpret_cast<PackedCompareNLECode>(entry)();
+ uint32_t res = reinterpret_cast<PackedCompareNLECode>(test->entry())();
EXPECT_EQ(static_cast<uword>(0x0), res);
}
@@ -1024,9 +1027,9 @@
}
-ASSEMBLER_TEST_RUN(PackedNegate, entry) {
+ASSEMBLER_TEST_RUN(PackedNegate, test) {
typedef float (*PackedNegateCode)();
- float res = reinterpret_cast<PackedNegateCode>(entry)();
+ float res = reinterpret_cast<PackedNegateCode>(test->entry())();
EXPECT_FLOAT_EQ(-12.3f, res, 0.001f);
}
@@ -1046,9 +1049,9 @@
}
-ASSEMBLER_TEST_RUN(PackedAbsolute, entry) {
+ASSEMBLER_TEST_RUN(PackedAbsolute, test) {
typedef float (*PackedAbsoluteCode)();
- float res = reinterpret_cast<PackedAbsoluteCode>(entry)();
+ float res = reinterpret_cast<PackedAbsoluteCode>(test->entry())();
EXPECT_FLOAT_EQ(15.3f, res, 0.001f);
}
@@ -1066,9 +1069,9 @@
}
-ASSEMBLER_TEST_RUN(PackedSetWZero, entry) {
+ASSEMBLER_TEST_RUN(PackedSetWZero, test) {
typedef float (*PackedSetWZeroCode)();
- float res = reinterpret_cast<PackedSetWZeroCode>(entry)();
+ float res = reinterpret_cast<PackedSetWZeroCode>(test->entry())();
EXPECT_FLOAT_EQ(0.0f, res, 0.001f);
}
@@ -1086,9 +1089,9 @@
}
-ASSEMBLER_TEST_RUN(PackedMin, entry) {
+ASSEMBLER_TEST_RUN(PackedMin, test) {
typedef float (*PackedMinCode)();
- float res = reinterpret_cast<PackedMinCode>(entry)();
+ float res = reinterpret_cast<PackedMinCode>(test->entry())();
EXPECT_FLOAT_EQ(2.0f, res, 0.001f);
}
@@ -1106,9 +1109,9 @@
}
-ASSEMBLER_TEST_RUN(PackedMax, entry) {
+ASSEMBLER_TEST_RUN(PackedMax, test) {
typedef float (*PackedMaxCode)();
- float res = reinterpret_cast<PackedMaxCode>(entry)();
+ float res = reinterpret_cast<PackedMaxCode>(test->entry())();
EXPECT_FLOAT_EQ(4.0f, res, 0.001f);
}
@@ -1140,9 +1143,9 @@
}
-ASSEMBLER_TEST_RUN(PackedLogicalOr, entry) {
+ASSEMBLER_TEST_RUN(PackedLogicalOr, test) {
typedef uint32_t (*PackedLogicalOrCode)();
- uint32_t res = reinterpret_cast<PackedLogicalOrCode>(entry)();
+ uint32_t res = reinterpret_cast<PackedLogicalOrCode>(test->entry())();
EXPECT_EQ(0xFFFFFFFF, res);
}
@@ -1173,9 +1176,9 @@
}
-ASSEMBLER_TEST_RUN(PackedLogicalAnd, entry) {
+ASSEMBLER_TEST_RUN(PackedLogicalAnd, test) {
typedef uint32_t (*PackedLogicalAndCode)();
- uint32_t res = reinterpret_cast<PackedLogicalAndCode>(entry)();
+ uint32_t res = reinterpret_cast<PackedLogicalAndCode>(test->entry())();
EXPECT_EQ(static_cast<uword>(0x0000F000), res);
}
@@ -1199,9 +1202,9 @@
}
-ASSEMBLER_TEST_RUN(PackedLogicalNot, entry) {
+ASSEMBLER_TEST_RUN(PackedLogicalNot, test) {
typedef uint32_t (*PackedLogicalNotCode)();
- uint32_t res = reinterpret_cast<PackedLogicalNotCode>(entry)();
+ uint32_t res = reinterpret_cast<PackedLogicalNotCode>(test->entry())();
EXPECT_EQ(static_cast<uword>(0x0), res);
}
@@ -1221,9 +1224,9 @@
}
-ASSEMBLER_TEST_RUN(SingleFPOperationsStack, entry) {
+ASSEMBLER_TEST_RUN(SingleFPOperationsStack, test) {
typedef float (*SingleFPOperationsStackCode)(float f);
- float res = reinterpret_cast<SingleFPOperationsStackCode>(entry)(3.4);
+ float res = reinterpret_cast<SingleFPOperationsStackCode>(test->entry())(3.4);
EXPECT_FLOAT_EQ(14.7f, res, 0.001f);
}
@@ -1264,9 +1267,9 @@
}
-ASSEMBLER_TEST_RUN(DoubleFPMoves, entry) {
+ASSEMBLER_TEST_RUN(DoubleFPMoves, test) {
typedef double (*DoubleFPMovesCode)();
- double res = reinterpret_cast<DoubleFPMovesCode>(entry)();
+ double res = reinterpret_cast<DoubleFPMovesCode>(test->entry())();
EXPECT_FLOAT_EQ(1024.67, res, 0.0001);
}
@@ -1286,9 +1289,9 @@
}
-ASSEMBLER_TEST_RUN(DoubleFPUStackMoves, entry) {
+ASSEMBLER_TEST_RUN(DoubleFPUStackMoves, test) {
typedef int64_t (*DoubleFPUStackMovesCode)();
- int64_t res = reinterpret_cast<DoubleFPUStackMovesCode>(entry)();
+ int64_t res = reinterpret_cast<DoubleFPUStackMovesCode>(test->entry())();
EXPECT_FLOAT_EQ(1024.67, (bit_cast<double, int64_t>(res)), 0.001);
}
@@ -1320,9 +1323,9 @@
}
-ASSEMBLER_TEST_RUN(DoubleFPOperations, entry) {
+ASSEMBLER_TEST_RUN(DoubleFPOperations, test) {
typedef double (*DoubleFPOperationsCode)();
- double res = reinterpret_cast<DoubleFPOperationsCode>(entry)();
+ double res = reinterpret_cast<DoubleFPOperationsCode>(test->entry())();
EXPECT_FLOAT_EQ(14.7, res, 0.001);
}
@@ -1352,9 +1355,10 @@
}
-ASSEMBLER_TEST_RUN(DoubleFPOperationsStack, entry) {
+ASSEMBLER_TEST_RUN(DoubleFPOperationsStack, test) {
typedef double (*DoubleFPOperationsStackCode)(double d);
- double res = reinterpret_cast<DoubleFPOperationsStackCode>(entry)(3.4);
+ double res =
+ reinterpret_cast<DoubleFPOperationsStackCode>(test->entry())(3.4);
EXPECT_FLOAT_EQ(14.7, res, 0.001);
}
@@ -1372,9 +1376,9 @@
}
-ASSEMBLER_TEST_RUN(IntToDoubleConversion, entry) {
+ASSEMBLER_TEST_RUN(IntToDoubleConversion, test) {
typedef double (*IntToDoubleConversionCode)();
- double res = reinterpret_cast<IntToDoubleConversionCode>(entry)();
+ double res = reinterpret_cast<IntToDoubleConversionCode>(test->entry())();
EXPECT_FLOAT_EQ(6.0, res, 0.001);
}
@@ -1385,9 +1389,9 @@
}
-ASSEMBLER_TEST_RUN(IntToDoubleConversion2, entry) {
+ASSEMBLER_TEST_RUN(IntToDoubleConversion2, test) {
typedef double (*IntToDoubleConversion2Code)(int i);
- double res = reinterpret_cast<IntToDoubleConversion2Code>(entry)(3);
+ double res = reinterpret_cast<IntToDoubleConversion2Code>(test->entry())(3);
EXPECT_FLOAT_EQ(3.0, res, 0.001);
}
@@ -1403,9 +1407,9 @@
}
-ASSEMBLER_TEST_RUN(IntToFloatConversion, entry) {
+ASSEMBLER_TEST_RUN(IntToFloatConversion, test) {
typedef float (*IntToFloatConversionCode)();
- float res = reinterpret_cast<IntToFloatConversionCode>(entry)();
+ float res = reinterpret_cast<IntToFloatConversionCode>(test->entry())();
EXPECT_FLOAT_EQ(6.0, res, 0.001);
}
@@ -1418,11 +1422,12 @@
}
-ASSEMBLER_TEST_RUN(FloatToIntConversionRound, entry) {
+ASSEMBLER_TEST_RUN(FloatToIntConversionRound, test) {
typedef int (*FloatToIntConversionRoundCode)(float f);
- int res = reinterpret_cast<FloatToIntConversionRoundCode>(entry)(12.3);
+ int res =
+ reinterpret_cast<FloatToIntConversionRoundCode>(test->entry())(12.3);
EXPECT_EQ(12, res);
- res = reinterpret_cast<FloatToIntConversionRoundCode>(entry)(12.8);
+ res = reinterpret_cast<FloatToIntConversionRoundCode>(test->entry())(12.8);
EXPECT_EQ(13, res);
}
@@ -1435,11 +1440,12 @@
}
-ASSEMBLER_TEST_RUN(FloatToIntConversionTrunc, entry) {
+ASSEMBLER_TEST_RUN(FloatToIntConversionTrunc, test) {
typedef int (*FloatToIntConversionTruncCode)(float f);
- int res = reinterpret_cast<FloatToIntConversionTruncCode>(entry)(12.3);
+ int res =
+ reinterpret_cast<FloatToIntConversionTruncCode>(test->entry())(12.3);
EXPECT_EQ(12, res);
- res = reinterpret_cast<FloatToIntConversionTruncCode>(entry)(12.8);
+ res = reinterpret_cast<FloatToIntConversionTruncCode>(test->entry())(12.8);
EXPECT_EQ(12, res);
}
@@ -1459,9 +1465,9 @@
}
-ASSEMBLER_TEST_RUN(FloatToDoubleConversion, entry) {
+ASSEMBLER_TEST_RUN(FloatToDoubleConversion, test) {
typedef double (*FloatToDoubleConversionCode)();
- double res = reinterpret_cast<FloatToDoubleConversionCode>(entry)();
+ double res = reinterpret_cast<FloatToDoubleConversionCode>(test->entry())();
EXPECT_FLOAT_EQ(12.3, res, 0.001);
}
@@ -1508,9 +1514,9 @@
}
-ASSEMBLER_TEST_RUN(FloatCompare, entry) {
+ASSEMBLER_TEST_RUN(FloatCompare, test) {
typedef int (*FloatCompareCode)();
- int res = reinterpret_cast<FloatCompareCode>(entry)();
+ int res = reinterpret_cast<FloatCompareCode>(test->entry())();
EXPECT_EQ(0, res);
}
@@ -1579,9 +1585,9 @@
}
-ASSEMBLER_TEST_RUN(DoubleCompare, entry) {
+ASSEMBLER_TEST_RUN(DoubleCompare, test) {
typedef int (*DoubleCompareCode)();
- int res = reinterpret_cast<DoubleCompareCode>(entry)();
+ int res = reinterpret_cast<DoubleCompareCode>(test->entry())();
EXPECT_EQ(0, res);
}
@@ -1602,9 +1608,9 @@
}
-ASSEMBLER_TEST_RUN(DoubleToFloatConversion, entry) {
+ASSEMBLER_TEST_RUN(DoubleToFloatConversion, test) {
typedef float (*DoubleToFloatConversionCode)();
- float res = reinterpret_cast<DoubleToFloatConversionCode>(entry)();
+ float res = reinterpret_cast<DoubleToFloatConversionCode>(test->entry())();
EXPECT_FLOAT_EQ(12.3f, res, 0.001);
}
@@ -1616,11 +1622,12 @@
}
-ASSEMBLER_TEST_RUN(DoubleToIntConversionRound, entry) {
+ASSEMBLER_TEST_RUN(DoubleToIntConversionRound, test) {
typedef int (*DoubleToIntConversionRoundCode)(double d);
- int res = reinterpret_cast<DoubleToIntConversionRoundCode>(entry)(12.3);
+ int res =
+ reinterpret_cast<DoubleToIntConversionRoundCode>(test->entry())(12.3);
EXPECT_EQ(12, res);
- res = reinterpret_cast<DoubleToIntConversionRoundCode>(entry)(12.8);
+ res = reinterpret_cast<DoubleToIntConversionRoundCode>(test->entry())(12.8);
EXPECT_EQ(13, res);
}
@@ -1632,11 +1639,12 @@
}
-ASSEMBLER_TEST_RUN(DoubleToIntConversionTrunc, entry) {
+ASSEMBLER_TEST_RUN(DoubleToIntConversionTrunc, test) {
typedef int (*DoubleToIntConversionTruncCode)(double d);
- int res = reinterpret_cast<DoubleToIntConversionTruncCode>(entry)(12.3);
+ int res =
+ reinterpret_cast<DoubleToIntConversionTruncCode>(test->entry())(12.3);
EXPECT_EQ(12, res);
- res = reinterpret_cast<DoubleToIntConversionTruncCode>(entry)(12.8);
+ res = reinterpret_cast<DoubleToIntConversionTruncCode>(test->entry())(12.8);
EXPECT_EQ(12, res);
}
@@ -1654,15 +1662,15 @@
}
-ASSEMBLER_TEST_RUN(DoubleToDoubleTrunc, entry) {
+ASSEMBLER_TEST_RUN(DoubleToDoubleTrunc, test) {
typedef double (*DoubleToDoubleTruncCode)(double d);
- double res = reinterpret_cast<DoubleToDoubleTruncCode>(entry)(12.3);
+ double res = reinterpret_cast<DoubleToDoubleTruncCode>(test->entry())(12.3);
EXPECT_EQ(12.0, res);
- res = reinterpret_cast<DoubleToDoubleTruncCode>(entry)(12.8);
+ res = reinterpret_cast<DoubleToDoubleTruncCode>(test->entry())(12.8);
EXPECT_EQ(12.0, res);
- res = reinterpret_cast<DoubleToDoubleTruncCode>(entry)(-12.3);
+ res = reinterpret_cast<DoubleToDoubleTruncCode>(test->entry())(-12.3);
EXPECT_EQ(-12.0, res);
- res = reinterpret_cast<DoubleToDoubleTruncCode>(entry)(-12.8);
+ res = reinterpret_cast<DoubleToDoubleTruncCode>(test->entry())(-12.8);
EXPECT_EQ(-12.0, res);
}
@@ -1680,27 +1688,31 @@
}
-ASSEMBLER_TEST_RUN(DoubleToDoubleRound, entry) {
+ASSEMBLER_TEST_RUN(DoubleToDoubleRound, test) {
typedef double (*DoubleToDoubleRoundCode)(double d);
- double res = reinterpret_cast<DoubleToDoubleRoundCode>(entry)(12.3);
+ double res = reinterpret_cast<DoubleToDoubleRoundCode>(test->entry())(12.3);
EXPECT_EQ(12.0, res);
- res = reinterpret_cast<DoubleToDoubleRoundCode>(entry)(12.8);
+ res = reinterpret_cast<DoubleToDoubleRoundCode>(test->entry())(12.8);
EXPECT_EQ(13.0, res);
- res = reinterpret_cast<DoubleToDoubleRoundCode>(entry)(0.5);
+ res = reinterpret_cast<DoubleToDoubleRoundCode>(test->entry())(0.5);
EXPECT_EQ(1.0, res);
- res = reinterpret_cast<DoubleToDoubleRoundCode>(entry)(-12.3);
+ res = reinterpret_cast<DoubleToDoubleRoundCode>(test->entry())(-12.3);
EXPECT_EQ(-12.0, res);
- res = reinterpret_cast<DoubleToDoubleRoundCode>(entry)(-12.8);
+ res = reinterpret_cast<DoubleToDoubleRoundCode>(test->entry())(-12.8);
EXPECT_EQ(-13.0, res);
- res = reinterpret_cast<DoubleToDoubleRoundCode>(entry)(-0.5);
+ res = reinterpret_cast<DoubleToDoubleRoundCode>(test->entry())(-0.5);
EXPECT_EQ(-1.0, res);
- res = reinterpret_cast<DoubleToDoubleRoundCode>(entry)(0.49999999999999994);
+ res = reinterpret_cast<DoubleToDoubleRoundCode>(
+ test->entry())(0.49999999999999994);
EXPECT_EQ(0.0, res);
- res = reinterpret_cast<DoubleToDoubleRoundCode>(entry)(-0.49999999999999994);
+ res = reinterpret_cast<DoubleToDoubleRoundCode>(
+ test->entry())(-0.49999999999999994);
EXPECT_EQ(-0.0, res);
- res = reinterpret_cast<DoubleToDoubleRoundCode>(entry)(9007199254740991.0);
+ res = reinterpret_cast<DoubleToDoubleRoundCode>(
+ test->entry())(9007199254740991.0);
EXPECT_EQ(9007199254740991.0, res);
- res = reinterpret_cast<DoubleToDoubleRoundCode>(entry)(-9007199254740991.0);
+ res = reinterpret_cast<DoubleToDoubleRoundCode>(
+ test->entry())(-9007199254740991.0);
EXPECT_EQ(-9007199254740991.0, res);
}
@@ -1719,9 +1731,9 @@
}
-ASSEMBLER_TEST_RUN(GlobalAddress, entry) {
+ASSEMBLER_TEST_RUN(GlobalAddress, test) {
typedef double (*GlobalAddressCode)();
- double res = reinterpret_cast<GlobalAddressCode>(entry)();
+ double res = reinterpret_cast<GlobalAddressCode>(test->entry())();
EXPECT_FLOAT_EQ(kDoubleConst, res, 0.000001);
}
@@ -1733,10 +1745,10 @@
}
-ASSEMBLER_TEST_RUN(Sine, entry) {
+ASSEMBLER_TEST_RUN(Sine, test) {
typedef float (*SineCode)(float f);
const float kFloatConst = 0.7;
- float res = reinterpret_cast<SineCode>(entry)(kFloatConst);
+ float res = reinterpret_cast<SineCode>(test->entry())(kFloatConst);
EXPECT_FLOAT_EQ(sin(kFloatConst), res, 0.0001);
}
@@ -1748,10 +1760,10 @@
}
-ASSEMBLER_TEST_RUN(Cosine, entry) {
+ASSEMBLER_TEST_RUN(Cosine, test) {
typedef float (*CosineCode)(float f);
const float kFloatConst = 0.7;
- float res = reinterpret_cast<CosineCode>(entry)(kFloatConst);
+ float res = reinterpret_cast<CosineCode>(test->entry())(kFloatConst);
EXPECT_FLOAT_EQ(cos(kFloatConst), res, 0.0001);
}
@@ -1765,10 +1777,10 @@
}
-ASSEMBLER_TEST_RUN(Tangent, entry) {
+ASSEMBLER_TEST_RUN(Tangent, test) {
typedef double (*TangentCode)(double d);
const double kDoubleConst = 0.6108652375000001;
- double res = reinterpret_cast<TangentCode>(entry)(kDoubleConst);
+ double res = reinterpret_cast<TangentCode>(test->entry())(kDoubleConst);
EXPECT_FLOAT_EQ(tan(kDoubleConst), res, 0.0001);
}
@@ -1784,10 +1796,10 @@
}
-ASSEMBLER_TEST_RUN(SquareRootFloat, entry) {
+ASSEMBLER_TEST_RUN(SquareRootFloat, test) {
typedef float (*SquareRootFloatCode)(float f);
const float kFloatConst = 0.7;
- float res = reinterpret_cast<SquareRootFloatCode>(entry)(kFloatConst);
+ float res = reinterpret_cast<SquareRootFloatCode>(test->entry())(kFloatConst);
EXPECT_FLOAT_EQ(sqrt(kFloatConst), res, 0.0001);
}
@@ -1805,10 +1817,11 @@
}
-ASSEMBLER_TEST_RUN(SquareRootDouble, entry) {
+ASSEMBLER_TEST_RUN(SquareRootDouble, test) {
typedef double (*SquareRootDoubleCode)(double d);
const double kDoubleConst = .7;
- double res = reinterpret_cast<SquareRootDoubleCode>(entry)(kDoubleConst);
+ double res =
+ reinterpret_cast<SquareRootDoubleCode>(test->entry())(kDoubleConst);
EXPECT_FLOAT_EQ(sqrt(kDoubleConst), res, 0.0001);
}
@@ -1824,10 +1837,10 @@
}
-ASSEMBLER_TEST_RUN(FloatNegate, entry) {
+ASSEMBLER_TEST_RUN(FloatNegate, test) {
typedef float (*FloatNegateCode)(float f);
const float kFloatConst = 12.345;
- float res = reinterpret_cast<FloatNegateCode>(entry)(kFloatConst);
+ float res = reinterpret_cast<FloatNegateCode>(test->entry())(kFloatConst);
EXPECT_FLOAT_EQ(-kFloatConst, res, 0.0001);
}
@@ -1845,10 +1858,10 @@
}
-ASSEMBLER_TEST_RUN(DoubleNegate, entry) {
+ASSEMBLER_TEST_RUN(DoubleNegate, test) {
typedef double (*DoubleNegateCode)(double f);
const double kDoubleConst = 12.345;
- double res = reinterpret_cast<DoubleNegateCode>(entry)(kDoubleConst);
+ double res = reinterpret_cast<DoubleNegateCode>(test->entry())(kDoubleConst);
EXPECT_FLOAT_EQ(-kDoubleConst, res, 0.0001);
}
@@ -1861,12 +1874,12 @@
}
-ASSEMBLER_TEST_RUN(LongMulReg, entry) {
+ASSEMBLER_TEST_RUN(LongMulReg, test) {
typedef int64_t (*LongMulRegCode)(int a, int b);
const int a = -12;
const int b = 13;
const int64_t mul_res = a * b;
- int64_t res = reinterpret_cast<LongMulRegCode>(entry)(a, b);
+ int64_t res = reinterpret_cast<LongMulRegCode>(test->entry())(a, b);
EXPECT_EQ(mul_res, res);
}
@@ -1878,12 +1891,12 @@
}
-ASSEMBLER_TEST_RUN(LongMulAddress, entry) {
+ASSEMBLER_TEST_RUN(LongMulAddress, test) {
typedef int64_t (*LongMulAddressCode)(int a, int b);
const int a = -12;
const int b = 13;
const int64_t mul_res = a * b;
- int64_t res = reinterpret_cast<LongMulAddressCode>(entry)(a, b);
+ int64_t res = reinterpret_cast<LongMulAddressCode>(test->entry())(a, b);
EXPECT_EQ(mul_res, res);
}
@@ -1896,16 +1909,16 @@
}
-ASSEMBLER_TEST_RUN(LongUnsignedMulReg, entry) {
+ASSEMBLER_TEST_RUN(LongUnsignedMulReg, test) {
typedef uint64_t (*LongUnsignedMulRegCode)(uint32_t a, uint32_t b);
uint32_t a = 3;
uint32_t b = 13;
uint64_t mul_res = a * b;
- uint64_t res = reinterpret_cast<LongUnsignedMulRegCode>(entry)(a, b);
+ uint64_t res = reinterpret_cast<LongUnsignedMulRegCode>(test->entry())(a, b);
EXPECT_EQ(mul_res, res);
a = 4021288948u;
b = 13;
- res = reinterpret_cast<LongUnsignedMulRegCode>(entry)(a, b);
+ res = reinterpret_cast<LongUnsignedMulRegCode>(test->entry())(a, b);
mul_res = static_cast<uint64_t>(a) * static_cast<uint64_t>(b);
EXPECT_EQ(mul_res, res);
}
@@ -1918,16 +1931,17 @@
}
-ASSEMBLER_TEST_RUN(LongUnsignedMulAddress, entry) {
+ASSEMBLER_TEST_RUN(LongUnsignedMulAddress, test) {
typedef uint64_t (*LongUnsignedMulAddressCode)(uint32_t a, uint32_t b);
uint32_t a = 12;
uint32_t b = 13;
uint64_t mul_res = a * b;
- uint64_t res = reinterpret_cast<LongUnsignedMulAddressCode>(entry)(a, b);
+ uint64_t res =
+ reinterpret_cast<LongUnsignedMulAddressCode>(test->entry())(a, b);
EXPECT_EQ(mul_res, res);
a = 4294967284u;
b = 13;
- res = reinterpret_cast<LongUnsignedMulAddressCode>(entry)(a, b);
+ res = reinterpret_cast<LongUnsignedMulAddressCode>(test->entry())(a, b);
mul_res = static_cast<uint64_t>(a) * static_cast<uint64_t>(b);
EXPECT_EQ(mul_res, res);
}
@@ -1948,15 +1962,15 @@
}
-ASSEMBLER_TEST_RUN(LongAddReg, entry) {
+ASSEMBLER_TEST_RUN(LongAddReg, test) {
typedef int64_t (*LongAddRegCode)(int64_t a, int64_t b);
int64_t a = 12;
int64_t b = 14;
- int64_t res = reinterpret_cast<LongAddRegCode>(entry)(a, b);
+ int64_t res = reinterpret_cast<LongAddRegCode>(test->entry())(a, b);
EXPECT_EQ((a + b), res);
a = 2147483647;
b = 600000;
- res = reinterpret_cast<LongAddRegCode>(entry)(a, b);
+ res = reinterpret_cast<LongAddRegCode>(test->entry())(a, b);
EXPECT_EQ((a + b), res);
}
@@ -1971,15 +1985,15 @@
}
-ASSEMBLER_TEST_RUN(LongAddAddress, entry) {
+ASSEMBLER_TEST_RUN(LongAddAddress, test) {
typedef int64_t (*LongAddAddressCode)(int64_t a, int64_t b);
int64_t a = 12;
int64_t b = 14;
- int64_t res = reinterpret_cast<LongAddAddressCode>(entry)(a, b);
+ int64_t res = reinterpret_cast<LongAddAddressCode>(test->entry())(a, b);
EXPECT_EQ((a + b), res);
a = 2147483647;
b = 600000;
- res = reinterpret_cast<LongAddAddressCode>(entry)(a, b);
+ res = reinterpret_cast<LongAddAddressCode>(test->entry())(a, b);
EXPECT_EQ((a + b), res);
}
@@ -1999,15 +2013,15 @@
}
-ASSEMBLER_TEST_RUN(LongSubReg, entry) {
+ASSEMBLER_TEST_RUN(LongSubReg, test) {
typedef int64_t (*LongSubRegCode)(int64_t a, int64_t b);
int64_t a = 12;
int64_t b = 14;
- int64_t res = reinterpret_cast<LongSubRegCode>(entry)(a, b);
+ int64_t res = reinterpret_cast<LongSubRegCode>(test->entry())(a, b);
EXPECT_EQ((a - b), res);
a = 600000;
b = 2147483647;
- res = reinterpret_cast<LongSubRegCode>(entry)(a, b);
+ res = reinterpret_cast<LongSubRegCode>(test->entry())(a, b);
EXPECT_EQ((a - b), res);
}
@@ -2022,15 +2036,15 @@
}
-ASSEMBLER_TEST_RUN(LongSubAddress, entry) {
+ASSEMBLER_TEST_RUN(LongSubAddress, test) {
typedef int64_t (*LongSubAddressCode)(int64_t a, int64_t b);
int64_t a = 12;
int64_t b = 14;
- int64_t res = reinterpret_cast<LongSubAddressCode>(entry)(a, b);
+ int64_t res = reinterpret_cast<LongSubAddressCode>(test->entry())(a, b);
EXPECT_EQ((a - b), res);
a = 600000;
b = 2147483647;
- res = reinterpret_cast<LongSubAddressCode>(entry)(a, b);
+ res = reinterpret_cast<LongSubAddressCode>(test->entry())(a, b);
EXPECT_EQ((a - b), res);
}
@@ -2056,15 +2070,15 @@
}
-ASSEMBLER_TEST_RUN(LongSubAddress2, entry) {
+ASSEMBLER_TEST_RUN(LongSubAddress2, test) {
typedef int64_t (*LongSubAddress2Code)(int64_t a, int64_t b);
int64_t a = 12;
int64_t b = 14;
- int64_t res = reinterpret_cast<LongSubAddress2Code>(entry)(a, b);
+ int64_t res = reinterpret_cast<LongSubAddress2Code>(test->entry())(a, b);
EXPECT_EQ((a - b), res);
a = 600000;
b = 2147483647;
- res = reinterpret_cast<LongSubAddress2Code>(entry)(a, b);
+ res = reinterpret_cast<LongSubAddress2Code>(test->entry())(a, b);
EXPECT_EQ((a - b), res);
}
@@ -2090,15 +2104,15 @@
}
-ASSEMBLER_TEST_RUN(LongAddAddress2, entry) {
+ASSEMBLER_TEST_RUN(LongAddAddress2, test) {
typedef int64_t (*LongAddAddress2Code)(int64_t a, int64_t b);
int64_t a = 12;
int64_t b = 14;
- int64_t res = reinterpret_cast<LongAddAddress2Code>(entry)(a, b);
+ int64_t res = reinterpret_cast<LongAddAddress2Code>(test->entry())(a, b);
EXPECT_EQ((a + b), res);
a = 600000;
b = 2147483647;
- res = reinterpret_cast<LongAddAddress2Code>(entry)(a, b);
+ res = reinterpret_cast<LongAddAddress2Code>(test->entry())(a, b);
EXPECT_EQ((a + b), res);
}
@@ -2117,10 +2131,11 @@
}
-ASSEMBLER_TEST_RUN(IntegerToDoubleConversion, entry) {
+ASSEMBLER_TEST_RUN(IntegerToDoubleConversion, test) {
typedef double (*IntegerToDoubleConversionCode)(int32_t);
const int32_t val = -12;
- double res = reinterpret_cast<IntegerToDoubleConversionCode>(entry)(val);
+ double res =
+ reinterpret_cast<IntegerToDoubleConversionCode>(test->entry())(val);
EXPECT_FLOAT_EQ(static_cast<double>(val), res, 0.001);
}
@@ -2146,19 +2161,19 @@
}
-ASSEMBLER_TEST_RUN(FPUStoreLong, entry) {
+ASSEMBLER_TEST_RUN(FPUStoreLong, test) {
typedef int64_t (*FPUStoreLongCode)(double d);
double val = 12.2;
- int64_t res = reinterpret_cast<FPUStoreLongCode>(entry)(val);
+ int64_t res = reinterpret_cast<FPUStoreLongCode>(test->entry())(val);
EXPECT_EQ(static_cast<int64_t>(val), res);
val = -12.2;
- res = reinterpret_cast<FPUStoreLongCode>(entry)(val);
+ res = reinterpret_cast<FPUStoreLongCode>(test->entry())(val);
EXPECT_EQ(static_cast<int64_t>(val), res);
val = 12.8;
- res = reinterpret_cast<FPUStoreLongCode>(entry)(val);
+ res = reinterpret_cast<FPUStoreLongCode>(test->entry())(val);
EXPECT_EQ(static_cast<int64_t>(val), res);
val = -12.8;
- res = reinterpret_cast<FPUStoreLongCode>(entry)(val);
+ res = reinterpret_cast<FPUStoreLongCode>(test->entry())(val);
EXPECT_EQ(static_cast<int64_t>(val), res);
}
@@ -2176,9 +2191,9 @@
}
-ASSEMBLER_TEST_RUN(XorpdZeroing, entry) {
+ASSEMBLER_TEST_RUN(XorpdZeroing, test) {
typedef double (*XorpdZeroingCode)(double d);
- double res = reinterpret_cast<XorpdZeroingCode>(entry)(12.56e3);
+ double res = reinterpret_cast<XorpdZeroingCode>(test->entry())(12.56e3);
EXPECT_FLOAT_EQ(0.0, res, 0.0001);
}
@@ -2196,9 +2211,9 @@
}
-ASSEMBLER_TEST_RUN(Pxor, entry) {
+ASSEMBLER_TEST_RUN(Pxor, test) {
typedef double (*PxorCode)(double d);
- double res = reinterpret_cast<PxorCode>(entry)(12.3456e3);
+ double res = reinterpret_cast<PxorCode>(test->entry())(12.3456e3);
EXPECT_FLOAT_EQ(0.0, res, 0.0);
}
@@ -2218,9 +2233,9 @@
}
-ASSEMBLER_TEST_RUN(Orpd, entry) {
+ASSEMBLER_TEST_RUN(Orpd, test) {
typedef double (*OrpdCode)(double d);
- double res = reinterpret_cast<OrpdCode>(entry)(12.56e3);
+ double res = reinterpret_cast<OrpdCode>(test->entry())(12.56e3);
EXPECT_FLOAT_EQ(-12.56e3, res, 0.0);
}
@@ -2234,10 +2249,10 @@
}
-ASSEMBLER_TEST_RUN(Pextrd0, entry) {
+ASSEMBLER_TEST_RUN(Pextrd0, test) {
if (CPUFeatures::sse4_1_supported()) {
typedef int32_t (*PextrdCode0)(double d);
- int32_t res = reinterpret_cast<PextrdCode0>(entry)(123456789);
+ int32_t res = reinterpret_cast<PextrdCode0>(test->entry())(123456789);
EXPECT_EQ(0x54000000, res);
}
}
@@ -2252,10 +2267,10 @@
}
-ASSEMBLER_TEST_RUN(Pextrd1, entry) {
+ASSEMBLER_TEST_RUN(Pextrd1, test) {
if (CPUFeatures::sse4_1_supported()) {
typedef int32_t (*PextrdCode1)(double d);
- int32_t res = reinterpret_cast<PextrdCode1>(entry)(123456789);
+ int32_t res = reinterpret_cast<PextrdCode1>(test->entry())(123456789);
EXPECT_EQ(0x419d6f34, res);
}
}
@@ -2271,10 +2286,10 @@
}
-ASSEMBLER_TEST_RUN(Pmovsxdq, entry) {
+ASSEMBLER_TEST_RUN(Pmovsxdq, test) {
if (CPUFeatures::sse4_1_supported()) {
typedef int32_t (*PmovsxdqCode)(double d);
- int32_t res = reinterpret_cast<PmovsxdqCode>(entry)(123456789);
+ int32_t res = reinterpret_cast<PmovsxdqCode>(test->entry())(123456789);
EXPECT_EQ(0, res);
}
}
@@ -2291,10 +2306,10 @@
}
-ASSEMBLER_TEST_RUN(Pcmpeqq, entry) {
+ASSEMBLER_TEST_RUN(Pcmpeqq, test) {
if (CPUFeatures::sse4_1_supported()) {
typedef int32_t (*PcmpeqqCode)(double d);
- int32_t res = reinterpret_cast<PcmpeqqCode>(entry)(0);
+ int32_t res = reinterpret_cast<PcmpeqqCode>(test->entry())(0);
EXPECT_EQ(-1, res);
}
}
@@ -2313,9 +2328,9 @@
}
-ASSEMBLER_TEST_RUN(AndPd, entry) {
+ASSEMBLER_TEST_RUN(AndPd, test) {
typedef double (*AndpdCode)(double d);
- double res = reinterpret_cast<AndpdCode>(entry)(12.56e3);
+ double res = reinterpret_cast<AndpdCode>(test->entry())(12.56e3);
EXPECT_FLOAT_EQ(12.56e3, res, 0.0);
}
@@ -2330,9 +2345,9 @@
}
-ASSEMBLER_TEST_RUN(Movq, entry) {
+ASSEMBLER_TEST_RUN(Movq, test) {
typedef double (*MovqCode)(double d);
- double res = reinterpret_cast<MovqCode>(entry)(12.34e5);
+ double res = reinterpret_cast<MovqCode>(test->entry())(12.34e5);
EXPECT_FLOAT_EQ(12.34e5, res, 0.0);
}
@@ -2350,13 +2365,13 @@
}
-ASSEMBLER_TEST_RUN(DoubleAbs, entry) {
+ASSEMBLER_TEST_RUN(DoubleAbs, test) {
typedef double (*DoubleAbsCode)(double d);
double val = -12.45;
- double res = reinterpret_cast<DoubleAbsCode>(entry)(val);
+ double res = reinterpret_cast<DoubleAbsCode>(test->entry())(val);
EXPECT_FLOAT_EQ(-val, res, 0.001);
val = 12.45;
- res = reinterpret_cast<DoubleAbsCode>(entry)(val);
+ res = reinterpret_cast<DoubleAbsCode>(test->entry())(val);
EXPECT_FLOAT_EQ(val, res, 0.001);
}
@@ -2369,13 +2384,13 @@
}
-ASSEMBLER_TEST_RUN(ExtractSignBits, entry) {
+ASSEMBLER_TEST_RUN(ExtractSignBits, test) {
typedef int (*ExtractSignBits)(double d);
- int res = reinterpret_cast<ExtractSignBits>(entry)(1.0);
+ int res = reinterpret_cast<ExtractSignBits>(test->entry())(1.0);
EXPECT_EQ(0, res);
- res = reinterpret_cast<ExtractSignBits>(entry)(-1.0);
+ res = reinterpret_cast<ExtractSignBits>(test->entry())(-1.0);
EXPECT_EQ(1, res);
- res = reinterpret_cast<ExtractSignBits>(entry)(-0.0);
+ res = reinterpret_cast<ExtractSignBits>(test->entry())(-0.0);
EXPECT_EQ(1, res);
}
@@ -2400,11 +2415,11 @@
}
-ASSEMBLER_TEST_RUN(ConditionalMovesSign, entry) {
+ASSEMBLER_TEST_RUN(ConditionalMovesSign, test) {
typedef int (*ConditionalMovesSignCode)(int i);
- int res = reinterpret_cast<ConditionalMovesSignCode>(entry)(785);
+ int res = reinterpret_cast<ConditionalMovesSignCode>(test->entry())(785);
EXPECT_EQ(1, res);
- res = reinterpret_cast<ConditionalMovesSignCode>(entry)(-12);
+ res = reinterpret_cast<ConditionalMovesSignCode>(test->entry())(-12);
EXPECT_EQ(-1, res);
}
@@ -2421,9 +2436,9 @@
}
-ASSEMBLER_TEST_RUN(TestLoadDoubleConstant, entry) {
+ASSEMBLER_TEST_RUN(TestLoadDoubleConstant, test) {
typedef double (*TestLoadDoubleConstantCode)();
- double res = reinterpret_cast<TestLoadDoubleConstantCode>(entry)();
+ double res = reinterpret_cast<TestLoadDoubleConstantCode>(test->entry())();
EXPECT_FLOAT_EQ(-12.34, res, 0.0001);
}
@@ -2446,9 +2461,9 @@
}
-ASSEMBLER_TEST_RUN(TestObjectCompare, entry) {
+ASSEMBLER_TEST_RUN(TestObjectCompare, test) {
typedef bool (*TestObjectCompare)();
- bool res = reinterpret_cast<TestObjectCompare>(entry)();
+ bool res = reinterpret_cast<TestObjectCompare>(test->entry())();
EXPECT_EQ(true, res);
}
@@ -2467,9 +2482,9 @@
}
-ASSEMBLER_TEST_RUN(TestNop, entry) {
+ASSEMBLER_TEST_RUN(TestNop, test) {
typedef int (*TestNop)();
- int res = reinterpret_cast<TestNop>(entry)();
+ int res = reinterpret_cast<TestNop>(test->entry())();
EXPECT_EQ(36, res); // 36 nop bytes emitted.
}
@@ -2481,9 +2496,9 @@
}
-ASSEMBLER_TEST_RUN(TestAlign0, entry) {
+ASSEMBLER_TEST_RUN(TestAlign0, test) {
typedef int (*TestAlign0)();
- int res = reinterpret_cast<TestAlign0>(entry)();
+ int res = reinterpret_cast<TestAlign0>(test->entry())();
EXPECT_EQ(0, res); // 0 bytes emitted.
}
@@ -2496,9 +2511,9 @@
}
-ASSEMBLER_TEST_RUN(TestAlign1, entry) {
+ASSEMBLER_TEST_RUN(TestAlign1, test) {
typedef int (*TestAlign1)();
- int res = reinterpret_cast<TestAlign1>(entry)();
+ int res = reinterpret_cast<TestAlign1>(test->entry())();
EXPECT_EQ(4, res); // 4 bytes emitted.
}
@@ -2511,9 +2526,9 @@
}
-ASSEMBLER_TEST_RUN(TestAlign1Offset1, entry) {
+ASSEMBLER_TEST_RUN(TestAlign1Offset1, test) {
typedef int (*TestAlign1Offset1)();
- int res = reinterpret_cast<TestAlign1Offset1>(entry)();
+ int res = reinterpret_cast<TestAlign1Offset1>(test->entry())();
EXPECT_EQ(3, res); // 3 bytes emitted.
}
@@ -2526,9 +2541,9 @@
}
-ASSEMBLER_TEST_RUN(TestAlignLarge, entry) {
+ASSEMBLER_TEST_RUN(TestAlignLarge, test) {
typedef int (*TestAlignLarge)();
- int res = reinterpret_cast<TestAlignLarge>(entry)();
+ int res = reinterpret_cast<TestAlignLarge>(test->entry())();
EXPECT_EQ(16, res); // 16 bytes emitted.
}
diff --git a/runtime/vm/assembler_mips.h b/runtime/vm/assembler_mips.h
index 9ad8b88..1c72fd8 100644
--- a/runtime/vm/assembler_mips.h
+++ b/runtime/vm/assembler_mips.h
@@ -115,7 +115,11 @@
class Assembler : public ValueObject {
public:
- Assembler() { UNIMPLEMENTED(); }
+ Assembler()
+ : buffer_(),
+ object_pool_(GrowableObjectArray::Handle()),
+ prologue_offset_(-1),
+ comments_() { }
~Assembler() { }
void PopRegister(Register r) {
@@ -139,6 +143,10 @@
UNIMPLEMENTED();
return *pointer_offsets_;
}
+ const GrowableObjectArray& object_pool() const {
+ UNIMPLEMENTED();
+ return object_pool_;
+ }
void FinalizeInstructions(const MemoryRegion& region) {
UNIMPLEMENTED();
}
@@ -169,6 +177,7 @@
private:
AssemblerBuffer buffer_;
+ GrowableObjectArray& object_pool_; // Objects and patchable jump targets.
ZoneGrowableArray<int>* pointer_offsets_;
int prologue_offset_;
diff --git a/runtime/vm/assembler_mips_test.cc b/runtime/vm/assembler_mips_test.cc
index 8e263f5..dddd1f4 100644
--- a/runtime/vm/assembler_mips_test.cc
+++ b/runtime/vm/assembler_mips_test.cc
@@ -20,9 +20,9 @@
}
-ASSEMBLER_TEST_RUN(Simple, entry) {
+ASSEMBLER_TEST_RUN(Simple, test) {
typedef int (*SimpleCode)();
- EXPECT_EQ(42, EXECUTE_TEST_CODE_INT32(SimpleCode, entry));
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
}
} // namespace dart
diff --git a/runtime/vm/assembler_test.cc b/runtime/vm/assembler_test.cc
index 69228a1..8b1c602 100644
--- a/runtime/vm/assembler_test.cc
+++ b/runtime/vm/assembler_test.cc
@@ -14,11 +14,11 @@
ASSEMBLER_TEST_EXTERN(StoreIntoObject);
-ASSEMBLER_TEST_RUN(StoreIntoObject, entry) {
+ASSEMBLER_TEST_RUN(StoreIntoObject, test) {
typedef void (*StoreData)(RawContext* ctx,
RawObject* value,
RawObject* growable_array);
- StoreData test_code = reinterpret_cast<StoreData>(entry);
+ StoreData test_code = reinterpret_cast<StoreData>(test->entry());
const Array& old_array = Array::Handle(Array::New(3, Heap::kOld));
const Array& new_array = Array::Handle(Array::New(3, Heap::kNew));
diff --git a/runtime/vm/assembler_x64.h b/runtime/vm/assembler_x64.h
index 1bfabf2..64add15 100644
--- a/runtime/vm/assembler_x64.h
+++ b/runtime/vm/assembler_x64.h
@@ -312,7 +312,11 @@
class Assembler : public ValueObject {
public:
- Assembler() : buffer_(), prologue_offset_(-1), comments_() { }
+ Assembler()
+ : buffer_(),
+ object_pool_(GrowableObjectArray::Handle()),
+ prologue_offset_(-1),
+ comments_() { }
~Assembler() { }
static const bool kNearJump = true;
@@ -692,6 +696,7 @@
const ZoneGrowableArray<int>& GetPointerOffsets() const {
return buffer_.pointer_offsets();
}
+ const GrowableObjectArray& object_pool() const { return object_pool_; }
void FinalizeInstructions(const MemoryRegion& region) {
buffer_.FinalizeInstructions(region);
@@ -711,6 +716,7 @@
private:
AssemblerBuffer buffer_;
+ GrowableObjectArray& object_pool_; // Object pool is not used on x64.
int prologue_offset_;
class CodeComment : public ZoneAllocated {
diff --git a/runtime/vm/assembler_x64_test.cc b/runtime/vm/assembler_x64_test.cc
index 8e3f62a..b035b6e 100644
--- a/runtime/vm/assembler_x64_test.cc
+++ b/runtime/vm/assembler_x64_test.cc
@@ -23,9 +23,9 @@
}
-ASSEMBLER_TEST_RUN(ReadArgument, entry) {
+ASSEMBLER_TEST_RUN(ReadArgument, test) {
typedef int64_t (*ReadArgumentCode)(int64_t n);
- ReadArgumentCode id = reinterpret_cast<ReadArgumentCode>(entry);
+ ReadArgumentCode id = reinterpret_cast<ReadArgumentCode>(test->entry());
EXPECT_EQ(42, id(42));
EXPECT_EQ(87, id(87));
static const int64_t kLargeConstant = 0x1234567812345678LL;
@@ -203,7 +203,7 @@
}
-ASSEMBLER_TEST_RUN(AddressingModes, entry) {
+ASSEMBLER_TEST_RUN(AddressingModes, test) {
// Avoid running the code since it is constructed to lead to crashes.
}
@@ -229,11 +229,11 @@
}
-ASSEMBLER_TEST_RUN(JumpAroundCrash, entry) {
- Instr* instr = Instr::At(entry);
+ASSEMBLER_TEST_RUN(JumpAroundCrash, test) {
+ Instr* instr = Instr::At(test->entry());
EXPECT(!instr->IsBreakPoint());
typedef void (*JumpAroundCrashCode)();
- reinterpret_cast<JumpAroundCrashCode>(entry)();
+ reinterpret_cast<JumpAroundCrashCode>(test->entry())();
}
@@ -250,9 +250,9 @@
}
-ASSEMBLER_TEST_RUN(SimpleLoop, entry) {
+ASSEMBLER_TEST_RUN(SimpleLoop, test) {
typedef int (*SimpleLoopCode)();
- EXPECT_EQ(2 * 87, reinterpret_cast<SimpleLoopCode>(entry)());
+ EXPECT_EQ(2 * 87, reinterpret_cast<SimpleLoopCode>(test->entry())());
}
@@ -269,9 +269,9 @@
}
-ASSEMBLER_TEST_RUN(Increment, entry) {
+ASSEMBLER_TEST_RUN(Increment, test) {
typedef int (*IncrementCode)();
- EXPECT_EQ(3, reinterpret_cast<IncrementCode>(entry)());
+ EXPECT_EQ(3, reinterpret_cast<IncrementCode>(test->entry())());
}
@@ -287,9 +287,9 @@
}
-ASSEMBLER_TEST_RUN(IncrementLong, entry) {
+ASSEMBLER_TEST_RUN(IncrementLong, test) {
typedef int64_t (*IncrementCodeLong)();
- EXPECT_EQ(0x100000001, reinterpret_cast<IncrementCodeLong>(entry)());
+ EXPECT_EQ(0x100000001, reinterpret_cast<IncrementCodeLong>(test->entry())());
}
@@ -306,9 +306,9 @@
}
-ASSEMBLER_TEST_RUN(Decrement, entry) {
+ASSEMBLER_TEST_RUN(Decrement, test) {
typedef int (*DecrementCode)();
- EXPECT_EQ(0, reinterpret_cast<DecrementCode>(entry)());
+ EXPECT_EQ(0, reinterpret_cast<DecrementCode>(test->entry())());
}
@@ -324,9 +324,9 @@
}
-ASSEMBLER_TEST_RUN(DecrementLong, entry) {
+ASSEMBLER_TEST_RUN(DecrementLong, test) {
typedef int64_t (*DecrementCodeLong)();
- EXPECT_EQ(0xffffffff, reinterpret_cast<DecrementCodeLong>(entry)());
+ EXPECT_EQ(0xffffffff, reinterpret_cast<DecrementCodeLong>(test->entry())());
}
@@ -339,9 +339,9 @@
}
-ASSEMBLER_TEST_RUN(SignedMultiply, entry) {
+ASSEMBLER_TEST_RUN(SignedMultiply, test) {
typedef int (*SignedMultiply)();
- EXPECT_EQ(8000, reinterpret_cast<SignedMultiply>(entry)());
+ EXPECT_EQ(8000, reinterpret_cast<SignedMultiply>(test->entry())());
}
@@ -371,9 +371,9 @@
}
-ASSEMBLER_TEST_RUN(SignedMultiply64, entry) {
+ASSEMBLER_TEST_RUN(SignedMultiply64, test) {
typedef int64_t (*SignedMultiply64)();
- EXPECT_EQ(32, reinterpret_cast<SignedMultiply64>(entry)());
+ EXPECT_EQ(32, reinterpret_cast<SignedMultiply64>(test->entry())());
}
@@ -396,10 +396,10 @@
}
-ASSEMBLER_TEST_RUN(SignedMultiplyLong, entry) {
+ASSEMBLER_TEST_RUN(SignedMultiplyLong, test) {
typedef int64_t (*SignedMultiplyLong)();
EXPECT_EQ(kProductLargeConstants,
- reinterpret_cast<SignedMultiplyLong>(entry)());
+ reinterpret_cast<SignedMultiplyLong>(test->entry())());
}
@@ -413,9 +413,9 @@
}
-ASSEMBLER_TEST_RUN(OverflowSignedMultiply, entry) {
+ASSEMBLER_TEST_RUN(OverflowSignedMultiply, test) {
typedef int (*OverflowSignedMultiply)();
- EXPECT_EQ(0, reinterpret_cast<OverflowSignedMultiply>(entry)());
+ EXPECT_EQ(0, reinterpret_cast<OverflowSignedMultiply>(test->entry())());
}
@@ -429,9 +429,9 @@
}
-ASSEMBLER_TEST_RUN(SignedMultiply1, entry) {
+ASSEMBLER_TEST_RUN(SignedMultiply1, test) {
typedef int (*SignedMultiply1)();
- EXPECT_EQ(8000, reinterpret_cast<SignedMultiply1>(entry)());
+ EXPECT_EQ(8000, reinterpret_cast<SignedMultiply1>(test->entry())());
}
@@ -445,9 +445,9 @@
}
-ASSEMBLER_TEST_RUN(SignedMultiply2, entry) {
+ASSEMBLER_TEST_RUN(SignedMultiply2, test) {
typedef int (*SignedMultiply2)();
- EXPECT_EQ(2000, reinterpret_cast<SignedMultiply2>(entry)());
+ EXPECT_EQ(2000, reinterpret_cast<SignedMultiply2>(test->entry())());
}
@@ -461,9 +461,9 @@
}
-ASSEMBLER_TEST_RUN(SignedDivide, entry) {
+ASSEMBLER_TEST_RUN(SignedDivide, test) {
typedef int32_t (*SignedDivide)();
- EXPECT_EQ(-87 / 42, reinterpret_cast<SignedDivide>(entry)());
+ EXPECT_EQ(-87 / 42, reinterpret_cast<SignedDivide>(test->entry())());
}
@@ -477,9 +477,10 @@
}
-ASSEMBLER_TEST_RUN(SignedDivideLong, entry) {
+ASSEMBLER_TEST_RUN(SignedDivideLong, test) {
typedef int64_t (*SignedDivideLong)();
- EXPECT_EQ(kLargeConstant / 42, reinterpret_cast<SignedDivideLong>(entry)());
+ EXPECT_EQ(kLargeConstant / 42,
+ reinterpret_cast<SignedDivideLong>(test->entry())());
}
@@ -491,9 +492,9 @@
}
-ASSEMBLER_TEST_RUN(Negate, entry) {
+ASSEMBLER_TEST_RUN(Negate, test) {
typedef int (*Negate)();
- EXPECT_EQ(-42, reinterpret_cast<Negate>(entry)());
+ EXPECT_EQ(-42, reinterpret_cast<Negate>(test->entry())());
}
@@ -508,9 +509,9 @@
}
-ASSEMBLER_TEST_RUN(MoveExtend, entry) {
+ASSEMBLER_TEST_RUN(MoveExtend, test) {
typedef int (*MoveExtend)();
- EXPECT_EQ(0xff - 1 + 0xffff, reinterpret_cast<MoveExtend>(entry)());
+ EXPECT_EQ(0xff - 1 + 0xffff, reinterpret_cast<MoveExtend>(test->entry())());
}
@@ -529,9 +530,10 @@
}
-ASSEMBLER_TEST_RUN(MoveExtendMemory, entry) {
+ASSEMBLER_TEST_RUN(MoveExtendMemory, test) {
typedef int (*MoveExtendMemory)();
- EXPECT_EQ(0xff - 1 + 0xffff, reinterpret_cast<MoveExtendMemory>(entry)());
+ EXPECT_EQ(0xff - 1 + 0xffff,
+ reinterpret_cast<MoveExtendMemory>(test->entry())());
}
@@ -547,9 +549,9 @@
}
-ASSEMBLER_TEST_RUN(MoveWord, entry) {
+ASSEMBLER_TEST_RUN(MoveWord, test) {
typedef int (*MoveWord)();
- EXPECT_EQ(0xffff, reinterpret_cast<MoveWord>(entry)());
+ EXPECT_EQ(0xffff, reinterpret_cast<MoveWord>(test->entry())());
}
@@ -566,9 +568,9 @@
}
-ASSEMBLER_TEST_RUN(MoveWordRex, entry) {
+ASSEMBLER_TEST_RUN(MoveWordRex, test) {
typedef int (*MoveWordRex)();
- EXPECT_EQ(0xffff, reinterpret_cast<MoveWordRex>(entry)());
+ EXPECT_EQ(0xffff, reinterpret_cast<MoveWordRex>(test->entry())());
}
@@ -587,9 +589,9 @@
}
-ASSEMBLER_TEST_RUN(Bitwise, entry) {
+ASSEMBLER_TEST_RUN(Bitwise, test) {
typedef int (*Bitwise)();
- EXPECT_EQ(256 + 1, reinterpret_cast<Bitwise>(entry)());
+ EXPECT_EQ(256 + 1, reinterpret_cast<Bitwise>(test->entry())());
}
@@ -631,9 +633,9 @@
}
-ASSEMBLER_TEST_RUN(Bitwise64, entry) {
+ASSEMBLER_TEST_RUN(Bitwise64, test) {
typedef int (*Bitwise64)();
- EXPECT_EQ(256 + 1, reinterpret_cast<Bitwise64>(entry)());
+ EXPECT_EQ(256 + 1, reinterpret_cast<Bitwise64>(test->entry())());
}
@@ -833,9 +835,9 @@
}
-ASSEMBLER_TEST_RUN(LogicalOps, entry) {
+ASSEMBLER_TEST_RUN(LogicalOps, test) {
typedef int (*LogicalOpsCode)();
- EXPECT_EQ(0, reinterpret_cast<LogicalOpsCode>(entry)());
+ EXPECT_EQ(0, reinterpret_cast<LogicalOpsCode>(test->entry())());
}
@@ -1023,9 +1025,9 @@
}
-ASSEMBLER_TEST_RUN(LogicalOps64, entry) {
+ASSEMBLER_TEST_RUN(LogicalOps64, test) {
typedef int (*LogicalOpsCode)();
- EXPECT_EQ(0, reinterpret_cast<LogicalOpsCode>(entry)());
+ EXPECT_EQ(0, reinterpret_cast<LogicalOpsCode>(test->entry())());
}
@@ -1073,9 +1075,9 @@
}
-ASSEMBLER_TEST_RUN(LogicalTestL, entry) {
+ASSEMBLER_TEST_RUN(LogicalTestL, test) {
typedef int (*LogicalTestCode)();
- EXPECT_EQ(0, reinterpret_cast<LogicalTestCode>(entry)());
+ EXPECT_EQ(0, reinterpret_cast<LogicalTestCode>(test->entry())());
}
@@ -1141,9 +1143,9 @@
}
-ASSEMBLER_TEST_RUN(LogicalTestQ, entry) {
+ASSEMBLER_TEST_RUN(LogicalTestQ, test) {
typedef int (*LogicalTestCode)();
- EXPECT_EQ(0, reinterpret_cast<LogicalTestCode>(entry)());
+ EXPECT_EQ(0, reinterpret_cast<LogicalTestCode>(test->entry())());
}
@@ -1159,9 +1161,9 @@
}
-ASSEMBLER_TEST_RUN(CompareSwapEQ, entry) {
+ASSEMBLER_TEST_RUN(CompareSwapEQ, test) {
typedef int (*CompareSwapEQCode)();
- EXPECT_EQ(0, reinterpret_cast<CompareSwapEQCode>(entry)());
+ EXPECT_EQ(0, reinterpret_cast<CompareSwapEQCode>(test->entry())());
}
@@ -1177,9 +1179,9 @@
}
-ASSEMBLER_TEST_RUN(CompareSwapNEQ, entry) {
+ASSEMBLER_TEST_RUN(CompareSwapNEQ, test) {
typedef int (*CompareSwapNEQCode)();
- EXPECT_EQ(4, reinterpret_cast<CompareSwapNEQCode>(entry)());
+ EXPECT_EQ(4, reinterpret_cast<CompareSwapNEQCode>(test->entry())());
}
@@ -1192,10 +1194,10 @@
}
-ASSEMBLER_TEST_RUN(Exchange, entry) {
+ASSEMBLER_TEST_RUN(Exchange, test) {
typedef int64_t (*Exchange)();
EXPECT_EQ(kAnotherLargeConstant - kLargeConstant,
- reinterpret_cast<Exchange>(entry)());
+ reinterpret_cast<Exchange>(test->entry())());
}
@@ -1205,9 +1207,10 @@
}
-ASSEMBLER_TEST_RUN(LargeConstant, entry) {
+ASSEMBLER_TEST_RUN(LargeConstant, test) {
typedef int64_t (*LargeConstantCode)();
- EXPECT_EQ(kLargeConstant, reinterpret_cast<LargeConstantCode>(entry)());
+ EXPECT_EQ(kLargeConstant,
+ reinterpret_cast<LargeConstantCode>(test->entry())());
}
@@ -1246,9 +1249,9 @@
}
-ASSEMBLER_TEST_RUN(CallSimpleLeaf, entry) {
+ASSEMBLER_TEST_RUN(CallSimpleLeaf, test) {
typedef int (*CallSimpleLeafCode)();
- EXPECT_EQ(42 + 87, reinterpret_cast<CallSimpleLeafCode>(entry)());
+ EXPECT_EQ(42 + 87, reinterpret_cast<CallSimpleLeafCode>(test->entry())());
}
@@ -1265,9 +1268,9 @@
}
-ASSEMBLER_TEST_RUN(JumpSimpleLeaf, entry) {
+ASSEMBLER_TEST_RUN(JumpSimpleLeaf, test) {
typedef int (*JumpSimpleLeafCode)();
- EXPECT_EQ(42, reinterpret_cast<JumpSimpleLeafCode>(entry)());
+ EXPECT_EQ(42, reinterpret_cast<JumpSimpleLeafCode>(test->entry())());
}
@@ -1326,9 +1329,9 @@
}
-ASSEMBLER_TEST_RUN(SingleFPMoves, entry) {
+ASSEMBLER_TEST_RUN(SingleFPMoves, test) {
typedef float (*SingleFPMovesCode)();
- EXPECT_EQ(234, reinterpret_cast<SingleFPMovesCode>(entry)());
+ EXPECT_EQ(234, reinterpret_cast<SingleFPMovesCode>(test->entry())());
}
@@ -1352,9 +1355,9 @@
}
-ASSEMBLER_TEST_RUN(SingleFPMoves2, entry) {
+ASSEMBLER_TEST_RUN(SingleFPMoves2, test) {
typedef float (*SingleFPMoves2Code)();
- EXPECT_EQ(234, reinterpret_cast<SingleFPMoves2Code>(entry)());
+ EXPECT_EQ(234, reinterpret_cast<SingleFPMoves2Code>(test->entry())());
}
@@ -1382,9 +1385,9 @@
}
-ASSEMBLER_TEST_RUN(SingleFPOperations, entry) {
+ASSEMBLER_TEST_RUN(SingleFPOperations, test) {
typedef float (*SingleFPOperationsCode)();
- float res = reinterpret_cast<SingleFPOperationsCode>(entry)();
+ float res = reinterpret_cast<SingleFPOperationsCode>(test->entry())();
EXPECT_FLOAT_EQ(0.0f, res, 0.001f);
}
@@ -1405,9 +1408,9 @@
}
-ASSEMBLER_TEST_RUN(PackedFPOperations, entry) {
+ASSEMBLER_TEST_RUN(PackedFPOperations, test) {
typedef float (*PackedFPOperationsCode)();
- float res = reinterpret_cast<PackedFPOperationsCode>(entry)();
+ float res = reinterpret_cast<PackedFPOperationsCode>(test->entry())();
EXPECT_FLOAT_EQ(14.7f, res, 0.001f);
}
@@ -1427,9 +1430,9 @@
}
-ASSEMBLER_TEST_RUN(PackedFPOperations2, entry) {
+ASSEMBLER_TEST_RUN(PackedFPOperations2, test) {
typedef float (*PackedFPOperations2Code)();
- float res = reinterpret_cast<PackedFPOperations2Code>(entry)();
+ float res = reinterpret_cast<PackedFPOperations2Code>(test->entry())();
EXPECT_FLOAT_EQ(0.0f, res, 0.001f);
}
@@ -1445,9 +1448,9 @@
}
-ASSEMBLER_TEST_RUN(PackedCompareEQ, entry) {
+ASSEMBLER_TEST_RUN(PackedCompareEQ, test) {
typedef uint32_t (*PackedCompareEQCode)();
- uint32_t res = reinterpret_cast<PackedCompareEQCode>(entry)();
+ uint32_t res = reinterpret_cast<PackedCompareEQCode>(test->entry())();
EXPECT_EQ(static_cast<uword>(0x0), res);
}
@@ -1463,9 +1466,9 @@
}
-ASSEMBLER_TEST_RUN(PackedCompareNEQ, entry) {
+ASSEMBLER_TEST_RUN(PackedCompareNEQ, test) {
typedef uint32_t (*PackedCompareNEQCode)();
- uint32_t res = reinterpret_cast<PackedCompareNEQCode>(entry)();
+ uint32_t res = reinterpret_cast<PackedCompareNEQCode>(test->entry())();
EXPECT_EQ(static_cast<uword>(0xFFFFFFFF), res);
}
@@ -1481,9 +1484,9 @@
}
-ASSEMBLER_TEST_RUN(PackedCompareLT, entry) {
+ASSEMBLER_TEST_RUN(PackedCompareLT, test) {
typedef uint32_t (*PackedCompareLTCode)();
- uint32_t res = reinterpret_cast<PackedCompareLTCode>(entry)();
+ uint32_t res = reinterpret_cast<PackedCompareLTCode>(test->entry())();
EXPECT_EQ(static_cast<uword>(0xFFFFFFFF), res);
}
@@ -1499,9 +1502,9 @@
}
-ASSEMBLER_TEST_RUN(PackedCompareLE, entry) {
+ASSEMBLER_TEST_RUN(PackedCompareLE, test) {
typedef uint32_t (*PackedCompareLECode)();
- uint32_t res = reinterpret_cast<PackedCompareLECode>(entry)();
+ uint32_t res = reinterpret_cast<PackedCompareLECode>(test->entry())();
EXPECT_EQ(static_cast<uword>(0xFFFFFFFF), res);
}
@@ -1517,9 +1520,9 @@
}
-ASSEMBLER_TEST_RUN(PackedCompareNLT, entry) {
+ASSEMBLER_TEST_RUN(PackedCompareNLT, test) {
typedef uint32_t (*PackedCompareNLTCode)();
- uint32_t res = reinterpret_cast<PackedCompareNLTCode>(entry)();
+ uint32_t res = reinterpret_cast<PackedCompareNLTCode>(test->entry())();
EXPECT_EQ(static_cast<uword>(0x0), res);
}
@@ -1535,9 +1538,9 @@
}
-ASSEMBLER_TEST_RUN(PackedCompareNLE, entry) {
+ASSEMBLER_TEST_RUN(PackedCompareNLE, test) {
typedef uint32_t (*PackedCompareNLECode)();
- uint32_t res = reinterpret_cast<PackedCompareNLECode>(entry)();
+ uint32_t res = reinterpret_cast<PackedCompareNLECode>(test->entry())();
EXPECT_EQ(static_cast<uword>(0x0), res);
}
@@ -1552,9 +1555,9 @@
}
-ASSEMBLER_TEST_RUN(PackedNegate, entry) {
+ASSEMBLER_TEST_RUN(PackedNegate, test) {
typedef float (*PackedNegateCode)();
- float res = reinterpret_cast<PackedNegateCode>(entry)();
+ float res = reinterpret_cast<PackedNegateCode>(test->entry())();
EXPECT_FLOAT_EQ(-12.3f, res, 0.001f);
}
@@ -1569,9 +1572,9 @@
}
-ASSEMBLER_TEST_RUN(PackedAbsolute, entry) {
+ASSEMBLER_TEST_RUN(PackedAbsolute, test) {
typedef float (*PackedAbsoluteCode)();
- float res = reinterpret_cast<PackedAbsoluteCode>(entry)();
+ float res = reinterpret_cast<PackedAbsoluteCode>(test->entry())();
EXPECT_FLOAT_EQ(15.3f, res, 0.001f);
}
@@ -1584,9 +1587,9 @@
}
-ASSEMBLER_TEST_RUN(PackedSetWZero, entry) {
+ASSEMBLER_TEST_RUN(PackedSetWZero, test) {
typedef float (*PackedSetWZeroCode)();
- float res = reinterpret_cast<PackedSetWZeroCode>(entry)();
+ float res = reinterpret_cast<PackedSetWZeroCode>(test->entry())();
EXPECT_FLOAT_EQ(0.0f, res, 0.001f);
}
@@ -1599,9 +1602,9 @@
}
-ASSEMBLER_TEST_RUN(PackedMin, entry) {
+ASSEMBLER_TEST_RUN(PackedMin, test) {
typedef float (*PackedMinCode)();
- float res = reinterpret_cast<PackedMinCode>(entry)();
+ float res = reinterpret_cast<PackedMinCode>(test->entry())();
EXPECT_FLOAT_EQ(2.0f, res, 0.001f);
}
@@ -1614,9 +1617,9 @@
}
-ASSEMBLER_TEST_RUN(PackedMax, entry) {
+ASSEMBLER_TEST_RUN(PackedMax, test) {
typedef float (*PackedMaxCode)();
- float res = reinterpret_cast<PackedMaxCode>(entry)();
+ float res = reinterpret_cast<PackedMaxCode>(test->entry())();
EXPECT_FLOAT_EQ(4.0f, res, 0.001f);
}
@@ -1648,9 +1651,9 @@
}
-ASSEMBLER_TEST_RUN(PackedLogicalOr, entry) {
+ASSEMBLER_TEST_RUN(PackedLogicalOr, test) {
typedef uint32_t (*PackedLogicalOrCode)();
- uint32_t res = reinterpret_cast<PackedLogicalOrCode>(entry)();
+ uint32_t res = reinterpret_cast<PackedLogicalOrCode>(test->entry())();
EXPECT_EQ(0xFFFFFFFF, res);
}
@@ -1681,9 +1684,9 @@
}
-ASSEMBLER_TEST_RUN(PackedLogicalAnd, entry) {
+ASSEMBLER_TEST_RUN(PackedLogicalAnd, test) {
typedef uint32_t (*PackedLogicalAndCode)();
- uint32_t res = reinterpret_cast<PackedLogicalAndCode>(entry)();
+ uint32_t res = reinterpret_cast<PackedLogicalAndCode>(test->entry())();
EXPECT_EQ(static_cast<uword>(0x0000F000), res);
}
@@ -1707,9 +1710,9 @@
}
-ASSEMBLER_TEST_RUN(PackedLogicalNot, entry) {
+ASSEMBLER_TEST_RUN(PackedLogicalNot, test) {
typedef uint32_t (*PackedLogicalNotCode)();
- uint32_t res = reinterpret_cast<PackedLogicalNotCode>(entry)();
+ uint32_t res = reinterpret_cast<PackedLogicalNotCode>(test->entry())();
EXPECT_EQ(static_cast<uword>(0x0), res);
}
@@ -1768,9 +1771,10 @@
}
-ASSEMBLER_TEST_RUN(DoubleFPMoves, entry) {
+ASSEMBLER_TEST_RUN(DoubleFPMoves, test) {
typedef double (*DoubleFPMovesCode)();
- EXPECT_FLOAT_EQ(1024.67, reinterpret_cast<DoubleFPMovesCode>(entry)(), 0.001);
+ EXPECT_FLOAT_EQ(1024.67,
+ reinterpret_cast<DoubleFPMovesCode>(test->entry())(), 0.001);
}
@@ -1799,9 +1803,9 @@
}
-ASSEMBLER_TEST_RUN(DoubleFPOperations, entry) {
+ASSEMBLER_TEST_RUN(DoubleFPOperations, test) {
typedef double (*SingleFPOperationsCode)();
- double res = reinterpret_cast<SingleFPOperationsCode>(entry)();
+ double res = reinterpret_cast<SingleFPOperationsCode>(test->entry())();
EXPECT_FLOAT_EQ(7.668, res, 0.001);
}
@@ -1816,9 +1820,9 @@
}
-ASSEMBLER_TEST_RUN(Int32ToDoubleConversion, entry) {
+ASSEMBLER_TEST_RUN(Int32ToDoubleConversion, test) {
typedef double (*IntToDoubleConversionCode)();
- double res = reinterpret_cast<IntToDoubleConversionCode>(entry)();
+ double res = reinterpret_cast<IntToDoubleConversionCode>(test->entry())();
EXPECT_FLOAT_EQ(-2.0, res, 0.001);
}
@@ -1833,9 +1837,9 @@
}
-ASSEMBLER_TEST_RUN(Int64ToDoubleConversion, entry) {
+ASSEMBLER_TEST_RUN(Int64ToDoubleConversion, test) {
typedef double (*Int64ToDoubleConversionCode)();
- double res = reinterpret_cast<Int64ToDoubleConversionCode>(entry)();
+ double res = reinterpret_cast<Int64ToDoubleConversionCode>(test->entry())();
EXPECT_FLOAT_EQ(static_cast<double>(12LL << 32), res, 0.001);
}
@@ -1856,9 +1860,9 @@
}
-ASSEMBLER_TEST_RUN(DoubleToInt64Conversion, entry) {
+ASSEMBLER_TEST_RUN(DoubleToInt64Conversion, test) {
typedef int64_t (*DoubleToInt64ConversionCode)();
- int64_t res = reinterpret_cast<DoubleToInt64ConversionCode>(entry)();
+ int64_t res = reinterpret_cast<DoubleToInt64ConversionCode>(test->entry())();
EXPECT_EQ(0, res);
}
@@ -1895,9 +1899,9 @@
}
-ASSEMBLER_TEST_RUN(TestObjectCompare, entry) {
+ASSEMBLER_TEST_RUN(TestObjectCompare, test) {
typedef bool (*TestObjectCompare)();
- bool res = reinterpret_cast<TestObjectCompare>(entry)();
+ bool res = reinterpret_cast<TestObjectCompare>(test->entry())();
EXPECT_EQ(true, res);
}
@@ -1916,9 +1920,9 @@
}
-ASSEMBLER_TEST_RUN(TestNop, entry) {
+ASSEMBLER_TEST_RUN(TestNop, test) {
typedef int (*TestNop)();
- int res = reinterpret_cast<TestNop>(entry)();
+ int res = reinterpret_cast<TestNop>(test->entry())();
EXPECT_EQ(36, res); // 36 nop bytes emitted.
}
@@ -1930,9 +1934,9 @@
}
-ASSEMBLER_TEST_RUN(TestAlign0, entry) {
+ASSEMBLER_TEST_RUN(TestAlign0, test) {
typedef int (*TestAlign0)();
- int res = reinterpret_cast<TestAlign0>(entry)();
+ int res = reinterpret_cast<TestAlign0>(test->entry())();
EXPECT_EQ(0, res); // 0 bytes emitted.
}
@@ -1945,9 +1949,9 @@
}
-ASSEMBLER_TEST_RUN(TestAlign1, entry) {
+ASSEMBLER_TEST_RUN(TestAlign1, test) {
typedef int (*TestAlign1)();
- int res = reinterpret_cast<TestAlign1>(entry)();
+ int res = reinterpret_cast<TestAlign1>(test->entry())();
EXPECT_EQ(4, res); // 4 bytes emitted.
}
@@ -1960,9 +1964,9 @@
}
-ASSEMBLER_TEST_RUN(TestAlign1Offset1, entry) {
+ASSEMBLER_TEST_RUN(TestAlign1Offset1, test) {
typedef int (*TestAlign1Offset1)();
- int res = reinterpret_cast<TestAlign1Offset1>(entry)();
+ int res = reinterpret_cast<TestAlign1Offset1>(test->entry())();
EXPECT_EQ(3, res); // 3 bytes emitted.
}
@@ -1975,9 +1979,9 @@
}
-ASSEMBLER_TEST_RUN(TestAlignLarge, entry) {
+ASSEMBLER_TEST_RUN(TestAlignLarge, test) {
typedef int (*TestAlignLarge)();
- int res = reinterpret_cast<TestAlignLarge>(entry)();
+ int res = reinterpret_cast<TestAlignLarge>(test->entry())();
EXPECT_EQ(16, res); // 16 bytes emitted.
}
@@ -2000,9 +2004,9 @@
}
-ASSEMBLER_TEST_RUN(TestAdds, entry) {
+ASSEMBLER_TEST_RUN(TestAdds, test) {
typedef int (*TestAdds)();
- int res = reinterpret_cast<TestAdds>(entry)();
+ int res = reinterpret_cast<TestAdds>(test->entry())();
EXPECT_EQ(20, res);
}
@@ -2014,9 +2018,9 @@
}
-ASSEMBLER_TEST_RUN(TestNot, entry) {
+ASSEMBLER_TEST_RUN(TestNot, test) {
typedef int (*TestNot)();
- unsigned int res = reinterpret_cast<TestNot>(entry)();
+ unsigned int res = reinterpret_cast<TestNot>(test->entry())();
EXPECT_EQ(0xFFFFFFFF, res);
}
@@ -2030,9 +2034,9 @@
}
-ASSEMBLER_TEST_RUN(XorpdZeroing, entry) {
+ASSEMBLER_TEST_RUN(XorpdZeroing, test) {
typedef double (*XorpdZeroingCode)(double d);
- double res = reinterpret_cast<XorpdZeroingCode>(entry)(12.56e3);
+ double res = reinterpret_cast<XorpdZeroingCode>(test->entry())(12.56e3);
EXPECT_FLOAT_EQ(0.0, res, 0.0001);
}
@@ -2050,9 +2054,9 @@
}
-ASSEMBLER_TEST_RUN(XorpdZeroing2, entry) {
+ASSEMBLER_TEST_RUN(XorpdZeroing2, test) {
typedef double (*XorpdZeroing2Code)(double d);
- double res = reinterpret_cast<XorpdZeroing2Code>(entry)(12.56e3);
+ double res = reinterpret_cast<XorpdZeroing2Code>(test->entry())(12.56e3);
EXPECT_FLOAT_EQ(0.0, res, 0.0001);
}
@@ -2063,9 +2067,9 @@
}
-ASSEMBLER_TEST_RUN(Pxor, entry) {
+ASSEMBLER_TEST_RUN(Pxor, test) {
typedef double (*PxorCode)(double d);
- double res = reinterpret_cast<PxorCode>(entry)(12.3456e3);
+ double res = reinterpret_cast<PxorCode>(test->entry())(12.3456e3);
EXPECT_FLOAT_EQ(0.0, res, 0.0);
}
@@ -2076,10 +2080,11 @@
}
-ASSEMBLER_TEST_RUN(SquareRootDouble, entry) {
+ASSEMBLER_TEST_RUN(SquareRootDouble, test) {
typedef double (*SquareRootDoubleCode)(double d);
const double kDoubleConst = .7;
- double res = reinterpret_cast<SquareRootDoubleCode>(entry)(kDoubleConst);
+ double res =
+ reinterpret_cast<SquareRootDoubleCode>(test->entry())(kDoubleConst);
EXPECT_FLOAT_EQ(sqrt(kDoubleConst), res, 0.0001);
}
@@ -2108,9 +2113,9 @@
}
-ASSEMBLER_TEST_RUN(DoubleFPUStackMoves, entry) {
+ASSEMBLER_TEST_RUN(DoubleFPUStackMoves, test) {
typedef int64_t (*DoubleFPUStackMovesCode)();
- int64_t res = reinterpret_cast<DoubleFPUStackMovesCode>(entry)();
+ int64_t res = reinterpret_cast<DoubleFPUStackMovesCode>(test->entry())();
EXPECT_FLOAT_EQ(1024.67, (bit_cast<double, int64_t>(res)), 0.001);
}
@@ -2127,10 +2132,10 @@
}
-ASSEMBLER_TEST_RUN(Sine, entry) {
+ASSEMBLER_TEST_RUN(Sine, test) {
typedef double (*SineCode)(double d);
const double kDoubleConst = 0.7;
- double res = reinterpret_cast<SineCode>(entry)(kDoubleConst);
+ double res = reinterpret_cast<SineCode>(test->entry())(kDoubleConst);
EXPECT_FLOAT_EQ(sin(kDoubleConst), res, 0.0001);
}
@@ -2147,10 +2152,10 @@
}
-ASSEMBLER_TEST_RUN(Cosine, entry) {
+ASSEMBLER_TEST_RUN(Cosine, test) {
typedef double (*CosineCode)(double f);
const double kDoubleConst = 0.7;
- double res = reinterpret_cast<CosineCode>(entry)(kDoubleConst);
+ double res = reinterpret_cast<CosineCode>(test->entry())(kDoubleConst);
EXPECT_FLOAT_EQ(cos(kDoubleConst), res, 0.0001);
}
@@ -2162,9 +2167,9 @@
}
-ASSEMBLER_TEST_RUN(IntToDoubleConversion, entry) {
+ASSEMBLER_TEST_RUN(IntToDoubleConversion, test) {
typedef double (*IntToDoubleConversionCode)();
- double res = reinterpret_cast<IntToDoubleConversionCode>(entry)();
+ double res = reinterpret_cast<IntToDoubleConversionCode>(test->entry())();
EXPECT_FLOAT_EQ(6.0, res, 0.001);
}
@@ -2179,9 +2184,9 @@
}
-ASSEMBLER_TEST_RUN(IntToDoubleConversion2, entry) {
+ASSEMBLER_TEST_RUN(IntToDoubleConversion2, test) {
typedef double (*IntToDoubleConversion2Code)(int i);
- double res = reinterpret_cast<IntToDoubleConversion2Code>(entry)(3);
+ double res = reinterpret_cast<IntToDoubleConversion2Code>(test->entry())(3);
EXPECT_FLOAT_EQ(3.0, res, 0.001);
}
@@ -2191,15 +2196,15 @@
}
-ASSEMBLER_TEST_RUN(DoubleToDoubleTrunc, entry) {
+ASSEMBLER_TEST_RUN(DoubleToDoubleTrunc, test) {
typedef double (*DoubleToDoubleTruncCode)(double d);
- double res = reinterpret_cast<DoubleToDoubleTruncCode>(entry)(12.3);
+ double res = reinterpret_cast<DoubleToDoubleTruncCode>(test->entry())(12.3);
EXPECT_EQ(12.0, res);
- res = reinterpret_cast<DoubleToDoubleTruncCode>(entry)(12.8);
+ res = reinterpret_cast<DoubleToDoubleTruncCode>(test->entry())(12.8);
EXPECT_EQ(12.0, res);
- res = reinterpret_cast<DoubleToDoubleTruncCode>(entry)(-12.3);
+ res = reinterpret_cast<DoubleToDoubleTruncCode>(test->entry())(-12.3);
EXPECT_EQ(-12.0, res);
- res = reinterpret_cast<DoubleToDoubleTruncCode>(entry)(-12.8);
+ res = reinterpret_cast<DoubleToDoubleTruncCode>(test->entry())(-12.8);
EXPECT_EQ(-12.0, res);
}
@@ -2210,13 +2215,13 @@
}
-ASSEMBLER_TEST_RUN(DoubleAbs, entry) {
+ASSEMBLER_TEST_RUN(DoubleAbs, test) {
typedef double (*DoubleAbsCode)(double d);
double val = -12.45;
- double res = reinterpret_cast<DoubleAbsCode>(entry)(val);
+ double res = reinterpret_cast<DoubleAbsCode>(test->entry())(val);
EXPECT_FLOAT_EQ(-val, res, 0.001);
val = 12.45;
- res = reinterpret_cast<DoubleAbsCode>(entry)(val);
+ res = reinterpret_cast<DoubleAbsCode>(test->entry())(val);
EXPECT_FLOAT_EQ(val, res, 0.001);
}
@@ -2227,27 +2232,31 @@
}
-ASSEMBLER_TEST_RUN(DoubleToDoubleRound, entry) {
+ASSEMBLER_TEST_RUN(DoubleToDoubleRound, test) {
typedef double (*DoubleToDoubleRoundCode)(double d);
- double res = reinterpret_cast<DoubleToDoubleRoundCode>(entry)(12.3);
+ double res = reinterpret_cast<DoubleToDoubleRoundCode>(test->entry())(12.3);
EXPECT_EQ(12.0, res);
- res = reinterpret_cast<DoubleToDoubleRoundCode>(entry)(12.8);
+ res = reinterpret_cast<DoubleToDoubleRoundCode>(test->entry())(12.8);
EXPECT_EQ(13.0, res);
- res = reinterpret_cast<DoubleToDoubleRoundCode>(entry)(0.5);
+ res = reinterpret_cast<DoubleToDoubleRoundCode>(test->entry())(0.5);
EXPECT_EQ(1.0, res);
- res = reinterpret_cast<DoubleToDoubleRoundCode>(entry)(-12.3);
+ res = reinterpret_cast<DoubleToDoubleRoundCode>(test->entry())(-12.3);
EXPECT_EQ(-12.0, res);
- res = reinterpret_cast<DoubleToDoubleRoundCode>(entry)(-12.8);
+ res = reinterpret_cast<DoubleToDoubleRoundCode>(test->entry())(-12.8);
EXPECT_EQ(-13.0, res);
- res = reinterpret_cast<DoubleToDoubleRoundCode>(entry)(-0.5);
+ res = reinterpret_cast<DoubleToDoubleRoundCode>(test->entry())(-0.5);
EXPECT_EQ(-1.0, res);
- res = reinterpret_cast<DoubleToDoubleRoundCode>(entry)(0.49999999999999994);
+ res = reinterpret_cast<DoubleToDoubleRoundCode>(
+ test->entry())(0.49999999999999994);
EXPECT_EQ(0.0, res);
- res = reinterpret_cast<DoubleToDoubleRoundCode>(entry)(-0.49999999999999994);
+ res = reinterpret_cast<DoubleToDoubleRoundCode>(
+ test->entry())(-0.49999999999999994);
EXPECT_EQ(-0.0, res);
- res = reinterpret_cast<DoubleToDoubleRoundCode>(entry)(9007199254740991.0);
+ res = reinterpret_cast<DoubleToDoubleRoundCode>(
+ test->entry())(9007199254740991.0);
EXPECT_EQ(9007199254740991.0, res);
- res = reinterpret_cast<DoubleToDoubleRoundCode>(entry)(-9007199254740991.0);
+ res = reinterpret_cast<DoubleToDoubleRoundCode>(
+ test->entry())(-9007199254740991.0);
EXPECT_EQ(-9007199254740991.0, res);
}
@@ -2259,13 +2268,13 @@
}
-ASSEMBLER_TEST_RUN(ExtractSignBits, entry) {
+ASSEMBLER_TEST_RUN(ExtractSignBits, test) {
typedef int (*ExtractSignBits)(double d);
- int res = reinterpret_cast<ExtractSignBits>(entry)(1.0);
+ int res = reinterpret_cast<ExtractSignBits>(test->entry())(1.0);
EXPECT_EQ(0, res);
- res = reinterpret_cast<ExtractSignBits>(entry)(-1.0);
+ res = reinterpret_cast<ExtractSignBits>(test->entry())(-1.0);
EXPECT_EQ(1, res);
- res = reinterpret_cast<ExtractSignBits>(entry)(-0.0);
+ res = reinterpret_cast<ExtractSignBits>(test->entry())(-0.0);
EXPECT_EQ(1, res);
}
diff --git a/runtime/vm/bootstrap.cc b/runtime/vm/bootstrap.cc
index 27f949b..1b1d8d0 100644
--- a/runtime/vm/bootstrap.cc
+++ b/runtime/vm/bootstrap.cc
@@ -56,9 +56,9 @@
}
-RawScript* Bootstrap::LoadMathScript(bool patch) {
- const char* url = patch ? "dart:math-patch" : "dart:math";
- const char* source = patch ? math_patch_ : math_source_;
+RawScript* Bootstrap::LoadCryptoScript(bool patch) {
+ const char* url = patch ? "dart:crypto-patch" : "dart:crypto";
+ const char* source = patch ? crypto_source_ : crypto_source_;
return LoadScript(url, source, patch);
}
@@ -70,6 +70,20 @@
}
+RawScript* Bootstrap::LoadJsonScript(bool patch) {
+ const char* url = patch ? "dart:json-patch" : "dart:json";
+ const char* source = patch ? json_source_ : json_source_;
+ return LoadScript(url, source, patch);
+}
+
+
+RawScript* Bootstrap::LoadMathScript(bool patch) {
+ const char* url = patch ? "dart:math-patch" : "dart:math";
+ const char* source = patch ? math_patch_ : math_source_;
+ return LoadScript(url, source, patch);
+}
+
+
RawScript* Bootstrap::LoadMirrorsScript(bool patch) {
const char* url = patch ? "dart:mirrors-patch" : "dart:mirrors";
const char* source = patch ? mirrors_patch_ : mirrors_source_;
@@ -84,6 +98,20 @@
}
+RawScript* Bootstrap::LoadUriScript(bool patch) {
+ const char* url = patch ? "dart:uri-patch" : "dart:uri";
+ const char* source = patch ? uri_source_ : uri_source_;
+ return LoadScript(url, source, patch);
+}
+
+
+RawScript* Bootstrap::LoadUtfScript(bool patch) {
+ const char* url = patch ? "dart:utf-patch" : "dart:utf";
+ const char* source = patch ? utf_source_ : utf_source_;
+ return LoadScript(url, source, patch);
+}
+
+
RawError* Bootstrap::Compile(const Library& library, const Script& script) {
if (FLAG_print_bootstrap) {
OS::Print("Bootstrap source '%s':\n%s\n",
diff --git a/runtime/vm/bootstrap.h b/runtime/vm/bootstrap.h
index 4c01cca..275c27d 100644
--- a/runtime/vm/bootstrap.h
+++ b/runtime/vm/bootstrap.h
@@ -21,10 +21,14 @@
static RawScript* LoadCoreScript(bool patch);
static RawScript* LoadCollectionScript(bool patch);
static RawScript* LoadCollectionDevScript(bool patch);
- static RawScript* LoadMathScript(bool patch);
+ static RawScript* LoadCryptoScript(bool patch);
static RawScript* LoadIsolateScript(bool patch);
+ static RawScript* LoadJsonScript(bool patch);
+ static RawScript* LoadMathScript(bool patch);
static RawScript* LoadMirrorsScript(bool patch);
static RawScript* LoadScalarlistScript(bool patch);
+ static RawScript* LoadUriScript(bool patch);
+ static RawScript* LoadUtfScript(bool patch);
static RawError* Compile(const Library& library, const Script& script);
static void SetupNativeResolver();
@@ -37,14 +41,18 @@
static const char corelib_patch_[];
static const char collection_source_[];
static const char collection_dev_source_[];
- static const char math_source_[];
- static const char math_patch_[];
+ static const char crypto_source_[];
static const char isolate_source_[];
static const char isolate_patch_[];
+ static const char json_source_[];
+ static const char math_source_[];
+ static const char math_patch_[];
static const char mirrors_source_[];
static const char mirrors_patch_[];
static const char scalarlist_source_[];
static const char scalarlist_patch_[];
+ static const char uri_source_[];
+ static const char utf_source_[];
};
} // namespace dart
diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h
index bcff2ab..dd21318 100644
--- a/runtime/vm/bootstrap_natives.h
+++ b/runtime/vm/bootstrap_natives.h
@@ -14,7 +14,7 @@
// List of bootstrap native entry points used in the core dart library.
#define BOOTSTRAP_NATIVE_LIST(V) \
V(Object_toString, 1) \
- V(Object_noSuchMethod, 5) \
+ V(Object_noSuchMethod, 6) \
V(Object_runtimeType, 1) \
V(Object_instanceOf, 5) \
V(Function_apply, 2) \
diff --git a/runtime/vm/bootstrap_nocorelib.cc b/runtime/vm/bootstrap_nocorelib.cc
index 7093777..d838fc2 100644
--- a/runtime/vm/bootstrap_nocorelib.cc
+++ b/runtime/vm/bootstrap_nocorelib.cc
@@ -39,7 +39,7 @@
}
-RawScript* Bootstrap::LoadMathScript(bool is_patch) {
+RawScript* Bootstrap::LoadCryptoScript(bool is_patch) {
UNREACHABLE();
return Script::null();
}
@@ -51,6 +51,18 @@
}
+RawScript* Bootstrap::LoadJsonScript(bool is_patch) {
+ UNREACHABLE();
+ return Script::null();
+}
+
+
+RawScript* Bootstrap::LoadMathScript(bool is_patch) {
+ UNREACHABLE();
+ return Script::null();
+}
+
+
RawScript* Bootstrap::LoadMirrorsScript(bool is_patch) {
UNREACHABLE();
return Script::null();
@@ -63,6 +75,18 @@
}
+RawScript* Bootstrap::LoadUriScript(bool is_patch) {
+ UNREACHABLE();
+ return Script::null();
+}
+
+
+RawScript* Bootstrap::LoadUtfScript(bool is_patch) {
+ UNREACHABLE();
+ return Script::null();
+}
+
+
RawError* Bootstrap::Compile(const Library& library, const Script& script) {
UNREACHABLE();
return Error::null();
diff --git a/runtime/vm/code_generator.cc b/runtime/vm/code_generator.cc
index 08ff160..75e1a03 100644
--- a/runtime/vm/code_generator.cc
+++ b/runtime/vm/code_generator.cc
@@ -762,8 +762,9 @@
// Before patching verify that we are not repeatedly patching to the same
// target.
ASSERT(target_code.EntryPoint() !=
- CodePatcher::GetStaticCallTargetAt(caller_frame->pc()));
- CodePatcher::PatchStaticCallAt(caller_frame->pc(), target_code.EntryPoint());
+ CodePatcher::GetStaticCallTargetAt(caller_frame->pc(), caller_code));
+ CodePatcher::PatchStaticCallAt(caller_frame->pc(), caller_code,
+ target_code.EntryPoint());
caller_code.SetStaticCallTargetCodeAt(caller_frame->pc(), target_code);
if (FLAG_trace_patching) {
OS::PrintErr("PatchStaticCall: patching from %#"Px" to '%s' %#"Px"\n",
@@ -1356,7 +1357,8 @@
const Function& target_function = Function::Handle(
caller_code.GetStaticCallTargetFunctionAt(frame->pc()));
const Code& target_code = Code::Handle(target_function.CurrentCode());
- CodePatcher::PatchStaticCallAt(frame->pc(), target_code.EntryPoint());
+ CodePatcher::PatchStaticCallAt(frame->pc(), caller_code,
+ target_code.EntryPoint());
caller_code.SetStaticCallTargetCodeAt(frame->pc(), target_code);
if (FLAG_trace_patching) {
OS::PrintErr("FixCallersTarget: patching from %#"Px" to '%s' %#"Px"\n",
diff --git a/runtime/vm/code_patcher.h b/runtime/vm/code_patcher.h
index ce9945f..ddf5c73 100644
--- a/runtime/vm/code_patcher.h
+++ b/runtime/vm/code_patcher.h
@@ -24,11 +24,15 @@
public:
// Dart static calls have a distinct, machine-dependent code pattern.
- // Patch static call to the new target.
- static void PatchStaticCallAt(uword addr, uword new_target_address);
+ // Patch static call before return_address in given code to the new target.
+ static void PatchStaticCallAt(uword return_address,
+ const Code& code,
+ uword new_target_address);
- // Patch instance call to the new target.
- static void PatchInstanceCallAt(uword addr, uword new_target_address);
+ // Patch instance call before return_address in given code to the new target.
+ static void PatchInstanceCallAt(uword return_address,
+ const Code& code,
+ uword new_target_address);
// Patch entry point with a jump as specified in the code's patch region.
static void PatchEntry(const Code& code);
@@ -40,16 +44,15 @@
// that there are no conflicts with object pointers). Used in ASSERTs.
static bool CodeIsPatchable(const Code& code);
- // Returns true if the code before return_address is a static
- // or dynamic Dart call.
- static bool IsDartCall(uword return_address);
-
- static uword GetStaticCallTargetAt(uword return_address);
+ // Return the target address of the static call before return_address
+ // in given code.
+ static uword GetStaticCallTargetAt(uword return_address, const Code& code);
// Get instance call information. Returns the call target and sets each
// of the output parameters ic_data and arguments_descriptor if they are
// non-NULL.
static uword GetInstanceCallAt(uword return_address,
+ const Code& code,
ICData* ic_data,
Array* arguments_descriptor);
diff --git a/runtime/vm/code_patcher_arm.cc b/runtime/vm/code_patcher_arm.cc
index 1297273..90582f8 100644
--- a/runtime/vm/code_patcher_arm.cc
+++ b/runtime/vm/code_patcher_arm.cc
@@ -7,20 +7,30 @@
#include "vm/code_patcher.h"
+#include "vm/object.h"
+
namespace dart {
-uword CodePatcher::GetStaticCallTargetAt(uword return_address) {
+uword CodePatcher::GetStaticCallTargetAt(uword return_address,
+ const Code& code) {
+ ASSERT(code.ContainsInstructionAt(return_address));
UNIMPLEMENTED();
return 0;
}
-void CodePatcher::PatchStaticCallAt(uword return_address, uword new_target) {
+void CodePatcher::PatchStaticCallAt(uword return_address,
+ const Code& code,
+ uword new_target) {
+ ASSERT(code.ContainsInstructionAt(return_address));
UNIMPLEMENTED();
}
-void CodePatcher::PatchInstanceCallAt(uword return_address, uword new_target) {
+void CodePatcher::PatchInstanceCallAt(uword return_address,
+ const Code& code,
+ uword new_target) {
+ ASSERT(code.ContainsInstructionAt(return_address));
UNIMPLEMENTED();
}
@@ -30,15 +40,11 @@
}
-bool CodePatcher::IsDartCall(uword return_address) {
- UNIMPLEMENTED();
- return false;
-}
-
-
uword CodePatcher::GetInstanceCallAt(uword return_address,
+ const Code& code,
ICData* ic_data,
Array* arguments_descriptor) {
+ ASSERT(code.ContainsInstructionAt(return_address));
UNIMPLEMENTED();
return 0;
}
diff --git a/runtime/vm/code_patcher_arm_test.cc b/runtime/vm/code_patcher_arm_test.cc
index fc73e85..5b9d1fb 100644
--- a/runtime/vm/code_patcher_arm_test.cc
+++ b/runtime/vm/code_patcher_arm_test.cc
@@ -49,10 +49,10 @@
}
-ASSEMBLER_TEST_RUN(IcDataAccess, entry) {
- uword return_address = entry + CodePatcher::InstanceCallSizeInBytes();
+ASSEMBLER_TEST_RUN(IcDataAccess, test) {
+ uword return_address = test->entry() + CodePatcher::InstanceCallSizeInBytes();
ICData& ic_data = ICData::Handle();
- CodePatcher::GetInstanceCallAt(return_address, &ic_data, NULL);
+ CodePatcher::GetInstanceCallAt(return_address, test->code(), &ic_data, NULL);
EXPECT_STREQ("targetFunction",
String::Handle(ic_data.target_name()).ToCString());
EXPECT_EQ(1, ic_data.num_args_tested());
diff --git a/runtime/vm/code_patcher_ia32.cc b/runtime/vm/code_patcher_ia32.cc
index 1209cbe..cd21ef2 100644
--- a/runtime/vm/code_patcher_ia32.cc
+++ b/runtime/vm/code_patcher_ia32.cc
@@ -142,19 +142,27 @@
};
-uword CodePatcher::GetStaticCallTargetAt(uword return_address) {
+uword CodePatcher::GetStaticCallTargetAt(uword return_address,
+ const Code& code) {
+ ASSERT(code.ContainsInstructionAt(return_address));
StaticCall call(return_address);
return call.target();
}
-void CodePatcher::PatchStaticCallAt(uword return_address, uword new_target) {
+void CodePatcher::PatchStaticCallAt(uword return_address,
+ const Code& code,
+ uword new_target) {
+ ASSERT(code.ContainsInstructionAt(return_address));
StaticCall call(return_address);
call.set_target(new_target);
}
-void CodePatcher::PatchInstanceCallAt(uword return_address, uword new_target) {
+void CodePatcher::PatchInstanceCallAt(uword return_address,
+ const Code& code,
+ uword new_target) {
+ ASSERT(code.ContainsInstructionAt(return_address));
InstanceCall call(return_address);
call.set_target(new_target);
}
@@ -169,14 +177,11 @@
}
-bool CodePatcher::IsDartCall(uword return_address) {
- return DartCallPattern::IsValid(return_address);
-}
-
-
uword CodePatcher::GetInstanceCallAt(uword return_address,
+ const Code& code,
ICData* ic_data,
Array* arguments_descriptor) {
+ ASSERT(code.ContainsInstructionAt(return_address));
InstanceCall call(return_address);
if (ic_data != NULL) {
*ic_data ^= call.ic_data();
diff --git a/runtime/vm/code_patcher_ia32_test.cc b/runtime/vm/code_patcher_ia32_test.cc
index 9845ab0..2bb6601 100644
--- a/runtime/vm/code_patcher_ia32_test.cc
+++ b/runtime/vm/code_patcher_ia32_test.cc
@@ -70,10 +70,10 @@
}
-ASSEMBLER_TEST_RUN(IcDataAccess, entry) {
- uword return_address = entry + CodePatcher::InstanceCallSizeInBytes();
+ASSEMBLER_TEST_RUN(IcDataAccess, test) {
+ uword return_address = test->entry() + CodePatcher::InstanceCallSizeInBytes();
ICData& ic_data = ICData::Handle();
- CodePatcher::GetInstanceCallAt(return_address, &ic_data, NULL);
+ CodePatcher::GetInstanceCallAt(return_address, test->code(), &ic_data, NULL);
EXPECT_STREQ("targetFunction",
String::Handle(ic_data.target_name()).ToCString());
EXPECT_EQ(1, ic_data.num_args_tested());
diff --git a/runtime/vm/code_patcher_mips.cc b/runtime/vm/code_patcher_mips.cc
index 987589d..a7ed7e0 100644
--- a/runtime/vm/code_patcher_mips.cc
+++ b/runtime/vm/code_patcher_mips.cc
@@ -7,20 +7,30 @@
#include "vm/code_patcher.h"
+#include "vm/object.h"
+
namespace dart {
-uword CodePatcher::GetStaticCallTargetAt(uword return_address) {
+uword CodePatcher::GetStaticCallTargetAt(uword return_address,
+ const Code& code) {
+ ASSERT(code.ContainsInstructionAt(return_address));
UNIMPLEMENTED();
return 0;
}
-void CodePatcher::PatchStaticCallAt(uword return_address, uword new_target) {
+void CodePatcher::PatchStaticCallAt(uword return_address,
+ const Code& code,
+ uword new_target) {
+ ASSERT(code.ContainsInstructionAt(return_address));
UNIMPLEMENTED();
}
-void CodePatcher::PatchInstanceCallAt(uword return_address, uword new_target) {
+void CodePatcher::PatchInstanceCallAt(uword return_address,
+ const Code& code,
+ uword new_target) {
+ ASSERT(code.ContainsInstructionAt(return_address));
UNIMPLEMENTED();
}
@@ -30,15 +40,11 @@
}
-bool CodePatcher::IsDartCall(uword return_address) {
- UNIMPLEMENTED();
- return false;
-}
-
-
uword CodePatcher::GetInstanceCallAt(uword return_address,
+ const Code& code,
ICData* ic_data,
Array* arguments_descriptor) {
+ ASSERT(code.ContainsInstructionAt(return_address));
UNIMPLEMENTED();
return 0;
}
diff --git a/runtime/vm/code_patcher_mips_test.cc b/runtime/vm/code_patcher_mips_test.cc
index 91cf209..79c9b63 100644
--- a/runtime/vm/code_patcher_mips_test.cc
+++ b/runtime/vm/code_patcher_mips_test.cc
@@ -49,10 +49,10 @@
}
-ASSEMBLER_TEST_RUN(IcDataAccess, entry) {
- uword return_address = entry + CodePatcher::InstanceCallSizeInBytes();
+ASSEMBLER_TEST_RUN(IcDataAccess, test) {
+ uword return_address = test->entry() + CodePatcher::InstanceCallSizeInBytes();
ICData& ic_data = ICData::Handle();
- CodePatcher::GetInstanceCallAt(return_address, &ic_data, NULL);
+ CodePatcher::GetInstanceCallAt(return_address, test->code(), &ic_data, NULL);
EXPECT_STREQ("targetFunction",
String::Handle(ic_data.target_name()).ToCString());
EXPECT_EQ(1, ic_data.num_args_tested());
diff --git a/runtime/vm/code_patcher_x64.cc b/runtime/vm/code_patcher_x64.cc
index ab3312e..5be92be 100644
--- a/runtime/vm/code_patcher_x64.cc
+++ b/runtime/vm/code_patcher_x64.cc
@@ -126,32 +126,37 @@
};
-uword CodePatcher::GetStaticCallTargetAt(uword return_address) {
+uword CodePatcher::GetStaticCallTargetAt(uword return_address,
+ const Code& code) {
+ ASSERT(code.ContainsInstructionAt(return_address));
StaticCall call(return_address);
return call.target();
}
-void CodePatcher::PatchStaticCallAt(uword return_address, uword new_target) {
+void CodePatcher::PatchStaticCallAt(uword return_address,
+ const Code& code,
+ uword new_target) {
+ ASSERT(code.ContainsInstructionAt(return_address));
StaticCall call(return_address);
call.set_target(new_target);
}
-void CodePatcher::PatchInstanceCallAt(uword return_address, uword new_target) {
+void CodePatcher::PatchInstanceCallAt(uword return_address,
+ const Code& code,
+ uword new_target) {
+ ASSERT(code.ContainsInstructionAt(return_address));
InstanceCall call(return_address);
call.set_target(new_target);
}
-bool CodePatcher::IsDartCall(uword return_address) {
- return DartCallPattern::IsValid(return_address);
-}
-
-
uword CodePatcher::GetInstanceCallAt(uword return_address,
+ const Code& code,
ICData* ic_data,
Array* arguments_descriptor) {
+ ASSERT(code.ContainsInstructionAt(return_address));
InstanceCall call(return_address);
if (ic_data != NULL) {
*ic_data ^= call.ic_data();
diff --git a/runtime/vm/code_patcher_x64_test.cc b/runtime/vm/code_patcher_x64_test.cc
index eef51c7..61807b3 100644
--- a/runtime/vm/code_patcher_x64_test.cc
+++ b/runtime/vm/code_patcher_x64_test.cc
@@ -70,10 +70,10 @@
}
-ASSEMBLER_TEST_RUN(IcDataAccess, entry) {
- uword return_address = entry + CodePatcher::InstanceCallSizeInBytes();
+ASSEMBLER_TEST_RUN(IcDataAccess, test) {
+ uword return_address = test->entry() + CodePatcher::InstanceCallSizeInBytes();
ICData& ic_data = ICData::Handle();
- CodePatcher::GetInstanceCallAt(return_address, &ic_data, NULL);
+ CodePatcher::GetInstanceCallAt(return_address, test->code(), &ic_data, NULL);
EXPECT_STREQ("targetFunction",
String::Handle(ic_data.target_name()).ToCString());
EXPECT_EQ(1, ic_data.num_args_tested());
diff --git a/runtime/vm/compiler.cc b/runtime/vm/compiler.cc
index 2efb71d..149691b 100644
--- a/runtime/vm/compiler.cc
+++ b/runtime/vm/compiler.cc
@@ -54,7 +54,6 @@
DECLARE_FLAG(bool, print_flow_graph);
DECLARE_FLAG(bool, print_flow_graph_optimized);
DECLARE_FLAG(bool, trace_failed_optimization_attempts);
-DECLARE_FLAG(bool, trace_type_propagation);
// Compile a function. Should call only if the function has not been compiled.
// Arg0: function object.
@@ -160,6 +159,7 @@
isolate);
// Transform to SSA (virtual register 0 and no inlining arguments).
flow_graph->ComputeSSA(0, NULL);
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
}
if (FLAG_print_flow_graph ||
@@ -174,13 +174,15 @@
&CompilerStats::graphoptimizer_timer,
isolate);
- flow_graph->ComputeUseLists();
-
FlowGraphOptimizer optimizer(flow_graph);
optimizer.ApplyICData();
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
- // Compute the use lists.
- flow_graph->ComputeUseLists();
+ // Optimize (a << b) & c patterns. Must occur before
+ // 'SelectRepresentations' which inserts conversion nodes.
+ // TODO(srdjan): Moved before inlining until environment use list can
+ // be used to detect when shift-left is outside the scope of bit-and.
+ optimizer.TryOptimizeLeftShiftWithBitAndPattern();
// Inlining (mutates the flow graph)
if (FLAG_use_inlining) {
@@ -189,64 +191,53 @@
FlowGraphInliner inliner(flow_graph);
inliner.Inline();
// Use lists are maintained and validated by the inliner.
- }
-
- if (FLAG_trace_type_propagation) {
- OS::Print("Before type propagation:\n");
- FlowGraphPrinter printer(*flow_graph);
- printer.PrintBlocks();
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
}
// Propagate types and eliminate more type tests.
if (FLAG_propagate_types) {
FlowGraphTypePropagator propagator(flow_graph);
propagator.Propagate();
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
}
- if (FLAG_trace_type_propagation) {
- OS::Print("After type propagation:\n");
- FlowGraphPrinter printer(*flow_graph);
- printer.PrintBlocks();
- }
-
- flow_graph->ComputeUseLists();
-
// Use propagated class-ids to optimize further.
optimizer.ApplyClassIds();
-
- // Recompute use lists after applying class ids.
- flow_graph->ComputeUseLists();
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
// Do optimizations that depend on the propagated type information.
optimizer.Canonicalize();
-
- flow_graph->ComputeUseLists();
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
if (FLAG_constant_propagation) {
ConstantPropagator::Optimize(flow_graph);
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
// A canonicalization pass to remove e.g. smi checks on smi constants.
optimizer.Canonicalize();
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
}
// Unbox doubles. Performed after constant propagation to minimize
// interference from phis merging double values and tagged
// values comming from dead paths.
- flow_graph->ComputeUseLists();
optimizer.SelectRepresentations();
- flow_graph->ComputeUseLists();
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
if (FLAG_common_subexpression_elimination) {
if (DominatorBasedCSE::Optimize(flow_graph)) {
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
// Do another round of CSE to take secondary effects into account:
// e.g. when eliminating dependent loads (a.x[0] + a.x[0])
// TODO(fschneider): Change to a one-pass optimization pass.
DominatorBasedCSE::Optimize(flow_graph);
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
}
}
if (FLAG_loop_invariant_code_motion &&
(parsed_function.function().deoptimization_counter() <
(FLAG_deoptimization_counter_threshold - 1))) {
LICM::Optimize(flow_graph);
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
}
if (FLAG_range_analysis) {
@@ -254,10 +245,12 @@
// optimistically moves CheckSmi through phis into loop preheaders
// making some phis smi.
optimizer.InferSmiRanges();
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
}
// The final canonicalization pass before the code generation.
optimizer.Canonicalize();
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
// Perform register allocation on the SSA graph.
FlowGraphAllocator allocator(*flow_graph);
diff --git a/runtime/vm/constants_arm.h b/runtime/vm/constants_arm.h
index 747b9a8..1ba6550 100644
--- a/runtime/vm/constants_arm.h
+++ b/runtime/vm/constants_arm.h
@@ -134,7 +134,7 @@
// Register aliases.
const Register TMP = kNoRegister; // No scratch register used by assembler.
const Register CTX = R9; // Caches current context in generated code.
-const Register CP = R10; // Caches constant pool base in generated code.
+const Register PP = R10; // Caches object pool pointer in generated code.
const Register SPREG = SP;
const Register FPREG = FP;
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 1c90a7c..f4a9fa8 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
@@ -2060,7 +2060,85 @@
}
-// TODO(hpayer): value should always be smaller then 0xff. Add error handling.
+static RawObject* ResolveConstructor(const char* current_func,
+ const Class& cls,
+ const String& class_name,
+ const String& dotted_name,
+ int num_args);
+
+
+static RawObject* ThrowArgumentError(const char* exception_message) {
+ Isolate* isolate = Isolate::Current();
+ // Lookup the class ArgumentError in dart:core.
+ const String& lib_url = String::Handle(String::New("dart:core"));
+ const String& class_name =
+ String::Handle(String::New("ArgumentError"));
+ const Library& lib =
+ Library::Handle(isolate, Library::LookupLibrary(lib_url));
+ if (lib.IsNull()) {
+ const String& message = String::Handle(
+ String::NewFormatted("%s: library '%s' not found.",
+ CURRENT_FUNC, lib_url.ToCString()));
+ return ApiError::New(message);
+ }
+ const Class& cls = Class::Handle(isolate,
+ lib.LookupClassAllowPrivate(class_name));
+ if (cls.IsNull()) {
+ const String& message = String::Handle(
+ String::NewFormatted("%s: class '%s' not found in library '%s'.",
+ CURRENT_FUNC, class_name.ToCString(),
+ lib_url.ToCString()));
+ return ApiError::New(message);
+ }
+ String& dot_name = String::Handle(String::New("."));
+ Object& result = Object::Handle(isolate);
+ result = ResolveConstructor(CURRENT_FUNC, cls, class_name, dot_name, 1);
+ if (result.IsError()) return result.raw();
+ ASSERT(result.IsFunction());
+ Function& constructor = Function::Handle(isolate);
+ constructor ^= result.raw();
+ if (!constructor.IsConstructor()) {
+ const String& message = String::Handle(
+ String::NewFormatted("%s: class '%s' is not a constructor.",
+ CURRENT_FUNC, class_name.ToCString()));
+ return ApiError::New(message);
+ }
+ Instance& exception = Instance::Handle(isolate);
+ exception = Instance::New(cls);
+ const Array& args = Array::Handle(isolate, Array::New(3));
+ args.SetAt(0, exception);
+ args.SetAt(1,
+ Smi::Handle(isolate, Smi::New(Function::kCtorPhaseAll)));
+ args.SetAt(2, String::Handle(String::New(exception_message)));
+ result = DartEntry::InvokeStatic(constructor, args);
+ if (result.IsError()) return result.raw();
+ ASSERT(result.IsNull());
+
+ if (isolate->top_exit_frame_info() == 0) {
+ // There are no dart frames on the stack so it would be illegal to
+ // throw an exception here.
+ const String& message = String::Handle(
+ String::New("No Dart frames on stack, cannot throw exception"));
+ return ApiError::New(message);
+ }
+ // Unwind all the API scopes till the exit frame before throwing an
+ // exception.
+ ApiState* state = isolate->api_state();
+ ASSERT(state != NULL);
+ const Instance* saved_exception;
+ {
+ NoGCScope no_gc;
+ RawInstance* raw_exception = exception.raw();
+ state->UnwindScopes(isolate->top_exit_frame_info());
+ saved_exception = &Instance::Handle(raw_exception);
+ }
+ Exceptions::Throw(*saved_exception);
+ const String& message = String::Handle(
+ String::New("Exception was not thrown, internal error"));
+ return ApiError::New(message);
+}
+
+// TODO(sgjesse): value should always be smaller then 0xff. Add error handling.
#define GET_LIST_ELEMENT_AS_BYTES(isolate, type, obj, native_array, offset, \
length) \
const type& array = type::Cast(obj); \
@@ -2069,8 +2147,9 @@
for (int i = 0; i < length; i++) { \
element = array.At(offset + i); \
if (!element.IsInteger()) { \
- return Api::NewError("%s expects the argument 'list' to be " \
- "a List of int", CURRENT_FUNC); \
+ return Api::NewHandle( \
+ isolate, ThrowArgumentError("List contains non-int elements")); \
+ \
} \
const Integer& integer = Integer::Cast(element); \
native_array[i] = static_cast<uint8_t>(integer.AsInt64Value() & 0xff); \
@@ -2240,30 +2319,147 @@
}
-// --- Byte Arrays ---
+// --- Typed Data ---
-DART_EXPORT bool Dart_IsByteArray(Dart_Handle object) {
- return RawObject::IsByteArrayClassId(Api::ClassId(object));
+// Helper method to get the type of a TypedData object.
+static Dart_TypedData_Type GetType(intptr_t class_id) {
+ Dart_TypedData_Type type;
+ switch (class_id) {
+ case kByteArrayCid :
+ type = kByteData;
+ break;
+ case kInt8ArrayCid :
+ case kExternalInt8ArrayCid :
+ type = kInt8;
+ break;
+ case kUint8ArrayCid :
+ case kExternalUint8ArrayCid :
+ type = kUint8;
+ break;
+ case kUint8ClampedArrayCid :
+ case kExternalUint8ClampedArrayCid :
+ type = kUint8Clamped;
+ break;
+ case kInt16ArrayCid :
+ case kExternalInt16ArrayCid :
+ type = kInt16;
+ break;
+ case kUint16ArrayCid :
+ case kExternalUint16ArrayCid :
+ type = kUint16;
+ break;
+ case kInt32ArrayCid :
+ case kExternalInt32ArrayCid :
+ type = kInt32;
+ break;
+ case kUint32ArrayCid :
+ case kExternalUint32ArrayCid :
+ type = kUint32;
+ break;
+ case kInt64ArrayCid :
+ case kExternalInt64ArrayCid :
+ type = kInt64;
+ break;
+ case kUint64ArrayCid :
+ case kExternalUint64ArrayCid :
+ type = kUint64;
+ break;
+ case kFloat32ArrayCid :
+ case kExternalFloat32ArrayCid :
+ type = kFloat32;
+ break;
+ case kFloat64ArrayCid :
+ case kExternalFloat64ArrayCid :
+ type = kFloat64;
+ break;
+ default:
+ type = kInvalid;
+ break;
+ }
+ return type;
}
-DART_EXPORT bool Dart_IsByteArrayExternal(Dart_Handle object) {
- return RawObject::IsExternalByteArrayClassId(Api::ClassId(object));
+DART_EXPORT Dart_TypedData_Type Dart_GetTypeOfTypedData(Dart_Handle object) {
+ intptr_t class_id = Api::ClassId(object);
+ return GetType(class_id);
}
-DART_EXPORT Dart_Handle Dart_NewByteArray(intptr_t length) {
+DART_EXPORT Dart_TypedData_Type Dart_GetTypeOfExternalTypedData(
+ Dart_Handle object) {
+ intptr_t class_id = Api::ClassId(object);
+ if (!RawObject::IsExternalByteArrayClassId(class_id)) {
+ return kInvalid;
+ }
+ return GetType(class_id);
+}
+
+
+template<typename type>
+static Dart_Handle NewTypedData(Isolate* isolate, intptr_t length) {
+ CHECK_LENGTH(length, type::kMaxElements);
+ return Api::NewHandle(isolate, type::New(length));
+}
+
+
+template<typename type, typename datatype>
+static Dart_Handle NewExternalTypedData(
+ void* data,
+ intptr_t length,
+ void* peer,
+ Dart_WeakPersistentHandleFinalizer callback) {
+ CHECK_LENGTH(length, type::kMaxElements);
+ const type& obj =
+ type::Handle(type::New(reinterpret_cast<datatype*>(data), length));
+ return reinterpret_cast<Dart_Handle>(obj.AddFinalizer(peer, callback));
+}
+
+
+DART_EXPORT Dart_Handle Dart_NewTypedData(Dart_TypedData_Type type,
+ intptr_t length) {
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
- CHECK_LENGTH(length, Uint8Array::kMaxElements);
CHECK_CALLBACK_STATE(isolate);
- return Api::NewHandle(isolate, Uint8Array::New(length));
+ switch (type) {
+ case kByteData :
+ // TODO(asiva): Add a new ByteArray::New() method.
+ break;
+ case kInt8 :
+ return NewTypedData<Int8Array>(isolate, length);
+ case kUint8 :
+ return NewTypedData<Uint8Array>(isolate, length);
+ case kUint8Clamped :
+ return NewTypedData<Uint8ClampedArray>(isolate, length);
+ case kInt16 :
+ return NewTypedData<Int16Array>(isolate, length);
+ case kUint16 :
+ return NewTypedData<Uint16Array>(isolate, length);
+ case kInt32 :
+ return NewTypedData<Int32Array>(isolate, length);
+ case kUint32 :
+ return NewTypedData<Uint32Array>(isolate, length);
+ case kInt64 :
+ return NewTypedData<Int64Array>(isolate, length);
+ case kUint64 :
+ return NewTypedData<Uint64Array>(isolate, length);
+ case kFloat32 :
+ return NewTypedData<Float32Array>(isolate, length);
+ case kFloat64 :
+ return NewTypedData<Float64Array>(isolate, length);
+ default:
+ return Api::NewError("%s expects argument 'type' to be of 'TypedData'",
+ CURRENT_FUNC);
+ }
+ UNREACHABLE();
+ return Api::Null(isolate);
}
-DART_EXPORT Dart_Handle Dart_NewExternalByteArray(
- uint8_t* data,
+DART_EXPORT Dart_Handle Dart_NewExternalTypedData(
+ Dart_TypedData_Type type,
+ void* data,
intptr_t length,
void* peer,
Dart_WeakPersistentHandleFinalizer callback) {
@@ -2272,57 +2468,83 @@
if (data == NULL && length != 0) {
RETURN_NULL_ERROR(data);
}
- CHECK_LENGTH(length, ExternalUint8Array::kMaxElements);
CHECK_CALLBACK_STATE(isolate);
- const ExternalUint8Array& obj = ExternalUint8Array::Handle(
- ExternalUint8Array::New(data, length));
- return reinterpret_cast<Dart_Handle>(obj.AddFinalizer(peer, callback));
+ switch (type) {
+ case kByteData :
+ // TODO(asiva): Allocate external ByteData object.
+ break;
+ case kInt8 :
+ return NewExternalTypedData<ExternalInt8Array, int8_t>(data,
+ length,
+ peer,
+ callback);
+ case kUint8 :
+ return NewExternalTypedData<ExternalUint8Array, uint8_t>(data,
+ length,
+ peer,
+ callback);
+ case kUint8Clamped :
+ return NewExternalTypedData<ExternalUint8ClampedArray, uint8_t>(data,
+ length,
+ peer,
+ callback);
+ case kInt16 :
+ return NewExternalTypedData<ExternalInt16Array, int16_t>(data,
+ length,
+ peer,
+ callback);
+ case kUint16 :
+ return NewExternalTypedData<ExternalUint16Array, uint16_t>(data,
+ length,
+ peer,
+ callback);
+ case kInt32 :
+ return NewExternalTypedData<ExternalInt32Array, int32_t>(data,
+ length,
+ peer,
+ callback);
+ case kUint32 :
+ return NewExternalTypedData<ExternalUint32Array, uint32_t>(data,
+ length,
+ peer,
+ callback);
+ case kInt64 :
+ return NewExternalTypedData<ExternalInt64Array, int64_t>(data,
+ length,
+ peer,
+ callback);
+ case kUint64 :
+ return NewExternalTypedData<ExternalUint64Array, uint64_t>(data,
+ length,
+ peer,
+ callback);
+ case kFloat32 :
+ return NewExternalTypedData<ExternalFloat32Array, float>(data,
+ length,
+ peer,
+ callback);
+ case kFloat64 :
+ return NewExternalTypedData<ExternalFloat64Array, double>(data,
+ length,
+ peer,
+ callback);
+ default:
+ return Api::NewError("%s expects argument 'type' to be of"
+ " 'external TypedData'", CURRENT_FUNC);
+ }
+ UNREACHABLE();
+ return Api::Null(isolate);
}
-DART_EXPORT Dart_Handle Dart_NewExternalClampedByteArray(
- uint8_t* data,
- intptr_t length,
- void* peer,
- Dart_WeakPersistentHandleFinalizer callback) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- if (data == NULL && length != 0) {
- RETURN_NULL_ERROR(data);
- }
- CHECK_LENGTH(length, ExternalUint8ClampedArray::kMaxElements);
- CHECK_CALLBACK_STATE(isolate);
- const ExternalUint8ClampedArray& obj = ExternalUint8ClampedArray::Handle(
- ExternalUint8ClampedArray::New(data, length));
- return reinterpret_cast<Dart_Handle>(obj.AddFinalizer(peer, callback));
-}
-
-
-DART_EXPORT Dart_Handle Dart_ExternalByteArrayGetData(Dart_Handle object,
- void** data) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const ExternalUint8Array& array =
- Api::UnwrapExternalUint8ArrayHandle(isolate, object);
- if (array.IsNull()) {
- RETURN_TYPE_ERROR(isolate, object, ExternalUint8Array);
- }
- if (data == NULL) {
- RETURN_NULL_ERROR(data);
- }
- *data = array.GetData();
- return Api::Success(isolate);
-}
-
-
-DART_EXPORT Dart_Handle Dart_ExternalByteArrayGetPeer(Dart_Handle object,
+DART_EXPORT Dart_Handle Dart_ExternalTypedDataGetPeer(Dart_Handle object,
void** peer) {
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
- const ExternalUint8Array& array =
- Api::UnwrapExternalUint8ArrayHandle(isolate, object);
+ const ByteArray& array =
+ Api::UnwrapByteArrayHandle(isolate, object);
if (array.IsNull()) {
- RETURN_TYPE_ERROR(isolate, object, ExternalUint8Array);
+ RETURN_TYPE_ERROR(isolate, object, ByteArray);
}
if (peer == NULL) {
RETURN_NULL_ERROR(peer);
@@ -2332,15 +2554,15 @@
}
-DART_EXPORT Dart_Handle Dart_ScalarListAcquireData(Dart_Handle array,
- Dart_Scalar_Type* type,
- void** data,
- intptr_t* len) {
+DART_EXPORT Dart_Handle Dart_TypedDataAcquireData(Dart_Handle object,
+ Dart_TypedData_Type* type,
+ void** data,
+ intptr_t* len) {
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
- intptr_t class_id = Api::ClassId(array);
+ intptr_t class_id = Api::ClassId(object);
if (!RawObject::IsByteArrayClassId(class_id)) {
- RETURN_TYPE_ERROR(isolate, array, 'scalar list');
+ RETURN_TYPE_ERROR(isolate, object, 'TypedData');
}
if (type == NULL) {
RETURN_NULL_ERROR(type);
@@ -2351,64 +2573,16 @@
if (len == NULL) {
RETURN_NULL_ERROR(len);
}
- // Get the type of typed array.
- switch (class_id) {
- case kByteArrayCid :
- *type = kByteArray;
- break;
- case kInt8ArrayCid :
- case kExternalInt8ArrayCid :
- *type = kInt8;
- break;
- case kUint8ArrayCid :
- case kExternalUint8ArrayCid :
- *type = kUint8;
- break;
- case kUint8ClampedArrayCid :
- case kExternalUint8ClampedArrayCid :
- *type = kUint8Clamped;
- break;
- case kInt16ArrayCid :
- case kExternalInt16ArrayCid :
- *type = kInt16;
- break;
- case kUint16ArrayCid :
- case kExternalUint16ArrayCid :
- *type = kUint16;
- break;
- case kInt32ArrayCid :
- case kExternalInt32ArrayCid :
- *type = kInt32;
- break;
- case kUint32ArrayCid :
- case kExternalUint32ArrayCid :
- *type = kUint32;
- break;
- case kInt64ArrayCid :
- case kExternalInt64ArrayCid :
- *type = kInt64;
- break;
- case kUint64ArrayCid :
- case kExternalUint64ArrayCid :
- *type = kUint64;
- break;
- case kFloat32ArrayCid :
- case kExternalFloat32ArrayCid :
- *type = kFloat32;
- break;
- case kFloat64ArrayCid :
- case kExternalFloat64ArrayCid :
- *type = kFloat64;
- break;
- }
- const ByteArray& obj = Api::UnwrapByteArrayHandle(isolate, array);
+ // Get the type of typed data object.
+ *type = GetType(class_id);
+ const ByteArray& obj = Api::UnwrapByteArrayHandle(isolate, object);
ASSERT(!obj.IsNull());
*len = obj.Length();
- // If it is an external typed array object just return the data field.
+ // If it is an external typed data object just return the data field.
if (RawObject::IsExternalByteArrayClassId(class_id)) {
*data = reinterpret_cast<void*>(obj.ByteAddr(0));
} else {
- // Regular typed array object, set up some GC and API callback guards.
+ // Regular typed data object, set up some GC and API callback guards.
isolate->IncrementNoGCScopeDepth();
START_NO_CALLBACK_SCOPE(isolate);
*data = reinterpret_cast<void*>(obj.ByteAddr(0));
@@ -2417,12 +2591,12 @@
}
-DART_EXPORT Dart_Handle Dart_ScalarListReleaseData(Dart_Handle array) {
+DART_EXPORT Dart_Handle Dart_TypedDataReleaseData(Dart_Handle object) {
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
- intptr_t class_id = Api::ClassId(array);
+ intptr_t class_id = Api::ClassId(object);
if (!RawObject::IsByteArrayClassId(class_id)) {
- RETURN_TYPE_ERROR(isolate, array, 'scalar list');
+ RETURN_TYPE_ERROR(isolate, object, 'TypedData');
}
if (!RawObject::IsExternalByteArrayClassId(class_id)) {
isolate->DecrementNoGCScopeDepth();
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index 0dbbccd..ea91a9e 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -816,11 +816,13 @@
}
-TEST_CASE(ByteArrayAccess) {
- Dart_Handle byte_array1 = Dart_NewByteArray(10);
+TEST_CASE(TypedDataAccess) {
+ EXPECT_EQ(kInvalid, Dart_GetTypeOfTypedData(Dart_True()));
+ EXPECT_EQ(kInvalid, Dart_GetTypeOfExternalTypedData(Dart_False()));
+ Dart_Handle byte_array1 = Dart_NewTypedData(kUint8, 10);
EXPECT_VALID(byte_array1);
- EXPECT(Dart_IsByteArray(byte_array1));
- EXPECT(!Dart_IsByteArrayExternal(byte_array1));
+ EXPECT_EQ(kUint8, Dart_GetTypeOfTypedData(byte_array1));
+ EXPECT_EQ(kInvalid, Dart_GetTypeOfExternalTypedData(byte_array1));
EXPECT(Dart_IsList(byte_array1));
intptr_t length = 0;
@@ -847,7 +849,7 @@
EXPECT_EQ(i + 1, int64_t_value);
}
- Dart_Handle byte_array2 = Dart_NewByteArray(10);
+ Dart_Handle byte_array2 = Dart_NewTypedData(kUint8, 10);
bool is_equal = false;
Dart_ObjectEquals(byte_array1, byte_array2, &is_equal);
EXPECT(!is_equal);
@@ -883,54 +885,54 @@
}
-TEST_CASE(ScalarListDirectAccess) {
+TEST_CASE(TypedDataDirectAccess) {
Dart_Handle str = Dart_NewStringFromCString("junk");
- Dart_Handle byte_array = Dart_NewByteArray(10);
+ Dart_Handle byte_array = Dart_NewTypedData(kUint8, 10);
EXPECT_VALID(byte_array);
Dart_Handle result;
- result = Dart_ScalarListAcquireData(byte_array, NULL, NULL, NULL);
- EXPECT_ERROR(result, "Dart_ScalarListAcquireData expects argument 'type'"
+ result = Dart_TypedDataAcquireData(byte_array, NULL, NULL, NULL);
+ EXPECT_ERROR(result, "Dart_TypedDataAcquireData expects argument 'type'"
" to be non-null.");
- Dart_Scalar_Type type;
- result = Dart_ScalarListAcquireData(byte_array, &type, NULL, NULL);
- EXPECT_ERROR(result, "Dart_ScalarListAcquireData expects argument 'data'"
+ Dart_TypedData_Type type;
+ result = Dart_TypedDataAcquireData(byte_array, &type, NULL, NULL);
+ EXPECT_ERROR(result, "Dart_TypedDataAcquireData expects argument 'data'"
" to be non-null.");
void* data;
- result = Dart_ScalarListAcquireData(byte_array, &type, &data, NULL);
- EXPECT_ERROR(result, "Dart_ScalarListAcquireData expects argument 'len'"
+ result = Dart_TypedDataAcquireData(byte_array, &type, &data, NULL);
+ EXPECT_ERROR(result, "Dart_TypedDataAcquireData expects argument 'len'"
" to be non-null.");
intptr_t len;
- result = Dart_ScalarListAcquireData(Dart_Null(), &type, &data, &len);
- EXPECT_ERROR(result, "Dart_ScalarListAcquireData expects argument 'array'"
+ result = Dart_TypedDataAcquireData(Dart_Null(), &type, &data, &len);
+ EXPECT_ERROR(result, "Dart_TypedDataAcquireData expects argument 'object'"
" to be non-null.");
- result = Dart_ScalarListAcquireData(str, &type, &data, &len);
- EXPECT_ERROR(result, "Dart_ScalarListAcquireData expects argument 'array'"
- " to be of type 'scalar list'.");
+ result = Dart_TypedDataAcquireData(str, &type, &data, &len);
+ EXPECT_ERROR(result, "Dart_TypedDataAcquireData expects argument 'object'"
+ " to be of type 'TypedData'.");
- result = Dart_ScalarListReleaseData(Dart_Null());
- EXPECT_ERROR(result, "Dart_ScalarListReleaseData expects argument 'array'"
+ result = Dart_TypedDataReleaseData(Dart_Null());
+ EXPECT_ERROR(result, "Dart_TypedDataReleaseData expects argument 'object'"
" to be non-null.");
- result = Dart_ScalarListReleaseData(str);
- EXPECT_ERROR(result, "Dart_ScalarListReleaseData expects argument 'array'"
- " to be of type 'scalar list'.");
+ result = Dart_TypedDataReleaseData(str);
+ EXPECT_ERROR(result, "Dart_TypedDataReleaseData expects argument 'object'"
+ " to be of type 'TypedData'.");
}
static void TestDirectAccess(Dart_Handle lib,
Dart_Handle array,
- Dart_Scalar_Type expected_type) {
+ Dart_TypedData_Type expected_type) {
// Invoke the dart function that sets initial values.
Dart_Handle dart_args[1];
dart_args[0] = array;
Dart_Invoke(lib, NewString("setMain"), 1, dart_args);
- // Now Get a direct access to this typed array and check it's contents.
+ // Now Get a direct access to this typed data object and check it's contents.
const int kLength = 10;
Dart_Handle result;
- Dart_Scalar_Type type;
+ Dart_TypedData_Type type;
void* data;
intptr_t len;
- result = Dart_ScalarListAcquireData(array, &type, &data, &len);
+ result = Dart_TypedDataAcquireData(array, &type, &data, &len);
EXPECT_VALID(result);
EXPECT_EQ(expected_type, type);
EXPECT_EQ(kLength, len);
@@ -945,8 +947,8 @@
dataP[i] += 10;
}
- // Release direct accesss to the typed array.
- result = Dart_ScalarListReleaseData(array);
+ // Release direct accesss to the typed data object.
+ result = Dart_TypedDataReleaseData(array);
EXPECT_VALID(result);
// Invoke the dart function in order to check the modified values.
@@ -954,7 +956,7 @@
}
-TEST_CASE(ScalarListDirectAccess1) {
+TEST_CASE(TypedDataDirectAccess1) {
const char* kScriptChars =
"import 'dart:scalarlist';\n"
"void setMain(var a) {"
@@ -974,17 +976,18 @@
// Create a test library and Load up a test script in it.
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
- // Test with an regular typed array object.
+ // Test with an regular typed data object.
Dart_Handle list_access_test_obj;
list_access_test_obj = Dart_Invoke(lib, NewString("main"), 0, NULL);
EXPECT_VALID(list_access_test_obj);
TestDirectAccess(lib, list_access_test_obj, kInt8);
- // Test with an external typed array object.
+ // Test with an external typed data object.
uint8_t data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
intptr_t data_length = ARRAY_SIZE(data);
Dart_Handle ext_list_access_test_obj;
- ext_list_access_test_obj = Dart_NewExternalByteArray(data,
+ ext_list_access_test_obj = Dart_NewExternalTypedData(kUint8,
+ data,
data_length,
NULL, NULL);
EXPECT_VALID(ext_list_access_test_obj);
@@ -992,20 +995,26 @@
}
-static void ExternalByteArrayAccessTests(Dart_Handle obj,
+static void ExternalTypedDataAccessTests(Dart_Handle obj,
+ Dart_TypedData_Type expected_type,
uint8_t data[],
intptr_t data_length) {
EXPECT_VALID(obj);
- EXPECT(Dart_IsByteArray(obj));
- EXPECT(Dart_IsByteArrayExternal(obj));
+ EXPECT_EQ(expected_type, Dart_GetTypeOfTypedData(obj));
+ EXPECT_EQ(expected_type, Dart_GetTypeOfExternalTypedData(obj));
EXPECT(Dart_IsList(obj));
void* raw_data = NULL;
- EXPECT_VALID(Dart_ExternalByteArrayGetData(obj, &raw_data));
+ intptr_t len;
+ Dart_TypedData_Type type;
+ EXPECT_VALID(Dart_TypedDataAcquireData(obj, &type, &raw_data, &len));
EXPECT(raw_data == data);
+ EXPECT_EQ(data_length, len);
+ EXPECT_EQ(expected_type, type);
+ EXPECT_VALID(Dart_TypedDataReleaseData(obj));
void* peer = &data; // just a non-NULL value
- EXPECT_VALID(Dart_ExternalByteArrayGetPeer(obj, &peer));
+ EXPECT_VALID(Dart_ExternalTypedDataGetPeer(obj, &peer));
EXPECT(peer == NULL);
intptr_t list_length = 0;
@@ -1048,22 +1057,25 @@
}
-TEST_CASE(ExternalByteArrayAccess) {
+TEST_CASE(ExternalTypedDataAccess) {
uint8_t data[] = { 0, 11, 22, 33, 44, 55, 66, 77 };
intptr_t data_length = ARRAY_SIZE(data);
- Dart_Handle obj = Dart_NewExternalByteArray(data, data_length, NULL, NULL);
- ExternalByteArrayAccessTests(obj, data, data_length);
+ Dart_Handle obj = Dart_NewExternalTypedData(kUint8,
+ data, data_length,
+ NULL, NULL);
+ ExternalTypedDataAccessTests(obj, kUint8, data, data_length);
}
-TEST_CASE(ExternalClampedByteArrayAccess) {
+TEST_CASE(ExternalClampedTypedDataAccess) {
uint8_t data[] = { 0, 11, 22, 33, 44, 55, 66, 77 };
intptr_t data_length = ARRAY_SIZE(data);
- Dart_Handle obj = Dart_NewExternalClampedByteArray(
- data, data_length, NULL, NULL);
- ExternalByteArrayAccessTests(obj, data, data_length);
+ Dart_Handle obj = Dart_NewExternalTypedData(kUint8Clamped,
+ data, data_length,
+ NULL, NULL);
+ ExternalTypedDataAccessTests(obj, kUint8Clamped, data, data_length);
}
@@ -1082,8 +1094,9 @@
uint8_t data[] = { 0, 11, 22, 33, 44, 55, 66, 77 };
intptr_t data_length = ARRAY_SIZE(data);
- Dart_Handle obj = Dart_NewExternalClampedByteArray(
- data, data_length, NULL, NULL);
+ Dart_Handle obj = Dart_NewExternalTypedData(kUint8Clamped,
+ data, data_length,
+ NULL, NULL);
EXPECT_VALID(obj);
Dart_Handle result;
// Create a test library and Load up a test script in it.
@@ -1102,25 +1115,27 @@
}
-static void ExternalByteArrayCallbackFinalizer(Dart_Handle handle, void* peer) {
+static void ExternalTypedDataCallbackFinalizer(Dart_Handle handle,
+ void* peer) {
Dart_DeletePersistentHandle(handle);
*static_cast<int*>(peer) = 42;
}
-TEST_CASE(ExternalByteArrayCallback) {
+TEST_CASE(ExternalTypedDataCallback) {
int peer = 0;
{
Dart_EnterScope();
uint8_t data[] = { 1, 2, 3, 4 };
- Dart_Handle obj = Dart_NewExternalByteArray(
+ Dart_Handle obj = Dart_NewExternalTypedData(
+ kUint8,
data,
ARRAY_SIZE(data),
&peer,
- ExternalByteArrayCallbackFinalizer);
+ ExternalTypedDataCallbackFinalizer);
EXPECT_VALID(obj);
void* api_peer = NULL;
- EXPECT_VALID(Dart_ExternalByteArrayGetPeer(obj, &api_peer));
+ EXPECT_VALID(Dart_ExternalTypedDataGetPeer(obj, &api_peer));
EXPECT_EQ(api_peer, &peer);
Dart_ExitScope();
}
diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc
index e4924c8..66ae155 100644
--- a/runtime/vm/dart_entry.cc
+++ b/runtime/vm/dart_entry.cc
@@ -326,6 +326,7 @@
RawObject* DartLibraryCalls::ExceptionCreate(const Library& lib,
const String& class_name,
+ const String& constructor_name,
const Array& arguments) {
const Class& cls = Class::Handle(lib.LookupClassAllowPrivate(class_name));
ASSERT(!cls.IsNull());
@@ -343,10 +344,10 @@
constructor_arguments.SetAt((i + kNumExtraArgs), obj);
}
- String& constructor_name = String::Handle(
- String::Concat(class_name, Symbols::Dot()));
- Function& constructor =
- Function::Handle(cls.LookupConstructor(constructor_name));
+ const String& function_name = String::Handle(
+ String::Concat(class_name, constructor_name));
+ const Function& constructor =
+ Function::Handle(cls.LookupConstructorAllowPrivate(function_name));
ASSERT(!constructor.IsNull());
const Object& retval =
Object::Handle(DartEntry::InvokeStatic(constructor, constructor_arguments));
diff --git a/runtime/vm/dart_entry.h b/runtime/vm/dart_entry.h
index 130475c..e4c6d5c 100644
--- a/runtime/vm/dart_entry.h
+++ b/runtime/vm/dart_entry.h
@@ -157,6 +157,7 @@
// On success, returns a RawInstance. On failure, a RawError.
static RawObject* ExceptionCreate(const Library& library,
const String& exception_name,
+ const String& constructor_name,
const Array& arguments);
// On success, returns a RawInstance. On failure, a RawError.
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index 7a68fc2..61a9f69 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -620,16 +620,21 @@
ASSERT(!is_enabled_);
switch (breakpoint_kind_) {
case PcDescriptors::kIcCall: {
+ const Code& code =
+ Code::Handle(Function::Handle(function_).unoptimized_code());
saved_bytes_.target_address_ =
- CodePatcher::GetInstanceCallAt(pc_, NULL, NULL);
- CodePatcher::PatchInstanceCallAt(pc_,
+ CodePatcher::GetInstanceCallAt(pc_, code, NULL, NULL);
+ CodePatcher::PatchInstanceCallAt(pc_, code,
StubCode::BreakpointDynamicEntryPoint());
break;
}
case PcDescriptors::kFuncCall: {
- saved_bytes_.target_address_ = CodePatcher::GetStaticCallTargetAt(pc_);
- CodePatcher::PatchStaticCallAt(pc_,
- StubCode::BreakpointStaticEntryPoint());
+ const Code& code =
+ Code::Handle(Function::Handle(function_).unoptimized_code());
+ saved_bytes_.target_address_ =
+ CodePatcher::GetStaticCallTargetAt(pc_, code);
+ CodePatcher::PatchStaticCallAt(pc_, code,
+ StubCode::BreakpointStaticEntryPoint());
break;
}
case PcDescriptors::kReturn:
@@ -645,12 +650,20 @@
void CodeBreakpoint::RestoreCode() {
ASSERT(is_enabled_);
switch (breakpoint_kind_) {
- case PcDescriptors::kIcCall:
- CodePatcher::PatchInstanceCallAt(pc_, saved_bytes_.target_address_);
+ case PcDescriptors::kIcCall: {
+ const Code& code =
+ Code::Handle(Function::Handle(function_).unoptimized_code());
+ CodePatcher::PatchInstanceCallAt(pc_, code,
+ saved_bytes_.target_address_);
break;
- case PcDescriptors::kFuncCall:
- CodePatcher::PatchStaticCallAt(pc_, saved_bytes_.target_address_);
+ }
+ case PcDescriptors::kFuncCall: {
+ const Code& code =
+ Code::Handle(Function::Handle(function_).unoptimized_code());
+ CodePatcher::PatchStaticCallAt(pc_, code,
+ saved_bytes_.target_address_);
break;
+ }
case PcDescriptors::kReturn:
RestoreFunctionReturn();
break;
@@ -1483,18 +1496,20 @@
func_to_instrument = bpt->function();
ICData& ic_data = ICData::Handle();
Array& descriptor = Array::Handle();
- CodePatcher::GetInstanceCallAt(bpt->pc_, &ic_data, &descriptor);
+ const Code& code =
+ Code::Handle(Function::Handle(bpt->function_).unoptimized_code());
+ CodePatcher::GetInstanceCallAt(bpt->pc_, code, &ic_data, &descriptor);
ArgumentsDescriptor arg_descriptor(descriptor);
ActivationFrame* top_frame = stack_trace->ActivationFrameAt(0);
intptr_t num_args = arg_descriptor.Count();
Instance& receiver =
Instance::Handle(top_frame->GetInstanceCallReceiver(num_args));
- Code& code =
+ Code& target_code =
Code::Handle(ResolveCompileInstanceCallTarget(receiver,
ic_data,
descriptor));
- if (!code.IsNull()) {
- Function& callee = Function::Handle(code.function());
+ if (!target_code.IsNull()) {
+ Function& callee = Function::Handle(target_code.function());
if (IsDebuggable(callee)) {
func_to_instrument = callee.raw();
}
diff --git a/runtime/vm/debugger_api_impl_test.cc b/runtime/vm/debugger_api_impl_test.cc
index bcde541..f33e5f7 100644
--- a/runtime/vm/debugger_api_impl_test.cc
+++ b/runtime/vm/debugger_api_impl_test.cc
@@ -1408,17 +1408,19 @@
EXPECT_EQ(kStackTraceLen, trace_len);
// Frame 0 corresponding to "Object._noSuchMethod".
- Dart_Handle frame0_locals = Dart_NewList(10);
+ Dart_Handle frame0_locals = Dart_NewList(12);
Dart_ListSetAt(frame0_locals, 0, NewString("this"));
Dart_ListSetAt(frame0_locals, 1, Dart_Null());
Dart_ListSetAt(frame0_locals, 2, NewString("isMethod"));
Dart_ListSetAt(frame0_locals, 3, Dart_Null());
Dart_ListSetAt(frame0_locals, 4, NewString("memberName"));
Dart_ListSetAt(frame0_locals, 5, Dart_Null());
- Dart_ListSetAt(frame0_locals, 6, NewString("arguments"));
+ Dart_ListSetAt(frame0_locals, 6, NewString("type"));
Dart_ListSetAt(frame0_locals, 7, Dart_Null());
- Dart_ListSetAt(frame0_locals, 8, NewString("namedArguments"));
+ Dart_ListSetAt(frame0_locals, 8, NewString("arguments"));
Dart_ListSetAt(frame0_locals, 9, Dart_Null());
+ Dart_ListSetAt(frame0_locals, 10, NewString("namedArguments"));
+ Dart_ListSetAt(frame0_locals, 11, Dart_Null());
// Frame 1 corresponding to "Object.noSuchMethod".
Dart_Handle frame1_locals = Dart_NewList(4);
diff --git a/runtime/vm/debuginfo_android.cc b/runtime/vm/debuginfo_android.cc
index 1a11e1b..ee4015c 100644
--- a/runtime/vm/debuginfo_android.cc
+++ b/runtime/vm/debuginfo_android.cc
@@ -2,6 +2,9 @@
// 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/globals.h"
+#if defined(TARGET_OS_ANDROID)
+
#include "vm/debuginfo.h"
#include "vm/elfgen.h"
@@ -70,3 +73,5 @@
}
} // namespace dart
+
+#endif // defined(TARGET_OS_ANDROID)
diff --git a/runtime/vm/debuginfo_linux.cc b/runtime/vm/debuginfo_linux.cc
index ef4b31a..9e73553 100644
--- a/runtime/vm/debuginfo_linux.cc
+++ b/runtime/vm/debuginfo_linux.cc
@@ -2,6 +2,9 @@
// 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/globals.h"
+#if defined(TARGET_OS_LINUX)
+
#include "vm/debuginfo.h"
#include "vm/elfgen.h"
@@ -70,3 +73,5 @@
}
} // namespace dart
+
+#endif // defined(TARGET_OS_LINUX)
diff --git a/runtime/vm/deopt_instructions.cc b/runtime/vm/deopt_instructions.cc
index 035cc65..1e4a614 100644
--- a/runtime/vm/deopt_instructions.cc
+++ b/runtime/vm/deopt_instructions.cc
@@ -269,7 +269,7 @@
// If the deoptimization happened at an IC call, update the IC data
// to avoid repeated deoptimization at the same site next time around.
ICData& ic_data = ICData::Handle();
- CodePatcher::GetInstanceCallAt(pc, &ic_data, NULL);
+ CodePatcher::GetInstanceCallAt(pc, code, &ic_data, NULL);
if (!ic_data.IsNull()) {
ic_data.set_deopt_reason(deopt_context->deopt_reason());
}
diff --git a/runtime/vm/disassembler_arm.cc b/runtime/vm/disassembler_arm.cc
index e88f6ca..b4e862e 100644
--- a/runtime/vm/disassembler_arm.cc
+++ b/runtime/vm/disassembler_arm.cc
@@ -10,11 +10,1208 @@
namespace dart {
+class ARMDecoder : public ValueObject {
+ public:
+ ARMDecoder(char* buffer, size_t buffer_size)
+ : buffer_(buffer),
+ buffer_size_(buffer_size),
+ buffer_pos_(0) {
+ buffer_[buffer_pos_] = '\0';
+ }
+
+ ~ARMDecoder() {}
+
+ // Writes one disassembled instruction into 'buffer' (0-terminated).
+ void InstructionDecode(uword pc);
+
+ private:
+ // Bottleneck functions to print into the out_buffer.
+ void Print(const char* str);
+
+ // Printing of common values.
+ void PrintRegister(int reg);
+ void PrintSRegister(int reg);
+ void PrintDRegister(int reg);
+ void PrintCondition(Instr* instr);
+ void PrintShiftRm(Instr* instr);
+ void PrintShiftImm(Instr* instr);
+ void PrintPU(Instr* instr);
+
+ // Handle formatting of instructions and their options.
+ int FormatRegister(Instr* instr, const char* option);
+ int FormatSRegister(Instr* instr, const char* option);
+ int FormatDRegister(Instr* instr, const char* option);
+ int FormatOption(Instr* instr, const char* option);
+ void Format(Instr* instr, const char* format);
+ void Unknown(Instr* instr);
+
+ // Each of these functions decodes one particular instruction type, a 3-bit
+ // field in the instruction encoding.
+ // Types 0 and 1 are combined as they are largely the same except for the way
+ // they interpret the shifter operand.
+ void DecodeType01(Instr* instr);
+ void DecodeType2(Instr* instr);
+ void DecodeType3(Instr* instr);
+ void DecodeType4(Instr* instr);
+ void DecodeType5(Instr* instr);
+ void DecodeType6(Instr* instr);
+ void DecodeType7(Instr* instr);
+
+ // Convenience functions.
+ char* get_buffer() const { return buffer_; }
+ char* current_position_in_buffer() { return buffer_ + buffer_pos_; }
+ size_t remaining_size_in_buffer() { return buffer_size_ - buffer_pos_; }
+
+ char* buffer_; // Decode instructions into this buffer.
+ size_t buffer_size_; // The size of the character buffer.
+ size_t buffer_pos_; // Current character position in buffer.
+
+ DISALLOW_COPY_AND_ASSIGN(ARMDecoder);
+};
+
+
+// Support for assertions in the ARMDecoder formatting functions.
+#define STRING_STARTS_WITH(string, compare_string) \
+ (strncmp(string, compare_string, strlen(compare_string)) == 0)
+
+
+// Append the str to the output buffer.
+void ARMDecoder::Print(const char* str) {
+ char cur = *str++;
+ while (cur != '\0' && (buffer_pos_ < (buffer_size_ - 1))) {
+ buffer_[buffer_pos_++] = cur;
+ cur = *str++;
+ }
+ buffer_[buffer_pos_] = '\0';
+}
+
+
+// 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 ARMDecoder::PrintCondition(Instr* instr) {
+ Print(cond_names[instr->ConditionField()]);
+}
+
+
+// These register names are defined in a way to match the native disassembler
+// formatting. See for example the command "objdump -d <binary file>".
+static const char* reg_names[kNumberOfCpuRegisters] = {
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "sl", "fp", "ip", "sp", "lr", "pc",
+};
+
+
+// Print the register name according to the active name converter.
+void ARMDecoder::PrintRegister(int reg) {
+ ASSERT(0 <= reg);
+ ASSERT(reg < kNumberOfCpuRegisters);
+ Print(reg_names[reg]);
+}
+
+
+void ARMDecoder::PrintSRegister(int reg) {
+ ASSERT(0 <= reg);
+ ASSERT(reg < kNumberOfSRegisters);
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ "s%d", reg);
+}
+
+
+void ARMDecoder::PrintDRegister(int reg) {
+ ASSERT(0 <= reg);
+ ASSERT(reg < kNumberOfDRegisters);
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ "d%d", reg);
+}
+
+
+// These shift names are defined in a way to match the native disassembler
+// formatting. See for example the command "objdump -d <binary file>".
+static const char* shift_names[kMaxShift] = {
+ "lsl", "lsr", "asr", "ror"
+};
+
+
+// Print the register shift operands for the instruction. Generally used for
+// data processing instructions.
+void ARMDecoder::PrintShiftRm(Instr* instr) {
+ Shift shift = instr->ShiftField();
+ int shift_amount = instr->ShiftAmountField();
+ int rm = instr->RmField();
+
+ PrintRegister(rm);
+
+ if ((instr->RegShiftField() == 0) && (shift == LSL) && (shift_amount == 0)) {
+ // Special case for using rm only.
+ return;
+ }
+ if (instr->RegShiftField() == 0) {
+ // by immediate
+ if ((shift == ROR) && (shift_amount == 0)) {
+ Print(", RRX");
+ return;
+ } else if (((shift == LSR) || (shift == ASR)) && (shift_amount == 0)) {
+ shift_amount = 32;
+ }
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ ", %s #%d",
+ shift_names[shift],
+ shift_amount);
+ } else {
+ // by register
+ int rs = instr->RsField();
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ ", %s ",
+ shift_names[shift]);
+ PrintRegister(rs);
+ }
+}
+
+
+// Print the immediate operand for the instruction. Generally used for data
+// processing instructions.
+void ARMDecoder::PrintShiftImm(Instr* instr) {
+ int rotate = instr->RotateField() * 2;
+ int immed8 = instr->Immed8Field();
+ int imm = (immed8 >> rotate) | (immed8 << (32 - rotate));
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ "#%d",
+ imm);
+}
+
+
+// Print PU formatting to reduce complexity of FormatOption.
+void ARMDecoder::PrintPU(Instr* instr) {
+ switch (instr->PUField()) {
+ case 0: {
+ Print("da");
+ break;
+ }
+ case 1: {
+ Print("ia");
+ break;
+ }
+ case 2: {
+ Print("db");
+ break;
+ }
+ case 3: {
+ Print("ib");
+ break;
+ }
+ default: {
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+// Handle all register based formatting in these functions to reduce the
+// complexity of FormatOption.
+int ARMDecoder::FormatRegister(Instr* instr, const char* format) {
+ ASSERT(format[0] == 'r');
+ if (format[1] == 'n') { // 'rn: Rn register
+ int reg = instr->RnField();
+ PrintRegister(reg);
+ return 2;
+ } else if (format[1] == 'd') { // 'rd: Rd register
+ int reg = instr->RdField();
+ PrintRegister(reg);
+ if (format[2] == '2') { // 'rd2: possibly Rd, Rd+1 register pair
+ if (instr->HasSign() && !instr->HasL()) {
+ if ((reg % 2) != 0) {
+ Print(" *** unknown (odd register pair) ***");
+ } else {
+ Print(", ");
+ PrintRegister(reg + 1);
+ }
+ }
+ return 3;
+ }
+ return 2;
+ } else if (format[1] == 's') { // 'rs: Rs register
+ int reg = instr->RsField();
+ PrintRegister(reg);
+ return 2;
+ } else if (format[1] == 'm') { // 'rm: Rm register
+ int reg = instr->RmField();
+ PrintRegister(reg);
+ return 2;
+ } else if (format[1] == 'l') {
+ // 'rlist: register list for load and store multiple instructions
+ ASSERT(STRING_STARTS_WITH(format, "rlist"));
+ int rlist = instr->RlistField();
+ int reg = 0;
+ Print("{");
+ // Print register list in ascending order, by scanning the bit mask.
+ while (rlist != 0) {
+ if ((rlist & 1) != 0) {
+ PrintRegister(reg);
+ if ((rlist >> 1) != 0) {
+ Print(", ");
+ }
+ }
+ reg++;
+ rlist >>= 1;
+ }
+ Print("}");
+ return 5;
+ }
+ UNREACHABLE();
+ return -1;
+}
+
+
+int ARMDecoder::FormatSRegister(Instr* instr, const char* format) {
+ ASSERT(format[0] == 's');
+ if (format[1] == 'n') { // 'sn: Sn register
+ int reg = instr->SnField();
+ PrintSRegister(reg);
+ return 2;
+ } else if (format[1] == 'd') { // 'sd: Sd register
+ int reg = instr->SdField();
+ PrintSRegister(reg);
+ return 2;
+ } else if (format[1] == 'm') {
+ int reg = instr->SmField();
+ if (format[2] == '1') { // 'sm1: S[m+1] register
+ reg++;
+ ASSERT(reg < kNumberOfSRegisters);
+ PrintSRegister(reg);
+ return 3;
+ } else { // 'sm: Sm register
+ PrintSRegister(reg);
+ return 2;
+ }
+ }
+ UNREACHABLE();
+ return -1;
+}
+
+
+int ARMDecoder::FormatDRegister(Instr* instr, const char* format) {
+ ASSERT(format[0] == 'd');
+ if (format[1] == 'n') { // 'dn: Dn register
+ int reg = instr->DnField();
+ PrintDRegister(reg);
+ return 2;
+ } else if (format[1] == 'd') { // 'dd: Dd register
+ int reg = instr->DdField();
+ PrintDRegister(reg);
+ return 2;
+ } else if (format[1] == 'm') { // 'dm: Dm register
+ int reg = instr->DmField();
+ PrintDRegister(reg);
+ return 2;
+ }
+ UNREACHABLE();
+ return -1;
+}
+
+
+// FormatOption takes a formatting string and interprets it based on
+// the current instructions. The format string points to the first
+// character of the option string (the option escape has already been
+// consumed by the caller.) FormatOption returns the number of
+// characters that were consumed from the formatting string.
+int ARMDecoder::FormatOption(Instr* instr, const char* format) {
+ switch (format[0]) {
+ case 'a': { // 'a: accumulate multiplies
+ if (instr->Bit(21) == 0) {
+ Print("ul");
+ } else {
+ Print("la");
+ }
+ return 1;
+ }
+ case 'b': { // 'b: byte loads or stores
+ if (instr->HasB()) {
+ Print("b");
+ }
+ return 1;
+ }
+ case 'c': { // 'cond: conditional execution
+ ASSERT(STRING_STARTS_WITH(format, "cond"));
+ PrintCondition(instr);
+ return 4;
+ }
+ case 'd': {
+ if (format[1] == 'e') { // 'dest: branch destination
+ ASSERT(STRING_STARTS_WITH(format, "dest"));
+ int off = (instr->SImmed24Field() << 2) + 8;
+ uword destination = reinterpret_cast<uword>(instr) + off;
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ "%#"Px"",
+ destination);
+ return 4;
+ } else {
+ return FormatDRegister(instr, format);
+ }
+ }
+ case 'i': { // 'imm12_4, imm4_12, immf, or immd
+ uint16_t immed16;
+ if (format[3] == 'f') {
+ ASSERT(STRING_STARTS_WITH(format, "immf"));
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ "%f",
+ instr->ImmFloatField());
+ return 4;
+ } else if (format[3] == 'd') {
+ ASSERT(STRING_STARTS_WITH(format, "immd"));
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ "%g",
+ instr->ImmDoubleField());
+ return 4;
+ } else if (format[3] == '1') {
+ ASSERT(STRING_STARTS_WITH(format, "imm12_4"));
+ immed16 = instr->BkptField();
+ } else {
+ ASSERT(STRING_STARTS_WITH(format, "imm4_12"));
+ immed16 = instr->MovwField();
+ }
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ "0x%x",
+ immed16);
+ return 7;
+ }
+ case 'l': { // 'l: branch and link
+ if (instr->HasLink()) {
+ Print("l");
+ }
+ return 1;
+ }
+ case 'm': { // 'memop: load/store instructions
+ ASSERT(STRING_STARTS_WITH(format, "memop"));
+ if (instr->HasL() ||
+ // Extra load/store instructions.
+ ((instr->TypeField() == 0) && instr->HasSign() && !instr->HasH())) {
+ Print("ldr");
+ } else {
+ Print("str");
+ }
+ return 5;
+ }
+ case 'o': {
+ if (format[3] == '1') {
+ if (format[4] == '0') {
+ // 'off10: 10-bit offset for VFP load and store instructions
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ "%d",
+ instr->Bits(0, 8) << 2);
+ } else {
+ // 'off12: 12-bit offset for load and store instructions
+ ASSERT(STRING_STARTS_WITH(format, "off12"));
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ "%d",
+ instr->Offset12Field());
+ }
+ return 5;
+ }
+ // 'off8: 8-bit offset for extra load and store instructions
+ ASSERT(STRING_STARTS_WITH(format, "off8"));
+ int offs8 = (instr->ImmedHField() << 4) | instr->ImmedLField();
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ "%d",
+ offs8);
+ return 4;
+ }
+ case 'p': { // 'pu: P and U bits for load and store instructions
+ ASSERT(STRING_STARTS_WITH(format, "pu"));
+ PrintPU(instr);
+ return 2;
+ }
+ case 'r': {
+ return FormatRegister(instr, format);
+ }
+ case 's': {
+ if (format[1] == 'h') { // 'shift_op or 'shift_rm
+ if (format[6] == 'o') { // 'shift_op
+ ASSERT(STRING_STARTS_WITH(format, "shift_op"));
+ if (instr->TypeField() == 0) {
+ PrintShiftRm(instr);
+ } else {
+ ASSERT(instr->TypeField() == 1);
+ PrintShiftImm(instr);
+ }
+ return 8;
+ } else { // 'shift_rm
+ ASSERT(STRING_STARTS_WITH(format, "shift_rm"));
+ PrintShiftRm(instr);
+ return 8;
+ }
+ } else if (format[1] == 'v') { // 'svc
+ ASSERT(STRING_STARTS_WITH(format, "svc"));
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ "0x%x",
+ instr->SvcField());
+ return 3;
+ } else if (format[1] == ' ') {
+ // 's: S field of data processing instructions
+ if (instr->HasS()) {
+ Print("s");
+ }
+ return 1;
+ } else {
+ return FormatSRegister(instr, format);
+ }
+ }
+ case 't': { // 'target: target of branch instructions
+ ASSERT(STRING_STARTS_WITH(format, "target"));
+ int off = (instr->SImmed24Field() << 2) + 8;
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ "%+d",
+ off);
+ return 6;
+ }
+ case 'u': { // 'u: signed or unsigned multiplies
+ if (instr->Bit(22) == 0) {
+ Print("u");
+ } else {
+ Print("s");
+ }
+ return 1;
+ }
+ case 'w': { // 'w: W field of load and store instructions
+ if (instr->HasW()) {
+ Print("!");
+ }
+ return 1;
+ }
+ case 'x': { // 'x: type of extra load/store instructions
+ if (!instr->HasSign()) {
+ Print("h");
+ } else if (instr->HasL()) {
+ if (instr->HasH()) {
+ Print("sh");
+ } else {
+ Print("sb");
+ }
+ } else {
+ Print("d");
+ }
+ return 1;
+ }
+ default: {
+ UNREACHABLE();
+ break;
+ }
+ }
+ UNREACHABLE();
+ return -1;
+}
+
+
+// Format takes a formatting string for a whole instruction and prints it into
+// the output buffer. All escaped options are handed to FormatOption to be
+// parsed further.
+void ARMDecoder::Format(Instr* instr, const char* format) {
+ char cur = *format++;
+ while ((cur != 0) && (buffer_pos_ < (buffer_size_ - 1))) {
+ if (cur == '\'') { // Single quote is used as the formatting escape.
+ format += FormatOption(instr, format);
+ } else {
+ buffer_[buffer_pos_++] = cur;
+ }
+ cur = *format++;
+ }
+ buffer_[buffer_pos_] = '\0';
+}
+
+
+// For currently unimplemented decodings the disassembler calls Unknown(instr)
+// which will just print "unknown" of the instruction bits.
+void ARMDecoder::Unknown(Instr* instr) {
+ Format(instr, "unknown");
+}
+
+
+void ARMDecoder::DecodeType01(Instr* instr) {
+ if (!instr->IsDataProcessing()) {
+ // miscellaneous, multiply, sync primitives, extra loads and stores.
+ if (instr->IsMiscellaneous()) {
+ switch (instr->Bits(4, 3)) {
+ case 1: {
+ if (instr->Bits(21, 2) == 0x3) {
+ Format(instr, "clz'cond 'rd, 'rm");
+ } else {
+ Unknown(instr);
+ }
+ break;
+ }
+ case 3: {
+ if (instr->Bits(21, 2) == 0x1) {
+ Format(instr, "blx'cond 'rm");
+ } else {
+ // Could be inlined constant.
+ Unknown(instr);
+ }
+ break;
+ }
+ case 7: {
+ if (instr->Bits(21, 2) == 0x1) {
+ Format(instr, "bkpt #'imm12_4");
+ } else {
+ // Format(instr, "smc'cond");
+ Unknown(instr); // Not used.
+ }
+ break;
+ }
+ default: {
+ Unknown(instr); // Not used.
+ break;
+ }
+ }
+ } else if (instr->IsMultiplyOrSyncPrimitive()) {
+ if (instr->Bit(24) == 0) {
+ // multiply instructions
+ switch (instr->Bits(21, 3)) {
+ case 0: {
+ // Assembler registers rd, rn, rm are encoded as rn, rm, rs.
+ Format(instr, "mul'cond's 'rn, 'rm, 'rs");
+ break;
+ }
+ case 1: {
+ // Assembler registers rd, rn, rm, ra are encoded as rn, rm, rs, rd.
+ Format(instr, "mla'cond's 'rn, 'rm, 'rs, 'rd");
+ break;
+ }
+ case 3: {
+ // Assembler registers rd, rn, rm, ra are encoded as rn, rm, rs, rd.
+ Format(instr, "mls'cond's 'rn, 'rm, 'rs, 'rd");
+ break;
+ }
+ case 4: {
+ // Registers rd_lo, rd_hi, rn, rm are encoded as rd, rn, rm, rs.
+ Format(instr, "umull'cond's 'rd, 'rn, 'rm, 'rs");
+ break;
+ }
+ default: {
+ Unknown(instr); // Not used.
+ break;
+ }
+ }
+ } else {
+ // synchronization primitives
+ switch (instr->Bits(20, 4)) {
+ case 8: {
+ Format(instr, "strex'cond 'rd, 'rm, ['rn]");
+ break;
+ }
+ case 9: {
+ Format(instr, "ldrex'cond 'rd, ['rn]");
+ break;
+ }
+ default: {
+ Unknown(instr); // Not used.
+ break;
+ }
+ }
+ }
+ } else if (instr->Bit(25) == 1) {
+ // 16-bit immediate loads, msr (immediate), and hints
+ switch (instr->Bits(20, 5)) {
+ case 16: {
+ Format(instr, "movw'cond 'rd, #'imm4_12");
+ break;
+ }
+ case 18: {
+ if ((instr->Bits(16, 4) == 0) && (instr->Bits(0, 8) == 0)) {
+ Format(instr, "nop'cond");
+ } else {
+ Unknown(instr); // Not used.
+ }
+ break;
+ }
+ case 20: {
+ Format(instr, "movt'cond 'rd, #'imm4_12");
+ break;
+ }
+ default: {
+ Unknown(instr); // Not used.
+ break;
+ }
+ }
+ } else {
+ // extra load/store instructions
+ switch (instr->PUField()) {
+ case 0: {
+ if (instr->Bit(22) == 0) {
+ Format(instr, "'memop'cond'x 'rd2, ['rn], -'rm");
+ } else {
+ Format(instr, "'memop'cond'x 'rd2, ['rn], #-'off8");
+ }
+ break;
+ }
+ case 1: {
+ if (instr->Bit(22) == 0) {
+ Format(instr, "'memop'cond'x 'rd2, ['rn], +'rm");
+ } else {
+ Format(instr, "'memop'cond'x 'rd2, ['rn], #+'off8");
+ }
+ break;
+ }
+ case 2: {
+ if (instr->Bit(22) == 0) {
+ Format(instr, "'memop'cond'x 'rd2, ['rn, -'rm]'w");
+ } else {
+ Format(instr, "'memop'cond'x 'rd2, ['rn, #-'off8]'w");
+ }
+ break;
+ }
+ case 3: {
+ if (instr->Bit(22) == 0) {
+ Format(instr, "'memop'cond'x 'rd2, ['rn, +'rm]'w");
+ } else {
+ Format(instr, "'memop'cond'x 'rd2, ['rn, #+'off8]'w");
+ }
+ break;
+ }
+ default: {
+ // The PU field is a 2-bit field.
+ UNREACHABLE();
+ break;
+ }
+ }
+ }
+ } else {
+ switch (instr->OpcodeField()) {
+ case AND: {
+ Format(instr, "and'cond's 'rd, 'rn, 'shift_op");
+ break;
+ }
+ case EOR: {
+ Format(instr, "eor'cond's 'rd, 'rn, 'shift_op");
+ break;
+ }
+ case SUB: {
+ Format(instr, "sub'cond's 'rd, 'rn, 'shift_op");
+ break;
+ }
+ case RSB: {
+ Format(instr, "rsb'cond's 'rd, 'rn, 'shift_op");
+ break;
+ }
+ case ADD: {
+ Format(instr, "add'cond's 'rd, 'rn, 'shift_op");
+ break;
+ }
+ case ADC: {
+ Format(instr, "adc'cond's 'rd, 'rn, 'shift_op");
+ break;
+ }
+ case SBC: {
+ Format(instr, "sbc'cond's 'rd, 'rn, 'shift_op");
+ break;
+ }
+ case RSC: {
+ Format(instr, "rsc'cond's 'rd, 'rn, 'shift_op");
+ break;
+ }
+ case TST: {
+ if (instr->HasS()) {
+ Format(instr, "tst'cond 'rn, 'shift_op");
+ } else {
+ Unknown(instr); // Not used.
+ }
+ break;
+ }
+ case TEQ: {
+ if (instr->HasS()) {
+ Format(instr, "teq'cond 'rn, 'shift_op");
+ } else {
+ Unknown(instr); // Not used.
+ }
+ break;
+ }
+ case CMP: {
+ if (instr->HasS()) {
+ Format(instr, "cmp'cond 'rn, 'shift_op");
+ } else {
+ Unknown(instr); // Not used.
+ }
+ break;
+ }
+ case CMN: {
+ if (instr->HasS()) {
+ Format(instr, "cmn'cond 'rn, 'shift_op");
+ } else {
+ Unknown(instr); // Not used.
+ }
+ break;
+ }
+ case ORR: {
+ Format(instr, "orr'cond's 'rd, 'rn, 'shift_op");
+ break;
+ }
+ case MOV: {
+ Format(instr, "mov'cond's 'rd, 'shift_op");
+ break;
+ }
+ case BIC: {
+ Format(instr, "bic'cond's 'rd, 'rn, 'shift_op");
+ break;
+ }
+ case MVN: {
+ Format(instr, "mvn'cond's 'rd, 'shift_op");
+ break;
+ }
+ default: {
+ // The Opcode field is a 4-bit field.
+ UNREACHABLE();
+ break;
+ }
+ }
+ }
+}
+
+
+void ARMDecoder::DecodeType2(Instr* instr) {
+ switch (instr->PUField()) {
+ case 0: {
+ if (instr->HasW()) {
+ Unknown(instr); // Not used.
+ } else {
+ Format(instr, "'memop'cond'b 'rd, ['rn], #-'off12");
+ }
+ break;
+ }
+ case 1: {
+ if (instr->HasW()) {
+ Unknown(instr); // Not used.
+ } else {
+ Format(instr, "'memop'cond'b 'rd, ['rn], #+'off12");
+ }
+ break;
+ }
+ case 2: {
+ Format(instr, "'memop'cond'b 'rd, ['rn, #-'off12]'w");
+ break;
+ }
+ case 3: {
+ Format(instr, "'memop'cond'b 'rd, ['rn, #+'off12]'w");
+ break;
+ }
+ default: {
+ // The PU field is a 2-bit field.
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+void ARMDecoder::DecodeType3(Instr* instr) {
+ switch (instr->PUField()) {
+ case 0: {
+ if (instr->HasW()) {
+ Unknown(instr);
+ } else {
+ Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm");
+ }
+ break;
+ }
+ case 1: {
+ if (instr->HasW()) {
+ Unknown(instr);
+ } else {
+ Format(instr, "'memop'cond'b 'rd, ['rn], +'shift_rm");
+ }
+ break;
+ }
+ case 2: {
+ Format(instr, "'memop'cond'b 'rd, ['rn, -'shift_rm]'w");
+ break;
+ }
+ case 3: {
+ Format(instr, "'memop'cond'b 'rd, ['rn, +'shift_rm]'w");
+ break;
+ }
+ default: {
+ // The PU field is a 2-bit field.
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+void ARMDecoder::DecodeType4(Instr* instr) {
+ if (instr->Bit(22) == 1) {
+ Unknown(instr); // Privileged mode currently not supported.
+ } else if (instr->HasL()) {
+ Format(instr, "ldm'cond'pu 'rn'w, 'rlist");
+ } else {
+ Format(instr, "stm'cond'pu 'rn'w, 'rlist");
+ }
+}
+
+
+void ARMDecoder::DecodeType5(Instr* instr) {
+ Format(instr, "b'l'cond 'target ; 'dest");
+}
+
+
+void ARMDecoder::DecodeType6(Instr* instr) {
+ if (instr->IsVFPDoubleTransfer()) {
+ if (instr->Bit(8) == 0) {
+ if (instr->Bit(20) == 1) {
+ Format(instr, "vmovrrs'cond 'rd, 'rn, {'sm, 'sm1}");
+ } else {
+ Format(instr, "vmovsrr'cond {'sm, 'sm1}, 'rd, 'rn");
+ }
+ } else {
+ if (instr->Bit(20) == 1) {
+ Format(instr, "vmovrrd'cond 'rd, 'rn, 'dm");
+ } else {
+ Format(instr, "vmovdrr'cond 'dm, 'rd, 'rn");
+ }
+ }
+ } else if (instr-> IsVFPLoadStore()) {
+ if (instr->Bit(8) == 0) {
+ if (instr->Bit(20) == 1) { // vldrs
+ if (instr->Bit(23) == 1) {
+ Format(instr, "vldrs'cond 'sd, ['rn, #+'off10]");
+ } else {
+ Format(instr, "vldrs'cond 'sd, ['rn, #-'off10]");
+ }
+ } else { // vstrs
+ if (instr->Bit(23) == 1) {
+ Format(instr, "vstrs'cond 'sd, ['rn, #+'off10]");
+ } else {
+ Format(instr, "vstrs'cond 'sd, ['rn, #-'off10]");
+ }
+ }
+ } else {
+ if (instr->Bit(20) == 1) { // vldrd
+ if (instr->Bit(23) == 1) {
+ Format(instr, "vldrd'cond 'dd, ['rn, #+'off10]");
+ } else {
+ Format(instr, "vldrd'cond 'dd, ['rn, #-'off10]");
+ }
+ } else { // vstrd
+ if (instr->Bit(23) == 1) {
+ Format(instr, "vstrd'cond 'dd, ['rn, #+'off10]");
+ } else {
+ Format(instr, "vstrd'cond 'dd, ['rn, #-'off10]");
+ }
+ }
+ }
+ } else {
+ Unknown(instr);
+ }
+}
+
+
+void ARMDecoder::DecodeType7(Instr* instr) {
+ if (instr->Bit(24) == 1) {
+ Format(instr, "svc'cond #'svc");
+ if (instr->SvcField() == kStopMessageSvcCode) {
+ const char* message = *reinterpret_cast<const char**>(
+ reinterpret_cast<intptr_t>(instr) - Instr::kInstrSize);
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ " ; \"%s\"",
+ message);
+ }
+ } else if (instr->IsVFPDataProcessingOrSingleTransfer()) {
+ if (instr->Bit(4) == 0) {
+ // VFP Data Processing
+ switch (instr->Bits(20, 4) & 0xb) {
+ case 0: { // vmla, vmls floating-point
+ if (instr->Bit(8) == 0) {
+ if (instr->Bit(6) == 0) {
+ Format(instr, "vmlas'cond 'sd, 'sn, 'sm");
+ } else {
+ Format(instr, "vmlss'cond 'sd, 'sn, 'sm");
+ }
+ } else {
+ if (instr->Bit(6) == 0) {
+ Format(instr, "vmlad'cond 'dd, 'dn, 'dm");
+ } else {
+ Format(instr, "vmlsd'cond 'dd, 'dn, 'dm");
+ }
+ }
+ break;
+ }
+ case 1: // vnmla, vnmls, vnmul
+ default: {
+ Unknown(instr);
+ break;
+ }
+ case 2: { // vmul
+ if (instr->Bit(8) == 0) {
+ Format(instr, "vmuls'cond 'sd, 'sn, 'sm");
+ } else {
+ Format(instr, "vmuld'cond 'dd, 'dn, 'dm");
+ }
+ break;
+ }
+ case 8: { // vdiv
+ if (instr->Bit(8) == 0) {
+ Format(instr, "vdivs'cond 'sd, 'sn, 'sm");
+ } else {
+ Format(instr, "vdivd'cond 'dd, 'dn, 'dm");
+ }
+ break;
+ }
+ case 3: { // vadd, vsub floating-point
+ if (instr->Bit(8) == 0) {
+ if (instr->Bit(6) == 0) {
+ Format(instr, "vadds'cond 'sd, 'sn, 'sm");
+ } else {
+ Format(instr, "vsubs'cond 'sd, 'sn, 'sm");
+ }
+ } else {
+ if (instr->Bit(6) == 0) {
+ Format(instr, "vaddd'cond 'dd, 'dn, 'dm");
+ } else {
+ Format(instr, "vsubd'cond 'dd, 'dn, 'dm");
+ }
+ }
+ break;
+ }
+ case 0xb: { // Other VFP data-processing instructions
+ if (instr->Bit(6) == 0) { // vmov immediate
+ if (instr->Bit(8) == 0) {
+ Format(instr, "vmovs'cond 'sd, #'immf");
+ } else {
+ Format(instr, "vmovd'cond 'dd, #'immd");
+ }
+ break;
+ }
+ switch (instr->Bits(16, 4)) {
+ case 0: { // vmov register, vabs
+ switch (instr->Bits(6, 2)) {
+ case 1: { // vmov register
+ if (instr->Bit(8) == 0) {
+ Format(instr, "vmovs'cond 'sd, 'sm");
+ } else {
+ Format(instr, "vmovd'cond 'dd, 'dm");
+ }
+ break;
+ }
+ case 3: { // vabs
+ if (instr->Bit(8) == 0) {
+ Format(instr, "vabss'cond 'sd, 'sm");
+ } else {
+ Format(instr, "vabsd'cond 'dd, 'dm");
+ }
+ break;
+ }
+ default: {
+ Unknown(instr);
+ break;
+ }
+ }
+ break;
+ }
+ case 1: { // vneg, vsqrt
+ switch (instr->Bits(6, 2)) {
+ case 1: { // vneg
+ if (instr->Bit(8) == 0) {
+ Format(instr, "vnegs'cond 'sd, 'sm");
+ } else {
+ Format(instr, "vnegd'cond 'dd, 'dm");
+ }
+ break;
+ }
+ case 3: { // vsqrt
+ if (instr->Bit(8) == 0) {
+ Format(instr, "vsqrts'cond 'sd, 'sm");
+ } else {
+ Format(instr, "vsqrtd'cond 'dd, 'dm");
+ }
+ break;
+ }
+ default: {
+ Unknown(instr);
+ break;
+ }
+ }
+ break;
+ }
+ case 4: // vcmp, vcmpe
+ case 5: { // vcmp #0.0, vcmpe #0.0
+ if (instr->Bit(7) == 1) { // vcmpe
+ Unknown(instr);
+ } else {
+ if (instr->Bit(8) == 0) { // vcmps
+ if (instr->Bit(16) == 0) {
+ Format(instr, "vcmps'cond 'sd, 'sm");
+ } else {
+ Format(instr, "vcmps'cond 'sd, #0.0");
+ }
+ } else { // vcmpd
+ if (instr->Bit(16) == 0) {
+ Format(instr, "vcmpd'cond 'dd, 'dm");
+ } else {
+ Format(instr, "vcmpd'cond 'dd, #0.0");
+ }
+ }
+ }
+ break;
+ }
+ case 7: { // vcvt between double-precision and single-precision
+ if (instr->Bit(8) == 0) {
+ Format(instr, "vcvtds'cond 'dd, 'sm");
+ } else {
+ Format(instr, "vcvtsd'cond 'sd, 'dm");
+ }
+ break;
+ }
+ case 8: { // vcvt, vcvtr between floating-point and integer
+ if (instr->Bit(8) == 0) {
+ if (instr->Bit(7) == 0) {
+ Format(instr, "vcvtsu'cond 'sd, 'sm");
+ } else {
+ Format(instr, "vcvtsi'cond 'sd, 'sm");
+ }
+ } else {
+ if (instr->Bit(7) == 0) {
+ Format(instr, "vcvtdu'cond 'dd, 'sm");
+ } else {
+ Format(instr, "vcvtdi'cond 'dd, 'sm");
+ }
+ }
+ break;
+ }
+ case 12:
+ case 13: { // vcvt, vcvtr between floating-point and integer
+ if (instr->Bit(7) == 0) {
+ // We only support round-to-zero mode
+ Unknown(instr);
+ break;
+ }
+ if (instr->Bit(8) == 0) {
+ if (instr->Bit(16) == 0) {
+ Format(instr, "vcvtus'cond 'sd, 'sm");
+ } else {
+ Format(instr, "vcvtis'cond 'sd, 'sm");
+ }
+ } else {
+ if (instr->Bit(16) == 0) {
+ Format(instr, "vcvtud'cond 'sd, 'dm");
+ } else {
+ Format(instr, "vcvtid'cond 'sd, 'dm");
+ }
+ }
+ break;
+ }
+ case 2: // vcvtb, vcvtt
+ case 3: // vcvtb, vcvtt
+ case 9: // undefined
+ case 10: // vcvt between floating-point and fixed-point
+ case 11: // vcvt between floating-point and fixed-point
+ case 14: // vcvt between floating-point and fixed-point
+ case 15: // vcvt between floating-point and fixed-point
+ default: {
+ Unknown(instr);
+ break;
+ }
+ }
+ }
+ break;
+ }
+ } else {
+ // 8, 16, or 32-bit Transfer between ARM Core and VFP
+ if ((instr->Bits(21, 3) == 0) && (instr->Bit(8) == 0)) {
+ if (instr->Bit(20) == 0) {
+ Format(instr, "vmovs'cond 'sn, 'rd");
+ } else {
+ Format(instr, "vmovr'cond 'rd, 'sn");
+ }
+ } else if ((instr->Bits(20, 4) == 0xf) && (instr->Bit(8) == 0) &&
+ (instr->Bits(12, 4) == 0xf)) {
+ Format(instr, "vmstat'cond");
+ } else {
+ Unknown(instr);
+ }
+ }
+ } else {
+ Unknown(instr);
+ }
+}
+
+
+void ARMDecoder::InstructionDecode(uword pc) {
+Instr* instr = Instr::At(pc);
+ if (instr->ConditionField() == kSpecialCondition) {
+ if (instr->InstructionBits() == static_cast<int32_t>(0xf57ff01f)) {
+ Format(instr, "clrex");
+ } else {
+ Unknown(instr);
+ }
+ } else {
+ switch (instr->TypeField()) {
+ case 0:
+ case 1: {
+ DecodeType01(instr);
+ break;
+ }
+ case 2: {
+ DecodeType2(instr);
+ break;
+ }
+ case 3: {
+ DecodeType3(instr);
+ break;
+ }
+ case 4: {
+ DecodeType4(instr);
+ break;
+ }
+ case 5: {
+ DecodeType5(instr);
+ break;
+ }
+ case 6: {
+ DecodeType6(instr);
+ break;
+ }
+ case 7: {
+ DecodeType7(instr);
+ break;
+ }
+ default: {
+ // The type field is 3-bits in the ARM encoding.
+ UNREACHABLE();
+ break;
+ }
+ }
+ }
+}
+
+
int Disassembler::DecodeInstruction(char* hex_buffer, intptr_t hex_size,
char* human_buffer, intptr_t human_size,
uword pc) {
- UNIMPLEMENTED();
- return 0;
+ ARMDecoder decoder(human_buffer, human_size);
+ decoder.InstructionDecode(pc);
+ int32_t instruction_bits = Instr::At(pc)->InstructionBits();
+ OS::SNPrint(hex_buffer, hex_size, "%08x", instruction_bits);
+ return Instr::kInstrSize;
}
@@ -22,7 +1219,32 @@
uword end,
DisassemblyFormatter* formatter,
const Code::Comments& comments) {
- UNIMPLEMENTED();
+ ASSERT(formatter != NULL);
+ char hex_buffer[kHexadecimalBufferSize]; // Instruction in hexadecimal form.
+ char human_buffer[kUserReadableBufferSize]; // Human-readable instruction.
+ uword pc = start;
+ intptr_t comment_finger = 0;
+ while (pc < end) {
+ const intptr_t offset = pc - start;
+ while (comment_finger < comments.Length() &&
+ comments.PCOffsetAt(comment_finger) <= offset) {
+ formatter->Print(
+ " ;; %s\n",
+ String::Handle(comments.CommentAt(comment_finger)).ToCString());
+ comment_finger++;
+ }
+ int instruction_length = DecodeInstruction(hex_buffer,
+ sizeof(hex_buffer),
+ human_buffer,
+ sizeof(human_buffer),
+ pc);
+ formatter->ConsumeInstruction(hex_buffer,
+ sizeof(hex_buffer),
+ human_buffer,
+ sizeof(human_buffer),
+ pc);
+ pc += instruction_length;
+ }
}
} // namespace dart
diff --git a/runtime/vm/disassembler_test.cc b/runtime/vm/disassembler_test.cc
index 2a33e0e..56ac9bd 100644
--- a/runtime/vm/disassembler_test.cc
+++ b/runtime/vm/disassembler_test.cc
@@ -9,19 +9,21 @@
namespace dart {
-// Disassembler only supported on IA32 and X64 now.
-#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64)
+// Disassembler only supported on IA32, X64, and ARM.
+#if defined(TARGET_ARCH_IA32) || \
+ defined(TARGET_ARCH_X64) || \
+ defined(TARGET_ARCH_ARM)
TEST_CASE(Disassembler) {
Assembler assembler;
// The used instructions work on all platforms.
Register reg = static_cast<Register>(0);
- assembler.AddImmediate(reg, Immediate(1));
- assembler.AddImmediate(reg, Immediate(3));
+ assembler.PopRegister(reg);
+ assembler.Stop("testing disassembler");
// Only verify that the disassembler does not crash.
AssemblerTest test("Disassembler", &assembler);
- uword entry = test.Assemble();
- Disassembler::Disassemble(entry, entry + assembler.CodeSize());
+ test.Assemble();
+ Disassembler::Disassemble(test.entry(), test.entry() + assembler.CodeSize());
}
#endif
diff --git a/runtime/vm/exceptions.cc b/runtime/vm/exceptions.cc
index 98f2d04..f6413ba 100644
--- a/runtime/vm/exceptions.cc
+++ b/runtime/vm/exceptions.cc
@@ -424,6 +424,7 @@
RawObject* Exceptions::Create(ExceptionType type, const Array& arguments) {
Library& library = Library::Handle();
const String* class_name = NULL;
+ const String* constructor_name = &Symbols::Dot();
switch (type) {
case kNone:
UNREACHABLE();
@@ -439,6 +440,7 @@
case kNoSuchMethod:
library = Library::CoreLibrary();
class_name = &Symbols::NoSuchMethodError();
+ constructor_name = &String::Handle(Symbols::New("._withType"));
break;
case kFormat:
library = Library::CoreLibrary();
@@ -478,7 +480,10 @@
break;
}
- return DartLibraryCalls::ExceptionCreate(library, *class_name, arguments);
+ return DartLibraryCalls::ExceptionCreate(library,
+ *class_name,
+ *constructor_name,
+ arguments);
}
} // namespace dart
diff --git a/runtime/vm/flow_graph.cc b/runtime/vm/flow_graph.cc
index ea66a5a..e7598ac 100644
--- a/runtime/vm/flow_graph.cc
+++ b/runtime/vm/flow_graph.cc
@@ -60,6 +60,34 @@
}
+void FlowGraph::InsertBefore(Instruction* next,
+ Instruction* instr,
+ Environment* env,
+ Definition::UseKind use_kind) {
+ InsertAfter(next->previous(), instr, env, use_kind);
+}
+
+
+void FlowGraph::InsertAfter(Instruction* prev,
+ Instruction* instr,
+ Environment* env,
+ Definition::UseKind use_kind) {
+ for (intptr_t i = instr->InputCount() - 1; i >= 0; --i) {
+ Value* input = instr->InputAt(i);
+ input->definition()->AddInputUse(input);
+ input->set_instruction(instr);
+ input->set_use_index(i);
+ }
+ ASSERT(instr->env() == NULL);
+ if (env != NULL) env->DeepCopyTo(instr);
+ if (use_kind == Definition::kValue) {
+ ASSERT(instr->IsDefinition());
+ instr->AsDefinition()->set_ssa_temp_index(alloc_ssa_temp_index());
+ }
+ instr->InsertAfter(prev);
+}
+
+
void FlowGraph::DiscoverBlocks() {
// Initialize state.
preorder_.Clear();
@@ -97,58 +125,14 @@
}
-static void ResetUseListsInInstruction(Instruction* instr) {
- Definition* defn = instr->AsDefinition();
- if (defn != NULL) {
- defn->set_input_use_list(NULL);
- defn->set_env_use_list(NULL);
- }
- for (intptr_t i = 0; i < instr->InputCount(); ++i) {
- Value* use = instr->InputAt(i);
- use->set_instruction(NULL);
- use->set_use_index(-1);
- use->set_previous_use(NULL);
- use->set_next_use(NULL);
- }
- for (Environment::DeepIterator it(instr->env()); !it.Done(); it.Advance()) {
- Value* use = it.CurrentValue();
- use->set_instruction(NULL);
- use->set_use_index(-1);
- use->set_previous_use(NULL);
- use->set_next_use(NULL);
- }
-}
-
-
-bool FlowGraph::ResetUseLists() {
- // Reset initial definitions.
- for (intptr_t i = 0; i < graph_entry_->initial_definitions()->length(); ++i) {
- ResetUseListsInInstruction((*graph_entry_->initial_definitions())[i]);
- }
-
- // Reset phis in join entries and the instructions in each block.
- for (intptr_t i = 0; i < preorder_.length(); ++i) {
- BlockEntryInstr* entry = preorder_[i];
- JoinEntryInstr* join = entry->AsJoinEntry();
- if (join != NULL && join->phis() != NULL) {
- for (intptr_t i = 0; i < join->phis()->length(); ++i) {
- PhiInstr* phi = (*join->phis())[i];
- if (phi != NULL) ResetUseListsInInstruction(phi);
- }
- }
- for (ForwardInstructionIterator it(entry); !it.Done(); it.Advance()) {
- ResetUseListsInInstruction(it.Current());
- }
- }
- return true; // Return true so we can ASSERT the reset code.
-}
-
-
-static void ValidateUseListsInInstruction(Instruction* instr) {
+static void VerifyUseListsInInstruction(Instruction* instr) {
ASSERT(instr != NULL);
ASSERT(!instr->IsJoinEntry());
for (intptr_t i = 0; i < instr->InputCount(); ++i) {
Value* use = instr->InputAt(i);
+ ASSERT(use->definition() != NULL);
+ ASSERT((use->definition() != instr) || use->definition()->IsPhi());
+ ASSERT(use->instruction() == instr);
ASSERT(use->use_index() == i);
ASSERT(!FLAG_verify_compiler ||
(1 == MembershipCount(use, use->definition()->input_use_list())));
@@ -157,6 +141,9 @@
intptr_t use_index = 0;
for (Environment::DeepIterator it(instr->env()); !it.Done(); it.Advance()) {
Value* use = it.CurrentValue();
+ ASSERT(use->definition() != NULL);
+ ASSERT((use->definition() != instr) || use->definition()->IsPhi());
+ ASSERT(use->instruction() == instr);
ASSERT(use->use_index() == use_index++);
ASSERT(!FLAG_verify_compiler ||
(1 == MembershipCount(use, use->definition()->env_use_list())));
@@ -170,9 +157,13 @@
ASSERT(prev == curr->previous_use());
ASSERT(defn == curr->definition());
Instruction* instr = curr->instruction();
- // The instruction should not be removed from the graph (phis are not
- // removed until register allocation.)
- ASSERT(instr->IsPhi() || (instr->previous() != NULL));
+ // The instruction should not be removed from the graph. Removed
+ // instructions have a NULL previous link. Phis are not removed until
+ // register allocation. Comparisons used only in a branch will have a
+ // NULL previous link though they are still in the graph.
+ ASSERT(instr->IsPhi() ||
+ (instr->IsDefinition() && instr->AsDefinition()->IsComparison()) ||
+ (instr->previous() != NULL));
ASSERT(curr == instr->InputAt(curr->use_index()));
prev = curr;
curr = curr->next_use();
@@ -185,9 +176,9 @@
ASSERT(defn == curr->definition());
Instruction* instr = curr->instruction();
ASSERT(curr == instr->env()->ValueAtUseIndex(curr->use_index()));
- // The instruction should not be removed from the graph (phis are not
- // removed until register allocation.)
- ASSERT(instr->IsPhi() || (instr->previous() != NULL));
+ ASSERT(instr->IsPhi() ||
+ (instr->IsDefinition() && instr->AsDefinition()->IsComparison()) ||
+ (instr->previous() != NULL));
prev = curr;
curr = curr->next_use();
}
@@ -195,24 +186,24 @@
}
-bool FlowGraph::ValidateUseLists() {
- // Validate initial definitions.
+bool FlowGraph::VerifyUseLists() {
+ // Verify the initial definitions.
for (intptr_t i = 0; i < graph_entry_->initial_definitions()->length(); ++i) {
- ValidateUseListsInInstruction((*graph_entry_->initial_definitions())[i]);
+ VerifyUseListsInInstruction((*graph_entry_->initial_definitions())[i]);
}
- // Validate phis in join entries and the instructions in each block.
+ // Verify phis in join entries and the instructions in each block.
for (intptr_t i = 0; i < preorder_.length(); ++i) {
BlockEntryInstr* entry = preorder_[i];
JoinEntryInstr* join = entry->AsJoinEntry();
if (join != NULL && join->phis() != NULL) {
for (intptr_t i = 0; i < join->phis()->length(); ++i) {
PhiInstr* phi = (*join->phis())[i];
- if (phi != NULL) ValidateUseListsInInstruction(phi);
+ if (phi != NULL) VerifyUseListsInInstruction(phi);
}
}
for (ForwardInstructionIterator it(entry); !it.Done(); it.Advance()) {
- ValidateUseListsInInstruction(it.Current());
+ VerifyUseListsInInstruction(it.Current());
}
}
return true; // Return true so we can ASSERT validation.
@@ -220,108 +211,6 @@
#endif // DEBUG
-static void ClearUseLists(Definition* defn) {
- ASSERT(defn != NULL);
- ASSERT(!defn->HasUses());
- defn->set_input_use_list(NULL);
- defn->set_env_use_list(NULL);
-}
-
-
-static void RecordInputUses(Instruction* instr) {
- ASSERT(instr != NULL);
- for (intptr_t i = 0; i < instr->InputCount(); ++i) {
- Value* use = instr->InputAt(i);
- ASSERT(use->instruction() == NULL);
- ASSERT(use->use_index() == -1);
- ASSERT(use->previous_use() == NULL);
- ASSERT(use->next_use() == NULL);
- DEBUG_ASSERT(!FLAG_verify_compiler ||
- (0 == MembershipCount(use, use->definition()->input_use_list())));
- use->set_instruction(instr);
- use->set_use_index(i);
- use->definition()->AddInputUse(use);
- }
-}
-
-
-static void RecordEnvUses(Instruction* instr) {
- ASSERT(instr != NULL);
- if (instr->env() == NULL) return;
- intptr_t use_index = 0;
- for (Environment::DeepIterator it(instr->env()); !it.Done(); it.Advance()) {
- Value* use = it.CurrentValue();
- ASSERT(use->instruction() == NULL);
- ASSERT(use->use_index() == -1);
- ASSERT(use->previous_use() == NULL);
- ASSERT(use->next_use() == NULL);
- DEBUG_ASSERT(!FLAG_verify_compiler ||
- (0 == MembershipCount(use, use->definition()->env_use_list())));
- use->set_instruction(instr);
- use->set_use_index(use_index++);
- use->definition()->AddEnvUse(use);
- }
-}
-
-
-static void ComputeUseListsRecursive(BlockEntryInstr* block) {
- // Clear phi definitions.
- JoinEntryInstr* join = block->AsJoinEntry();
- if (join != NULL && join->phis() != NULL) {
- for (intptr_t i = 0; i < join->phis()->length(); ++i) {
- PhiInstr* phi = (*join->phis())[i];
- if (phi != NULL) ClearUseLists(phi);
- }
- }
- // Compute uses on normal instructions.
- for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
- Instruction* instr = it.Current();
- if (instr->IsDefinition()) ClearUseLists(instr->AsDefinition());
- RecordInputUses(instr);
- RecordEnvUses(instr);
- }
- // Compute recursively on dominated blocks.
- for (intptr_t i = 0; i < block->dominated_blocks().length(); ++i) {
- ComputeUseListsRecursive(block->dominated_blocks()[i]);
- }
- // Add phi uses on successor edges.
- if (block->last_instruction()->SuccessorCount() == 1 &&
- block->last_instruction()->SuccessorAt(0)->IsJoinEntry()) {
- JoinEntryInstr* join =
- block->last_instruction()->SuccessorAt(0)->AsJoinEntry();
- intptr_t pred_index = join->IndexOfPredecessor(block);
- ASSERT(pred_index >= 0);
- if (join->phis() != NULL) {
- for (intptr_t i = 0; i < join->phis()->length(); ++i) {
- PhiInstr* phi = (*join->phis())[i];
- if (phi == NULL) continue;
- Value* use = phi->InputAt(pred_index);
- ASSERT(use->instruction() == NULL);
- ASSERT(use->use_index() == -1);
- ASSERT(use->previous_use() == NULL);
- ASSERT(use->next_use() == NULL);
- DEBUG_ASSERT(!FLAG_verify_compiler ||
- (0 == MembershipCount(use, use->definition()->input_use_list())));
- use->set_instruction(phi);
- use->set_use_index(pred_index);
- use->definition()->AddInputUse(use);
- }
- }
- }
-}
-
-
-void FlowGraph::ComputeUseLists() {
- DEBUG_ASSERT(ResetUseLists());
- // Clear initial definitions.
- for (intptr_t i = 0; i < graph_entry_->initial_definitions()->length(); ++i) {
- ClearUseLists((*graph_entry_->initial_definitions())[i]);
- }
- ComputeUseListsRecursive(graph_entry_);
- DEBUG_ASSERT(!FLAG_verify_compiler || ValidateUseLists());
-}
-
-
void FlowGraph::ComputeSSA(intptr_t next_virtual_register_number,
GrowableArray<Definition*>* inlining_parameters) {
ASSERT((next_virtual_register_number == 0) || (inlining_parameters != NULL));
@@ -581,9 +470,18 @@
// at goto instructions. Optimizations like LICM expect an environment at
// gotos.
if (current->CanDeoptimize() || current->IsGoto()) {
- current->set_env(Environment::From(*env,
- num_non_copied_params_,
- parsed_function_.function()));
+ Environment* deopt_env =
+ Environment::From(*env,
+ num_non_copied_params_,
+ parsed_function_.function());
+ current->set_env(deopt_env);
+ intptr_t use_index = 0;
+ for (Environment::DeepIterator it(deopt_env); !it.Done(); it.Advance()) {
+ Value* use = it.CurrentValue();
+ use->set_instruction(current);
+ use->set_use_index(use_index++);
+ use->definition()->AddEnvUse(use);
+ }
}
if (current->CanDeoptimize()) {
current->env()->set_deopt_id(current->deopt_id());
@@ -603,11 +501,16 @@
Definition* input_defn = v->definition();
if (input_defn->IsLoadLocal() || input_defn->IsStoreLocal()) {
// Remove the load/store from the graph.
+ input_defn->UnuseAllInputs();
input_defn->RemoveFromGraph();
// Assert we are not referencing nulls in the initial environment.
ASSERT(reaching_defn->ssa_temp_index() != -1);
- current->SetInputAt(i, new Value(reaching_defn));
+ v->set_definition(reaching_defn);
+ input_defn = reaching_defn;
}
+ v->set_instruction(current);
+ v->set_use_index(i);
+ input_defn->AddInputUse(v);
}
// Drop pushed arguments for calls.
@@ -645,6 +548,7 @@
env->Add((*env)[index]);
// We remove load/store instructions when we find their use in 2a.
} else {
+ definition->UnuseAllInputs();
it.RemoveCurrentFromGraph();
}
} else {
@@ -685,7 +589,11 @@
PhiInstr* phi = (*successor->phis())[i];
if (phi != NULL) {
// Rename input operand.
- phi->SetInputAt(pred_index, new Value((*env)[i]));
+ Value* use = new Value((*env)[i]);
+ phi->SetInputAt(pred_index, use);
+ use->set_instruction(phi);
+ use->set_use_index(pred_index);
+ use->definition()->AddInputUse(use);
}
}
}
diff --git a/runtime/vm/flow_graph.h b/runtime/vm/flow_graph.h
index 81edee2..59c0c0d 100644
--- a/runtime/vm/flow_graph.h
+++ b/runtime/vm/flow_graph.h
@@ -6,17 +6,12 @@
#define VM_FLOW_GRAPH_H_
#include "vm/growable_array.h"
+#include "vm/intermediate_language.h"
#include "vm/parser.h"
namespace dart {
-class BlockEntryInstr;
-class ConstantInstr;
-class Definition;
class FlowGraphBuilder;
-class GraphEntryInstr;
-class PhiInstr;
-class ReturnInstr;
class ValueInliningContext;
class BlockIterator : public ValueObject {
@@ -117,10 +112,18 @@
ConstantInstr* AddConstantToInitialDefinitions(const Object& object);
void AddToInitialDefinitions(Definition* defn);
+ void InsertBefore(Instruction* next,
+ Instruction* instr,
+ Environment* env,
+ Definition::UseKind use_kind);
+ void InsertAfter(Instruction* prev,
+ Instruction* instr,
+ Environment* env,
+ Definition::UseKind use_kind);
+
// Operations on the flow graph.
void ComputeSSA(intptr_t next_virtual_register_number,
GrowableArray<Definition*>* inlining_parameters);
- void ComputeUseLists();
// Finds natural loops in the flow graph and attaches a list of loop
// body blocks for each loop header.
@@ -134,9 +137,8 @@
void InvalidateDominatorTree() { invalid_dominator_tree_ = true; }
#ifdef DEBUG
- // Validation methods for debugging.
- bool ResetUseLists();
- bool ValidateUseLists();
+ // Verification methods for debugging.
+ bool VerifyUseLists();
#endif // DEBUG
private:
diff --git a/runtime/vm/flow_graph_allocator.cc b/runtime/vm/flow_graph_allocator.cc
index 4adf681..a2b0c36 100644
--- a/runtime/vm/flow_graph_allocator.cc
+++ b/runtime/vm/flow_graph_allocator.cc
@@ -2432,9 +2432,11 @@
for (ForwardInstructionIterator instr_it(block);
!instr_it.Done();
instr_it.Advance()) {
- Instruction* instr = instr_it.Current();
- if (instr->IsDefinition() && instr->representation() == kUnboxedMint) {
- mint_values_->Add(instr->AsDefinition()->ssa_temp_index());
+ Definition* defn = instr_it.Current()->AsDefinition();
+ if ((defn != NULL) &&
+ (defn->ssa_temp_index() >= 0) &&
+ (defn->representation() == kUnboxedMint)) {
+ mint_values_->Add(defn->ssa_temp_index());
}
}
}
diff --git a/runtime/vm/flow_graph_builder.cc b/runtime/vm/flow_graph_builder.cc
index 909d937..8bcba37 100644
--- a/runtime/vm/flow_graph_builder.cc
+++ b/runtime/vm/flow_graph_builder.cc
@@ -4,6 +4,7 @@
#include "vm/flow_graph_builder.h"
+#include "lib/invocation_mirror.h"
#include "vm/ast_printer.h"
#include "vm/code_descriptors.h"
#include "vm/dart_entry.h"
@@ -136,6 +137,7 @@
} else if (num_exits == 1) {
// For just one exit, replace the uses and remove the call from the graph.
call->ReplaceUsesWith(ValueAt(0)->definition());
+ ValueAt(0)->RemoveFromUseList();
call->previous()->LinkTo(callee_entry->next());
LastInstructionAt(0)->LinkTo(call->next());
// In case of control flow, locally update the predecessors, phis and
@@ -210,6 +212,12 @@
}
// Replace uses of the call with the phi.
call->ReplaceUsesWith(phi);
+ } else {
+ // In the case that the result is unused, remove the return value uses
+ // from their definition's use list.
+ for (intptr_t i = 0; i < num_exits; ++i) {
+ ValueAt(i)->RemoveFromUseList();
+ }
}
// Remove the call from the graph.
call->previous()->LinkTo(callee_entry->next());
@@ -2367,9 +2375,15 @@
// NoSuchMethodError.
// Throw a NoSuchMethodError.
- StaticCallInstr* call = BuildThrowNoSuchMethodError(node->token_pos(),
- node->cls(),
- getter_name);
+ StaticCallInstr* call = BuildThrowNoSuchMethodError(
+ node->token_pos(),
+ node->cls(),
+ getter_name,
+ InvocationMirror::EncodeType(
+ node->cls().IsTopLevel() ?
+ InvocationMirror::kTopLevel :
+ InvocationMirror::kStatic,
+ InvocationMirror::kGetter));
ReturnDefinition(call);
return;
}
@@ -2411,9 +2425,15 @@
arguments);
} else {
// Throw a NoSuchMethodError.
- call = BuildThrowNoSuchMethodError(node->token_pos(),
- node->cls(),
- setter_name);
+ call = BuildThrowNoSuchMethodError(
+ node->token_pos(),
+ node->cls(),
+ setter_name,
+ InvocationMirror::EncodeType(
+ node->cls().IsTopLevel() ?
+ InvocationMirror::kTopLevel :
+ InvocationMirror::kStatic,
+ InvocationMirror::kGetter));
}
} else {
if (is_super_setter) {
@@ -3090,7 +3110,8 @@
StaticCallInstr* EffectGraphVisitor::BuildThrowNoSuchMethodError(
intptr_t token_pos,
const Class& function_class,
- const String& function_name) {
+ const String& function_name,
+ int invocation_type) {
ZoneGrowableArray<PushArgumentInstr*>* arguments =
new ZoneGrowableArray<PushArgumentInstr*>();
// Object receiver.
@@ -3109,6 +3130,10 @@
const String& member_name = String::ZoneHandle(Symbols::New(function_name));
Value* member_name_value = Bind(new ConstantInstr(member_name));
arguments->Add(PushArgument(member_name_value));
+ // Smi invocation_type.
+ Value* invocation_type_value = Bind(new ConstantInstr(
+ Smi::ZoneHandle(Smi::New(invocation_type))));
+ arguments->Add(PushArgument(invocation_type_value));
// List arguments.
Value* arguments_value = Bind(new ConstantInstr(Array::ZoneHandle()));
arguments->Add(PushArgument(arguments_value));
@@ -3219,7 +3244,7 @@
TargetEntryInstr* normal_entry =
new TargetEntryInstr(AllocateBlockId(),
CatchClauseNode::kInvalidTryIndex);
- graph_entry_ = new GraphEntryInstr(normal_entry);
+ graph_entry_ = new GraphEntryInstr(parsed_function(), normal_entry);
EffectGraphVisitor for_effect(this, 0);
// TODO(kmillikin): We can eliminate stack checks in some cases (e.g., the
// stack check on entry for leaf routines).
diff --git a/runtime/vm/flow_graph_builder.h b/runtime/vm/flow_graph_builder.h
index b30ad92..92f5174 100644
--- a/runtime/vm/flow_graph_builder.h
+++ b/runtime/vm/flow_graph_builder.h
@@ -317,7 +317,8 @@
StaticCallInstr* BuildThrowNoSuchMethodError(intptr_t token_pos,
const Class& function_class,
- const String& function_name);
+ const String& function_name,
+ int invocation_type);
void BuildStaticSetter(StaticSetterNode* node, bool result_is_needed);
Definition* BuildStoreStaticField(StoreStaticFieldNode* node,
diff --git a/runtime/vm/flow_graph_compiler_ia32.cc b/runtime/vm/flow_graph_compiler_ia32.cc
index 72e7d35..138ecf3 100644
--- a/runtime/vm/flow_graph_compiler_ia32.cc
+++ b/runtime/vm/flow_graph_compiler_ia32.cc
@@ -24,6 +24,7 @@
DECLARE_FLAG(int, optimization_counter_threshold);
DECLARE_FLAG(bool, print_ast);
DECLARE_FLAG(bool, print_scopes);
+DECLARE_FLAG(bool, enable_type_checks);
DECLARE_FLAG(bool, eliminate_type_checks);
@@ -624,6 +625,12 @@
void FlowGraphCompiler::EmitInstructionPrologue(Instruction* instr) {
if (!is_optimizing()) {
+ if (FLAG_enable_type_checks && instr->IsAssertAssignable()) {
+ AssertAssignableInstr* assert = instr->AsAssertAssignable();
+ AddCurrentDescriptor(PcDescriptors::kDeoptBefore,
+ assert->deopt_id(),
+ assert->token_pos());
+ }
AllocateRegistersLocally(instr);
}
}
diff --git a/runtime/vm/flow_graph_compiler_x64.cc b/runtime/vm/flow_graph_compiler_x64.cc
index f92c613..6d62e562 100644
--- a/runtime/vm/flow_graph_compiler_x64.cc
+++ b/runtime/vm/flow_graph_compiler_x64.cc
@@ -23,6 +23,7 @@
DECLARE_FLAG(int, optimization_counter_threshold);
DECLARE_FLAG(bool, print_ast);
DECLARE_FLAG(bool, print_scopes);
+DECLARE_FLAG(bool, enable_type_checks);
DECLARE_FLAG(bool, eliminate_type_checks);
@@ -620,6 +621,12 @@
void FlowGraphCompiler::EmitInstructionPrologue(Instruction* instr) {
if (!is_optimizing()) {
+ if (FLAG_enable_type_checks && instr->IsAssertAssignable()) {
+ AssertAssignableInstr* assert = instr->AsAssertAssignable();
+ AddCurrentDescriptor(PcDescriptors::kDeoptBefore,
+ assert->deopt_id(),
+ assert->token_pos());
+ }
AllocateRegistersLocally(instr);
}
}
diff --git a/runtime/vm/flow_graph_inliner.cc b/runtime/vm/flow_graph_inliner.cc
index bd74d4c..3fc75b3 100644
--- a/runtime/vm/flow_graph_inliner.cc
+++ b/runtime/vm/flow_graph_inliner.cc
@@ -491,7 +491,7 @@
// Compute SSA on the callee graph, catching bailouts.
callee_graph->ComputeSSA(caller_graph_->max_virtual_register_number(),
¶m_stubs);
- callee_graph->ComputeUseLists();
+ DEBUG_ASSERT(callee_graph->VerifyUseLists());
}
{
@@ -501,7 +501,7 @@
// TODO(zerny): Do more optimization passes on the callee graph.
FlowGraphOptimizer optimizer(callee_graph);
optimizer.ApplyICData();
- callee_graph->ComputeUseLists();
+ DEBUG_ASSERT(callee_graph->VerifyUseLists());
}
if (FLAG_trace_inlining &&
@@ -561,13 +561,6 @@
// Plug result in the caller graph.
inlining_context->ReplaceCall(caller_graph_, call, callee_graph);
- // Remove push arguments of the call.
- for (intptr_t i = 0; i < call->ArgumentCount(); ++i) {
- PushArgumentInstr* push = call->ArgumentAt(i);
- push->ReplaceUsesWith(push->value()->definition());
- push->RemoveFromGraph();
- }
-
// Replace each stub with the actual argument or the caller's constant.
// Nulls denote optional parameters for which no actual was given.
for (intptr_t i = 0; i < arguments->length(); ++i) {
@@ -576,6 +569,14 @@
if (actual != NULL) stub->ReplaceUsesWith(actual->definition());
}
+ // Remove push arguments of the call.
+ for (intptr_t i = 0; i < call->ArgumentCount(); ++i) {
+ PushArgumentInstr* push = call->PushArgumentAt(i);
+ push->ReplaceUsesWith(push->value()->definition());
+ push->UnuseAllInputs();
+ push->RemoveFromGraph();
+ }
+
// Replace remaining constants with uses by constants in the caller's
// initial definitions.
GrowableArray<Definition*>* defns =
@@ -596,7 +597,7 @@
if (!in_cache) function_cache_.Add(parsed_function);
// Check that inlining maintains use lists.
- DEBUG_ASSERT(!FLAG_verify_compiler || caller_graph_->ValidateUseLists());
+ DEBUG_ASSERT(!FLAG_verify_compiler || caller_graph_->VerifyUseLists());
// Build succeeded so we restore the bailout jump.
inlined_ = true;
@@ -652,7 +653,7 @@
StaticCallInstr* call = calls[i];
GrowableArray<Value*> arguments(call->ArgumentCount());
for (int i = 0; i < call->ArgumentCount(); ++i) {
- arguments.Add(call->ArgumentAt(i)->value());
+ arguments.Add(call->PushArgumentAt(i)->value());
}
TryInlining(call->function(), call->argument_names(), &arguments, call);
}
@@ -667,14 +668,14 @@
// Find the closure of the callee.
ASSERT(call->ArgumentCount() > 0);
const CreateClosureInstr* closure =
- call->ArgumentAt(0)->value()->definition()->AsCreateClosure();
+ call->ArgumentAt(0)->AsCreateClosure();
if (closure == NULL) {
TRACE_INLINING(OS::Print(" Bailout: non-closure operator\n"));
continue;
}
GrowableArray<Value*> arguments(call->ArgumentCount());
for (int i = 0; i < call->ArgumentCount(); ++i) {
- arguments.Add(call->ArgumentAt(i)->value());
+ arguments.Add(call->PushArgumentAt(i)->value());
}
TryInlining(closure->function(),
call->argument_names(),
@@ -710,7 +711,7 @@
}
GrowableArray<Value*> arguments(instr->ArgumentCount());
for (int arg_i = 0; arg_i < instr->ArgumentCount(); ++arg_i) {
- arguments.Add(instr->ArgumentAt(arg_i)->value());
+ arguments.Add(instr->PushArgumentAt(arg_i)->value());
}
TryInlining(target,
instr->instance_call()->argument_names(),
diff --git a/runtime/vm/flow_graph_optimizer.cc b/runtime/vm/flow_graph_optimizer.cc
index c72acf8..58cf837 100644
--- a/runtime/vm/flow_graph_optimizer.cc
+++ b/runtime/vm/flow_graph_optimizer.cc
@@ -33,6 +33,8 @@
DEFINE_FLAG(int, max_polymorphic_checks, 4,
"Maximum number of polymorphic check, otherwise it is megamorphic.");
DEFINE_FLAG(bool, remove_redundant_phis, true, "Remove redundant phis.");
+DEFINE_FLAG(bool, truncating_left_shift, true,
+ "Optimize left shift to truncate if possible");
void FlowGraphOptimizer::ApplyICData() {
@@ -49,20 +51,20 @@
ForwardInstructionIterator it(entry);
current_iterator_ = ⁢
for (; !it.Done(); it.Advance()) {
- if (it.Current()->IsInstanceCall()) {
- InstanceCallInstr* call = it.Current()->AsInstanceCall();
+ Instruction* instr = it.Current();
+ if (instr->IsInstanceCall()) {
+ InstanceCallInstr* call = instr->AsInstanceCall();
if (call->HasICData()) {
if (TryCreateICData(call)) {
VisitInstanceCall(call);
}
}
- } else if (it.Current()->IsPolymorphicInstanceCall()) {
- SpecializePolymorphicInstanceCall(
- it.Current()->AsPolymorphicInstanceCall());
- } else if (it.Current()->IsStrictCompare()) {
- VisitStrictCompare(it.Current()->AsStrictCompare());
- } else if (it.Current()->IsBranch()) {
- ComparisonInstr* compare = it.Current()->AsBranch()->comparison();
+ } else if (instr->IsPolymorphicInstanceCall()) {
+ SpecializePolymorphicInstanceCall(instr->AsPolymorphicInstanceCall());
+ } else if (instr->IsStrictCompare()) {
+ VisitStrictCompare(instr->AsStrictCompare());
+ } else if (instr->IsBranch()) {
+ ComparisonInstr* compare = instr->AsBranch()->comparison();
if (compare->IsStrictCompare()) {
VisitStrictCompare(compare->AsStrictCompare());
}
@@ -84,7 +86,7 @@
GrowableArray<intptr_t> class_ids(call->ic_data()->num_args_tested());
ASSERT(call->ic_data()->num_args_tested() <= call->ArgumentCount());
for (intptr_t i = 0; i < call->ic_data()->num_args_tested(); i++) {
- intptr_t cid = call->ArgumentAt(i)->value()->Type()->ToCid();
+ intptr_t cid = call->PushArgumentAt(i)->value()->Type()->ToCid();
class_ids.Add(cid);
}
// TODO(srdjan): Test for other class_ids > 1.
@@ -150,7 +152,8 @@
return; // Already specialized.
}
- const intptr_t receiver_cid = call->ArgumentAt(0)->value()->Type()->ToCid();
+ const intptr_t receiver_cid =
+ call->PushArgumentAt(0)->value()->Type()->ToCid();
if (receiver_cid == kDynamicCid) {
return; // No information about receiver was infered.
}
@@ -166,6 +169,93 @@
}
+static BinarySmiOpInstr* AsSmiShiftLeftInstruction(Definition* d) {
+ BinarySmiOpInstr* instr = d->AsBinarySmiOp();
+ if ((instr != NULL) && (instr->op_kind() == Token::kSHL)) {
+ return instr;
+ }
+ return NULL;
+}
+
+
+static bool IsPositiveOrZeroSmiConst(Definition* d) {
+ ConstantInstr* const_instr = d->AsConstant();
+ if ((const_instr != NULL) && (const_instr->value().IsSmi())) {
+ return Smi::Cast(const_instr->value()).Value() >= 0;
+ }
+ return false;
+}
+
+
+void FlowGraphOptimizer::OptimizeLeftShiftBitAndSmiOp(
+ Definition* bit_and_instr,
+ Definition* left_instr,
+ Definition* right_instr) {
+ ASSERT(bit_and_instr != NULL);
+ ASSERT((left_instr != NULL) && (right_instr != NULL));
+
+ // Check for pattern, smi_shift_left must be single-use.
+ bool is_positive_or_zero = IsPositiveOrZeroSmiConst(left_instr);
+ if (!is_positive_or_zero) {
+ is_positive_or_zero = IsPositiveOrZeroSmiConst(right_instr);
+ }
+ if (!is_positive_or_zero) return;
+
+ BinarySmiOpInstr* smi_shift_left = NULL;
+ if (bit_and_instr->InputAt(0)->IsSingleUse()) {
+ smi_shift_left = AsSmiShiftLeftInstruction(left_instr);
+ }
+ if ((smi_shift_left == NULL) && (bit_and_instr->InputAt(1)->IsSingleUse())) {
+ smi_shift_left = AsSmiShiftLeftInstruction(right_instr);
+ }
+ if (smi_shift_left == NULL) return;
+
+ // Pattern recognized.
+ smi_shift_left->set_is_truncating(true);
+ ASSERT(bit_and_instr->IsBinarySmiOp() || bit_and_instr->IsBinaryMintOp());
+ if (bit_and_instr->IsBinaryMintOp()) {
+ // Replace Mint op with Smi op.
+ BinarySmiOpInstr* smi_op = new BinarySmiOpInstr(
+ Token::kBIT_AND,
+ bit_and_instr->AsBinaryMintOp()->instance_call(),
+ new Value(left_instr),
+ new Value(right_instr));
+ bit_and_instr->ReplaceWith(smi_op, current_iterator());
+ }
+}
+
+
+// Optimize (a << b) & c pattern: if c is a positive Smi or zero, then the
+// shift can be a truncating Smi shift-left and result is always Smi.
+void FlowGraphOptimizer::TryOptimizeLeftShiftWithBitAndPattern() {
+ if (!FLAG_truncating_left_shift) return;
+ ASSERT(current_iterator_ == NULL);
+ for (intptr_t i = 0; i < block_order_.length(); ++i) {
+ BlockEntryInstr* entry = block_order_[i];
+ ForwardInstructionIterator it(entry);
+ current_iterator_ = ⁢
+ for (; !it.Done(); it.Advance()) {
+ if (it.Current()->IsBinarySmiOp()) {
+ BinarySmiOpInstr* binop = it.Current()->AsBinarySmiOp();
+ if (binop->op_kind() == Token::kBIT_AND) {
+ OptimizeLeftShiftBitAndSmiOp(binop,
+ binop->left()->definition(),
+ binop->right()->definition());
+ }
+ } else if (it.Current()->IsBinaryMintOp()) {
+ BinaryMintOpInstr* mintop = it.Current()->AsBinaryMintOp();
+ if (mintop->op_kind() == Token::kBIT_AND) {
+ OptimizeLeftShiftBitAndSmiOp(mintop,
+ mintop->left()->definition(),
+ mintop->right()->definition());
+ }
+ }
+ }
+ current_iterator_ = NULL;
+ }
+}
+
+
static void EnsureSSATempIndex(FlowGraph* graph,
Definition* defn,
Definition* replacement) {
@@ -234,24 +324,32 @@
(use->Type()->ToCid() == kDoubleCid));
const intptr_t deopt_id = (deopt_target != NULL) ?
deopt_target->DeoptimizationTarget() : Isolate::kNoDeoptId;
- converted = new UnboxIntegerInstr(new Value(use->definition()), deopt_id);
+ converted = new UnboxIntegerInstr(use->CopyWithType(), deopt_id);
+
} else if ((from == kUnboxedMint) && (to == kTagged)) {
- converted = new BoxIntegerInstr(new Value(use->definition()));
+ converted = new BoxIntegerInstr(use->CopyWithType());
+
} else if (from == kUnboxedMint && to == kUnboxedDouble) {
// Convert by boxing/unboxing.
// TODO(fschneider): Implement direct unboxed mint-to-double conversion.
- BoxIntegerInstr* boxed = new BoxIntegerInstr(new Value(use->definition()));
+ BoxIntegerInstr* boxed = new BoxIntegerInstr(use->CopyWithType());
+ use->RemoveFromUseList();
+ use->set_definition(boxed);
+ boxed->AddInputUse(use);
InsertBefore(insert_before, boxed, NULL, Definition::kValue);
+
const intptr_t deopt_id = (deopt_target != NULL) ?
deopt_target->DeoptimizationTarget() : Isolate::kNoDeoptId;
converted = new UnboxDoubleInstr(new Value(boxed), deopt_id);
+
} else if ((from == kUnboxedDouble) && (to == kTagged)) {
- converted = new BoxDoubleInstr(new Value(use->definition()), NULL);
+ converted = new BoxDoubleInstr(use->CopyWithType(), NULL);
+
} else if ((from == kTagged) && (to == kUnboxedDouble)) {
- const intptr_t deopt_id = (deopt_target != NULL) ?
- deopt_target->DeoptimizationTarget() : Isolate::kNoDeoptId;
ASSERT((deopt_target != NULL) ||
(use->Type()->ToCid() == kDoubleCid));
+ const intptr_t deopt_id = (deopt_target != NULL) ?
+ deopt_target->DeoptimizationTarget() : Isolate::kNoDeoptId;
ConstantInstr* constant = use->definition()->AsConstant();
if ((constant != NULL) && constant->value().IsSmi()) {
const double dbl_val = Smi::Cast(constant->value()).AsDoubleValue();
@@ -261,13 +359,15 @@
InsertBefore(insert_before, double_const, NULL, Definition::kValue);
converted = new UnboxDoubleInstr(new Value(double_const), deopt_id);
} else {
- converted = new UnboxDoubleInstr(new Value(use->definition()), deopt_id);
+ converted = new UnboxDoubleInstr(use->CopyWithType(), deopt_id);
}
}
ASSERT(converted != NULL);
+ use->RemoveFromUseList();
+ use->set_definition(converted);
+ converted->AddInputUse(use);
InsertBefore(insert_before, converted, use->instruction()->env(),
Definition::kValue);
- use->set_definition(converted);
}
@@ -459,23 +559,16 @@
}
-static void RemovePushArguments(InstanceCallInstr* call) {
- // Remove original push arguments.
+void FlowGraphOptimizer::ReplaceCall(Definition* call,
+ Definition* replacement) {
+ // Remove the original push arguments.
for (intptr_t i = 0; i < call->ArgumentCount(); ++i) {
- PushArgumentInstr* push = call->ArgumentAt(i);
+ PushArgumentInstr* push = call->PushArgumentAt(i);
push->ReplaceUsesWith(push->value()->definition());
+ push->UnuseAllInputs();
push->RemoveFromGraph();
}
-}
-
-
-static void RemovePushArguments(StaticCallInstr* call) {
- // Remove original push arguments.
- for (intptr_t i = 0; i < call->ArgumentCount(); ++i) {
- PushArgumentInstr* push = call->ArgumentAt(i);
- push->ReplaceUsesWith(push->value()->definition());
- push->RemoveFromGraph();
- }
+ call->ReplaceWith(replacement, current_iterator());
}
@@ -496,19 +589,42 @@
}
-void FlowGraphOptimizer::AddCheckClass(InstanceCallInstr* call,
- Value* value) {
+void FlowGraphOptimizer::AddCheckSmi(Definition* to_check,
+ intptr_t deopt_id,
+ Environment* deopt_environment,
+ Instruction* insert_before) {
+ if (to_check->Type()->ToCid() != kSmiCid) {
+ InsertBefore(insert_before,
+ new CheckSmiInstr(new Value(to_check), deopt_id),
+ deopt_environment,
+ Definition::kEffect);
+ }
+}
+
+
+void FlowGraphOptimizer::AddCheckClass(Definition* to_check,
+ const ICData& unary_checks,
+ intptr_t deopt_id,
+ Environment* deopt_environment,
+ Instruction* insert_before) {
// Type propagation has not run yet, we cannot eliminate the check.
- const ICData& unary_checks =
- ICData::ZoneHandle(call->ic_data()->AsUnaryClassChecks());
Instruction* check = NULL;
if ((unary_checks.NumberOfChecks() == 1) &&
(unary_checks.GetReceiverClassIdAt(0) == kSmiCid)) {
- check = new CheckSmiInstr(value, call->deopt_id());
+ check = new CheckSmiInstr(new Value(to_check), deopt_id);
} else {
- check = new CheckClassInstr(value, call->deopt_id(), unary_checks);
+ check = new CheckClassInstr(new Value(to_check), deopt_id, unary_checks);
}
- InsertBefore(call, check, call->env(), Definition::kEffect);
+ InsertBefore(insert_before, check, deopt_environment, Definition::kEffect);
+}
+
+
+void FlowGraphOptimizer::AddReceiverCheck(InstanceCallInstr* call) {
+ AddCheckClass(call->ArgumentAt(0),
+ ICData::ZoneHandle(call->ic_data()->AsUnaryClassChecks()),
+ call->deopt_id(),
+ call->env(),
+ call);
}
@@ -530,46 +646,43 @@
intptr_t FlowGraphOptimizer::PrepareIndexedOp(InstanceCallInstr* call,
intptr_t class_id,
- Value** array,
- Value** index) {
- *array = call->ArgumentAt(0)->value();
- *index = call->ArgumentAt(1)->value();
+ Definition** array,
+ Definition** index) {
// Insert class check and index smi checks and attach a copy of the
// original environment because the operation can still deoptimize.
- AddCheckClass(call, (*array)->Copy());
+ AddReceiverCheck(call);
InsertBefore(call,
- new CheckSmiInstr((*index)->Copy(), call->deopt_id()),
+ new CheckSmiInstr(new Value(*index), call->deopt_id()),
call->env(),
Definition::kEffect);
+
// If both index and array are constants, then do a compile-time check.
// TODO(srdjan): Remove once constant propagation handles bounds checks.
bool skip_check = false;
- if ((*array)->BindsToConstant() && (*index)->BindsToConstant()) {
- ConstantInstr* array_def = (*array)->definition()->AsConstant();
+ if ((*array)->IsConstant() && (*index)->IsConstant()) {
const ImmutableArray& constant_array =
- ImmutableArray::Cast(array_def->value());
- ConstantInstr* index_def = (*index)->definition()->AsConstant();
- if (index_def->value().IsSmi()) {
- intptr_t constant_index = Smi::Cast(index_def->value()).Value();
- skip_check = (constant_index < constant_array.Length());
- }
+ ImmutableArray::Cast((*array)->AsConstant()->value());
+ const Object& constant_index = (*index)->AsConstant()->value();
+ skip_check = constant_index.IsSmi() &&
+ (Smi::Cast(constant_index).Value() < constant_array.Length());
}
if (!skip_check) {
// Insert array length load and bounds check.
const bool is_immutable =
CheckArrayBoundInstr::IsFixedLengthArrayType(class_id);
- LoadFieldInstr* length = new LoadFieldInstr(
- (*array)->Copy(),
- CheckArrayBoundInstr::LengthOffsetFor(class_id),
- Type::ZoneHandle(Type::SmiType()),
- is_immutable);
+ LoadFieldInstr* length =
+ new LoadFieldInstr(new Value(*array),
+ CheckArrayBoundInstr::LengthOffsetFor(class_id),
+ Type::ZoneHandle(Type::SmiType()),
+ is_immutable);
length->set_result_cid(kSmiCid);
length->set_recognized_kind(
LoadFieldInstr::RecognizedKindFromArrayCid(class_id));
InsertBefore(call, length, NULL, Definition::kValue);
+
InsertBefore(call,
new CheckArrayBoundInstr(new Value(length),
- (*index)->Copy(),
+ new Value(*index),
class_id,
call),
call->env(),
@@ -578,12 +691,12 @@
if (class_id == kGrowableObjectArrayCid) {
// Insert data elements load.
LoadFieldInstr* elements =
- new LoadFieldInstr((*array)->Copy(),
+ new LoadFieldInstr(new Value(*array),
GrowableObjectArray::data_offset(),
Type::ZoneHandle(Type::DynamicType()));
elements->set_result_cid(kArrayCid);
InsertBefore(call, elements, NULL, Definition::kValue);
- *array = new Value(elements);
+ *array = elements;
return kArrayCid;
}
return class_id;
@@ -651,9 +764,73 @@
return false;
}
+ BuildStoreIndexed(call, value_check, class_id);
+ return true;
+}
+
+
+bool FlowGraphOptimizer::TryInlineByteArraySetIndexed(InstanceCallInstr* call) {
+ const intptr_t class_id = ReceiverClassId(call);
+ ICData& value_check = ICData::ZoneHandle();
+ switch (class_id) {
+ case kInt8ArrayCid:
+ case kUint8ArrayCid:
+ case kUint8ClampedArrayCid:
+ case kExternalUint8ArrayCid:
+ case kExternalUint8ClampedArrayCid:
+ case kInt16ArrayCid:
+ case kUint16ArrayCid: {
+ // Check that value is always smi.
+ value_check = ICData::New(Function::Handle(),
+ String::Handle(),
+ Isolate::kNoDeoptId,
+ 1);
+ value_check.AddReceiverCheck(kSmiCid, Function::Handle());
+ break;
+ }
+ case kInt32ArrayCid:
+ case kUint32ArrayCid:
+ // Check if elements fit into a smi or the platform supports unboxed
+ // mints.
+ if ((kSmiBits < 32) && !FlowGraphCompiler::SupportsUnboxedMints()) {
+ return false;
+ }
+ // We don't have ICData for the value stored, so we optimistically assume
+ // smis first. If we ever deoptimized here, we require to unbox the value
+ // before storing to handle the mint case, too.
+ if (call->ic_data()->deopt_reason() == kDeoptUnknown) {
+ value_check = ICData::New(Function::Handle(),
+ String::Handle(),
+ Isolate::kNoDeoptId,
+ 1);
+ value_check.AddReceiverCheck(kSmiCid, Function::Handle());
+ }
+ break;
+ case kFloat32ArrayCid:
+ case kFloat64ArrayCid: {
+ // Check that value is always double.
+ value_check = ICData::New(Function::Handle(),
+ String::Handle(),
+ Isolate::kNoDeoptId,
+ 1);
+ value_check.AddReceiverCheck(kDoubleCid, Function::Handle());
+ break;
+ }
+ default:
+ return false;
+ }
+ BuildStoreIndexed(call, value_check, class_id);
+ return true;
+}
+
+
+void FlowGraphOptimizer::BuildStoreIndexed(InstanceCallInstr* call,
+ const ICData& value_check,
+ intptr_t class_id) {
+ Definition* array = call->ArgumentAt(0);
+ Definition* index = call->ArgumentAt(1);
+ Definition* stored_value = call->ArgumentAt(2);
if (FLAG_enable_type_checks) {
- Value* array = call->ArgumentAt(0)->value();
- Value* value = call->ArgumentAt(2)->value();
// Only type check for the value. A type check for the index is not
// needed here because we insert a deoptimizing smi-check for the case
// the index is not a smi.
@@ -661,8 +838,8 @@
Function::ZoneHandle(call->ic_data()->GetTargetAt(0));
const AbstractType& value_type =
AbstractType::ZoneHandle(target.ParameterTypeAt(2));
- Value* instantiator = NULL;
- Value* type_args = NULL;
+ Definition* instantiator = NULL;
+ Definition* type_args = NULL;
switch (class_id) {
case kArrayCid:
case kGrowableObjectArrayCid: {
@@ -670,12 +847,12 @@
intptr_t type_arguments_field_offset =
instantiator_class.type_arguments_field_offset();
LoadFieldInstr* load_type_args =
- new LoadFieldInstr(array->Copy(),
+ new LoadFieldInstr(new Value(array),
type_arguments_field_offset,
Type::ZoneHandle()); // No type.
InsertBefore(call, load_type_args, NULL, Definition::kValue);
- instantiator = array->Copy();
- type_args = new Value(load_type_args);
+ instantiator = array;
+ type_args = load_type_args;
break;
}
case kInt8ArrayCid:
@@ -691,8 +868,7 @@
// Fall through.
case kFloat32ArrayCid:
case kFloat64ArrayCid: {
- instantiator = new Value(flow_graph_->constant_null());
- type_args = new Value(flow_graph_->constant_null());
+ type_args = instantiator = flow_graph_->constant_null();
ASSERT((class_id != kFloat32ArrayCid && class_id != kFloat64ArrayCid) ||
value_type.IsDoubleType());
ASSERT(value_type.IsInstantiated());
@@ -704,9 +880,9 @@
}
AssertAssignableInstr* assert_value =
new AssertAssignableInstr(call->token_pos(),
- value->Copy(),
- instantiator,
- type_args,
+ new Value(stored_value),
+ new Value(instantiator),
+ new Value(type_args),
value_type,
Symbols::Value());
// Newly inserted instructions that can deoptimize or throw an exception
@@ -716,36 +892,22 @@
InsertBefore(call, assert_value, call->env(), Definition::kValue);
}
- Value* array = NULL;
- Value* index = NULL;
intptr_t array_cid = PrepareIndexedOp(call, class_id, &array, &index);
- Value* value = call->ArgumentAt(2)->value();
// Check if store barrier is needed.
- bool needs_store_barrier = true;
+ bool needs_store_barrier = !RawObject::IsByteArrayClassId(array_cid);
if (!value_check.IsNull()) {
needs_store_barrier = false;
- if (value_check.NumberOfChecks() == 1 &&
- value_check.GetReceiverClassIdAt(0) == kSmiCid) {
- InsertBefore(call,
- new CheckSmiInstr(value->Copy(), call->deopt_id()),
- call->env(),
- Definition::kEffect);
- } else {
- InsertBefore(call,
- new CheckClassInstr(value->Copy(),
- call->deopt_id(),
- value_check),
- call->env(),
- Definition::kEffect);
- }
+ AddCheckClass(stored_value, value_check, call->deopt_id(), call->env(),
+ call);
}
- Definition* array_op =
- new StoreIndexedInstr(array, index, value,
- needs_store_barrier, array_cid, call->deopt_id());
- call->ReplaceWith(array_op, current_iterator());
- RemovePushArguments(call);
- return true;
+ Definition* array_op = new StoreIndexedInstr(new Value(array),
+ new Value(index),
+ new Value(stored_value),
+ needs_store_barrier,
+ array_cid,
+ call->deopt_id());
+ ReplaceCall(call, array_op);
}
@@ -787,49 +949,20 @@
default:
return false;
}
- Value* array = NULL;
- Value* index = NULL;
+ Definition* array = call->ArgumentAt(0);
+ Definition* index = call->ArgumentAt(1);
intptr_t array_cid = PrepareIndexedOp(call, class_id, &array, &index);
Definition* array_op =
- new LoadIndexedInstr(array,
- index,
+ new LoadIndexedInstr(new Value(array),
+ new Value(index),
FlowGraphCompiler::ElementSizeFor(array_cid),
array_cid,
deopt_id);
- call->ReplaceWith(array_op, current_iterator());
- RemovePushArguments(call);
+ ReplaceCall(call, array_op);
return true;
}
-void FlowGraphOptimizer::InsertBefore(Instruction* next,
- Instruction* instr,
- Environment* env,
- Definition::UseKind use_kind) {
- if (env != NULL) env->DeepCopyTo(instr);
- if (use_kind == Definition::kValue) {
- ASSERT(instr->IsDefinition());
- instr->AsDefinition()->set_ssa_temp_index(
- flow_graph_->alloc_ssa_temp_index());
- }
- instr->InsertBefore(next);
-}
-
-
-void FlowGraphOptimizer::InsertAfter(Instruction* prev,
- Instruction* instr,
- Environment* env,
- Definition::UseKind use_kind) {
- if (env != NULL) env->DeepCopyTo(instr);
- if (use_kind == Definition::kValue) {
- ASSERT(instr->IsDefinition());
- instr->AsDefinition()->set_ssa_temp_index(
- flow_graph_->alloc_ssa_temp_index());
- }
- instr->InsertAfter(prev);
-}
-
-
bool FlowGraphOptimizer::TryReplaceWithBinaryOp(InstanceCallInstr* call,
Token::Kind op_kind) {
intptr_t operands_type = kIllegalCid;
@@ -926,89 +1059,77 @@
break;
default:
UNREACHABLE();
- };
+ }
ASSERT(call->ArgumentCount() == 2);
+ Definition* left = call->ArgumentAt(0);
+ Definition* right = call->ArgumentAt(1);
if (operands_type == kDoubleCid) {
- Value* left = call->ArgumentAt(0)->value();
- Value* right = call->ArgumentAt(1)->value();
-
- // Check that either left or right are not a smi. Result or a
+ // Check that either left or right are not a smi. Result of a
// binary operation with two smis is a smi not a double.
InsertBefore(call,
- new CheckEitherNonSmiInstr(left->Copy(),
- right->Copy(),
+ new CheckEitherNonSmiInstr(new Value(left),
+ new Value(right),
call),
call->env(),
Definition::kEffect);
BinaryDoubleOpInstr* double_bin_op =
- new BinaryDoubleOpInstr(op_kind, left->Copy(), right->Copy(), call);
- call->ReplaceWith(double_bin_op, current_iterator());
- RemovePushArguments(call);
+ new BinaryDoubleOpInstr(op_kind, new Value(left), new Value(right),
+ call);
+ ReplaceCall(call, double_bin_op);
} else if (operands_type == kMintCid) {
if (!FlowGraphCompiler::SupportsUnboxedMints()) return false;
- Value* left = call->ArgumentAt(0)->value();
- Value* right = call->ArgumentAt(1)->value();
if ((op_kind == Token::kSHR) || (op_kind == Token::kSHL)) {
ShiftMintOpInstr* shift_op =
- new ShiftMintOpInstr(op_kind, left, right, call);
- call->ReplaceWith(shift_op, current_iterator());
+ new ShiftMintOpInstr(op_kind, new Value(left), new Value(right),
+ call);
+ ReplaceCall(call, shift_op);
} else {
BinaryMintOpInstr* bin_op =
- new BinaryMintOpInstr(op_kind, left, right, call);
- call->ReplaceWith(bin_op, current_iterator());
+ new BinaryMintOpInstr(op_kind, new Value(left), new Value(right),
+ call);
+ ReplaceCall(call, bin_op);
}
- RemovePushArguments(call);
} else if (op_kind == Token::kMOD) {
// TODO(vegorov): implement fast path code for modulo.
ASSERT(operands_type == kSmiCid);
- if (!call->ArgumentAt(1)->value()->BindsToConstant()) return false;
- const Object& obj = call->ArgumentAt(1)->value()->BoundConstant();
+ if (!right->IsConstant()) return false;
+ const Object& obj = right->AsConstant()->value();
if (!obj.IsSmi()) return false;
const intptr_t value = Smi::Cast(obj).Value();
- if ((value > 0) && Utils::IsPowerOfTwo(value)) {
- Value* left = call->ArgumentAt(0)->value();
- // Insert smi check and attach a copy of the original
- // environment because the smi operation can still deoptimize.
- InsertBefore(call,
- new CheckSmiInstr(left->Copy(), call->deopt_id()),
- call->env(),
- Definition::kEffect);
- ConstantInstr* c = new ConstantInstr(Smi::Handle(Smi::New(value - 1)));
- InsertBefore(call, c, NULL, Definition::kValue);
- BinarySmiOpInstr* bin_op =
- new BinarySmiOpInstr(Token::kBIT_AND, call, left, new Value(c));
- call->ReplaceWith(bin_op, current_iterator());
- RemovePushArguments(call);
- } else {
- // Did not replace.
- return false;
- }
+ if ((value <= 0) || !Utils::IsPowerOfTwo(value)) return false;
+
+ // Insert smi check and attach a copy of the original environment
+ // because the smi operation can still deoptimize.
+ InsertBefore(call,
+ new CheckSmiInstr(new Value(left), call->deopt_id()),
+ call->env(),
+ Definition::kEffect);
+ ConstantInstr* constant =
+ new ConstantInstr(Smi::Handle(Smi::New(value - 1)));
+ InsertBefore(call, constant, NULL, Definition::kValue);
+ BinarySmiOpInstr* bin_op =
+ new BinarySmiOpInstr(Token::kBIT_AND, call,
+ new Value(left),
+ new Value(constant));
+ ReplaceCall(call, bin_op);
} else {
ASSERT(operands_type == kSmiCid);
- Value* left = call->ArgumentAt(0)->value();
- Value* right = call->ArgumentAt(1)->value();
// Insert two smi checks and attach a copy of the original
// environment because the smi operation can still deoptimize.
- InsertBefore(call,
- new CheckSmiInstr(left->Copy(), call->deopt_id()),
- call->env(),
- Definition::kEffect);
- InsertBefore(call,
- new CheckSmiInstr(right->Copy(), call->deopt_id()),
- call->env(),
- Definition::kEffect);
- if (left->BindsToConstant() &&
+ AddCheckSmi(left, call->deopt_id(), call->env(), call);
+ AddCheckSmi(right, call->deopt_id(), call->env(), call);
+ if (left->IsConstant() &&
((op_kind == Token::kADD) || (op_kind == Token::kMUL))) {
// Constant should be on the right side.
- Value* temp = left;
+ Definition* temp = left;
left = right;
right = temp;
}
- BinarySmiOpInstr* bin_op = new BinarySmiOpInstr(op_kind, call, left, right);
- call->ReplaceWith(bin_op, current_iterator());
- RemovePushArguments(call);
+ BinarySmiOpInstr* bin_op =
+ new BinarySmiOpInstr(op_kind, call, new Value(left), new Value(right));
+ ReplaceCall(call, bin_op);
}
return true;
}
@@ -1017,35 +1138,32 @@
bool FlowGraphOptimizer::TryReplaceWithUnaryOp(InstanceCallInstr* call,
Token::Kind op_kind) {
ASSERT(call->ArgumentCount() == 1);
+ Definition* input = call->ArgumentAt(0);
Definition* unary_op = NULL;
if (HasOnlyOneSmi(*call->ic_data())) {
- Value* value = call->ArgumentAt(0)->value();
InsertBefore(call,
- new CheckSmiInstr(value->Copy(), call->deopt_id()),
+ new CheckSmiInstr(new Value(input), call->deopt_id()),
call->env(),
Definition::kEffect);
- unary_op = new UnarySmiOpInstr(op_kind, call, value);
+ unary_op = new UnarySmiOpInstr(op_kind, call, new Value(input));
} else if ((op_kind == Token::kBIT_NOT) &&
HasOnlySmiOrMint(*call->ic_data()) &&
FlowGraphCompiler::SupportsUnboxedMints()) {
- Value* value = call->ArgumentAt(0)->value();
- unary_op = new UnaryMintOpInstr(op_kind, value, call);
+ unary_op = new UnaryMintOpInstr(op_kind, new Value(input), call);
} else if (HasOnlyOneDouble(*call->ic_data()) &&
(op_kind == Token::kNEGATE)) {
- Value* value = call->ArgumentAt(0)->value();
- AddCheckClass(call, value->Copy());
+ AddReceiverCheck(call);
ConstantInstr* minus_one =
new ConstantInstr(Double::ZoneHandle(Double::NewCanonical(-1)));
InsertBefore(call, minus_one, NULL, Definition::kValue);
unary_op = new BinaryDoubleOpInstr(Token::kMUL,
- value,
+ new Value(input),
new Value(minus_one),
call);
}
if (unary_op == NULL) return false;
- call->ReplaceWith(unary_op, current_iterator());
- RemovePushArguments(call);
+ ReplaceCall(call, unary_op);
return true;
}
@@ -1071,7 +1189,7 @@
bool FlowGraphOptimizer::InstanceCallNeedsClassCheck(
InstanceCallInstr* call) const {
if (!FLAG_use_cha) return true;
- Definition* callee_receiver = call->ArgumentAt(0)->value()->definition();
+ Definition* callee_receiver = call->ArgumentAt(0);
ASSERT(callee_receiver != NULL);
const Function& function = flow_graph_->parsed_function().function();
if (function.IsDynamicFunction() &&
@@ -1087,7 +1205,7 @@
bool FlowGraphOptimizer::MethodExtractorNeedsClassCheck(
InstanceCallInstr* call) const {
if (!FLAG_use_cha) return true;
- Definition* callee_receiver = call->ArgumentAt(0)->value()->definition();
+ Definition* callee_receiver = call->ArgumentAt(0);
ASSERT(callee_receiver != NULL);
const Function& function = flow_graph_->parsed_function().function();
if (function.IsDynamicFunction() &&
@@ -1115,18 +1233,20 @@
ASSERT(!field.IsNull());
if (InstanceCallNeedsClassCheck(call)) {
- AddCheckClass(call, call->ArgumentAt(0)->value()->Copy());
+ AddReceiverCheck(call);
}
- // Detach environment from the original instruction because it can't
- // deoptimize.
- call->set_env(NULL);
LoadFieldInstr* load = new LoadFieldInstr(
- call->ArgumentAt(0)->value(),
+ new Value(call->ArgumentAt(0)),
field.Offset(),
AbstractType::ZoneHandle(field.type()),
field.is_final());
- call->ReplaceWith(load, current_iterator());
- RemovePushArguments(call);
+ // Detach environment from the original instruction because it can't
+ // deoptimize.
+ for (Environment::DeepIterator it(call->env()); !it.Done(); it.Advance()) {
+ it.CurrentValue()->RemoveFromUseList();
+ }
+ call->set_env(NULL);
+ ReplaceCall(call, load);
}
@@ -1134,29 +1254,26 @@
intptr_t length_offset,
bool is_immutable,
MethodRecognizer::Kind kind) {
- // Check receiver class.
- AddCheckClass(call, call->ArgumentAt(0)->value()->Copy());
+ AddReceiverCheck(call);
LoadFieldInstr* load = new LoadFieldInstr(
- call->ArgumentAt(0)->value(),
+ new Value(call->ArgumentAt(0)),
length_offset,
Type::ZoneHandle(Type::SmiType()),
is_immutable);
load->set_result_cid(kSmiCid);
load->set_recognized_kind(kind);
- call->ReplaceWith(load, current_iterator());
- RemovePushArguments(call);
+ ReplaceCall(call, load);
}
void FlowGraphOptimizer::InlineGrowableArrayCapacityGetter(
InstanceCallInstr* call) {
- // Check receiver class.
- AddCheckClass(call, call->ArgumentAt(0)->value()->Copy());
+ AddReceiverCheck(call);
// TODO(srdjan): type of load should be GrowableObjectArrayType.
LoadFieldInstr* data_load = new LoadFieldInstr(
- call->ArgumentAt(0)->value(),
+ new Value(call->ArgumentAt(0)),
Array::data_offset(),
Type::ZoneHandle(Type::DynamicType()));
data_load->set_result_cid(kArrayCid);
@@ -1169,18 +1286,17 @@
length_load->set_result_cid(kSmiCid);
length_load->set_recognized_kind(MethodRecognizer::kObjectArrayLength);
- call->ReplaceWith(length_load, current_iterator());
- RemovePushArguments(call);
+ ReplaceCall(call, length_load);
}
-static LoadFieldInstr* BuildLoadStringLength(Value* str) {
+static LoadFieldInstr* BuildLoadStringLength(Definition* str) {
// Treat length loads as mutable (i.e. affected by side effects) to avoid
// hoisting them since we can't hoist the preceding class-check. This
// is because of externalization of strings that affects their class-id.
const bool is_immutable = false;
LoadFieldInstr* load = new LoadFieldInstr(
- str,
+ new Value(str),
String::length_offset(),
Type::ZoneHandle(Type::SmiType()),
is_immutable);
@@ -1191,20 +1307,16 @@
void FlowGraphOptimizer::InlineStringLengthGetter(InstanceCallInstr* call) {
- // Check receiver class.
- AddCheckClass(call, call->ArgumentAt(0)->value()->Copy());
-
- LoadFieldInstr* load = BuildLoadStringLength(call->ArgumentAt(0)->value());
- call->ReplaceWith(load, current_iterator());
- RemovePushArguments(call);
+ AddReceiverCheck(call);
+ LoadFieldInstr* load = BuildLoadStringLength(call->ArgumentAt(0));
+ ReplaceCall(call, load);
}
void FlowGraphOptimizer::InlineStringIsEmptyGetter(InstanceCallInstr* call) {
- // Check receiver class.
- AddCheckClass(call, call->ArgumentAt(0)->value()->Copy());
+ AddReceiverCheck(call);
- LoadFieldInstr* load = BuildLoadStringLength(call->ArgumentAt(0)->value());
+ LoadFieldInstr* load = BuildLoadStringLength(call->ArgumentAt(0));
InsertBefore(call, load, NULL, Definition::kValue);
ConstantInstr* zero = new ConstantInstr(Smi::Handle(Smi::New(0)));
@@ -1214,8 +1326,7 @@
new StrictCompareInstr(Token::kEQ_STRICT,
new Value(load),
new Value(zero));
- call->ReplaceWith(compare, current_iterator());
- RemovePushArguments(call);
+ ReplaceCall(call, compare);
}
@@ -1306,40 +1417,37 @@
LoadIndexedInstr* FlowGraphOptimizer::BuildStringCharCodeAt(
InstanceCallInstr* call,
intptr_t cid) {
- Value* str = call->ArgumentAt(0)->value();
- Value* index = call->ArgumentAt(1)->value();
- AddCheckClass(call, str->Copy());
+ Definition* str = call->ArgumentAt(0);
+ Definition* index = call->ArgumentAt(1);
+ AddReceiverCheck(call);
InsertBefore(call,
- new CheckSmiInstr(index->Copy(), call->deopt_id()),
+ new CheckSmiInstr(new Value(index), call->deopt_id()),
call->env(),
Definition::kEffect);
// If both index and string are constants, then do a compile-time check.
// TODO(srdjan): Remove once constant propagation handles bounds checks.
bool skip_check = false;
- if (str->BindsToConstant() && index->BindsToConstant()) {
- ConstantInstr* string_def = str->definition()->AsConstant();
+ if (str->IsConstant() && index->IsConstant()) {
const String& constant_string =
- String::Cast(string_def->value());
- ConstantInstr* index_def = index->definition()->AsConstant();
- if (index_def->value().IsSmi()) {
- intptr_t constant_index = Smi::Cast(index_def->value()).Value();
- skip_check = (constant_index < constant_string.Length());
- }
+ String::Cast(str->AsConstant()->value());
+ const Object& constant_index = index->AsConstant()->value();
+ skip_check = constant_index.IsSmi() &&
+ (Smi::Cast(constant_index).Value() < constant_string.Length());
}
if (!skip_check) {
// Insert bounds check.
- LoadFieldInstr* length = BuildLoadStringLength(str->Copy());
+ LoadFieldInstr* length = BuildLoadStringLength(str);
InsertBefore(call, length, NULL, Definition::kValue);
InsertBefore(call,
new CheckArrayBoundInstr(new Value(length),
- index->Copy(),
+ new Value(index),
cid,
call),
call->env(),
Definition::kEffect);
}
- return new LoadIndexedInstr(str,
- index,
+ return new LoadIndexedInstr(new Value(str),
+ new Value(index),
FlowGraphCompiler::ElementSizeFor(cid),
cid,
Isolate::kNoDeoptId); // Can't deoptimize.
@@ -1349,16 +1457,15 @@
void FlowGraphOptimizer::ReplaceWithMathCFunction(
InstanceCallInstr* call,
MethodRecognizer::Kind recognized_kind) {
- AddCheckClass(call, call->ArgumentAt(0)->value()->Copy());
+ AddReceiverCheck(call);
ZoneGrowableArray<Value*>* args =
new ZoneGrowableArray<Value*>(call->ArgumentCount());
for (intptr_t i = 0; i < call->ArgumentCount(); i++) {
- args->Add(call->ArgumentAt(i)->value());
+ args->Add(new Value(call->ArgumentAt(i)));
}
InvokeMathCFunctionInstr* invoke =
new InvokeMathCFunctionInstr(args, call, recognized_kind);
- call->ReplaceWith(invoke, current_iterator());
- RemovePushArguments(call);
+ ReplaceCall(call, invoke);
}
@@ -1390,18 +1497,51 @@
// No type feedback collected or multiple targets found.
return false;
}
+
Function& target = Function::Handle();
GrowableArray<intptr_t> class_ids;
ic_data.GetCheckAt(0, &class_ids, &target);
MethodRecognizer::Kind recognized_kind =
MethodRecognizer::RecognizeKind(target);
+
+ // Byte array access.
+ switch (recognized_kind) {
+ case MethodRecognizer::kFloat32ArrayGetIndexed:
+ case MethodRecognizer::kFloat64ArrayGetIndexed:
+ case MethodRecognizer::kInt8ArrayGetIndexed:
+ case MethodRecognizer::kUint8ArrayGetIndexed:
+ case MethodRecognizer::kUint8ClampedArrayGetIndexed:
+ case MethodRecognizer::kExternalUint8ArrayGetIndexed:
+ case MethodRecognizer::kExternalUint8ClampedArrayGetIndexed:
+ case MethodRecognizer::kInt16ArrayGetIndexed:
+ case MethodRecognizer::kUint16ArrayGetIndexed:
+ case MethodRecognizer::kInt32ArrayGetIndexed:
+ case MethodRecognizer::kUint32ArrayGetIndexed:
+ return TryReplaceWithLoadIndexed(call);
+
+ case MethodRecognizer::kFloat32ArraySetIndexed:
+ case MethodRecognizer::kFloat64ArraySetIndexed:
+ case MethodRecognizer::kInt8ArraySetIndexed:
+ case MethodRecognizer::kUint8ArraySetIndexed:
+ case MethodRecognizer::kUint8ClampedArraySetIndexed:
+ case MethodRecognizer::kExternalUint8ArraySetIndexed:
+ case MethodRecognizer::kExternalUint8ClampedArraySetIndexed:
+ case MethodRecognizer::kInt16ArraySetIndexed:
+ case MethodRecognizer::kUint16ArraySetIndexed:
+ case MethodRecognizer::kInt32ArraySetIndexed:
+ case MethodRecognizer::kUint32ArraySetIndexed:
+ return TryInlineByteArraySetIndexed(call);
+
+ default:
+ break;
+ }
+
if ((recognized_kind == MethodRecognizer::kStringBaseCharCodeAt) &&
(ic_data.NumberOfChecks() == 1) &&
((class_ids[0] == kOneByteStringCid) ||
(class_ids[0] == kTwoByteStringCid))) {
LoadIndexedInstr* instr = BuildStringCharCodeAt(call, class_ids[0]);
- call->ReplaceWith(instr, current_iterator());
- RemovePushArguments(call);
+ ReplaceCall(call, instr);
return true;
}
if ((recognized_kind == MethodRecognizer::kStringBaseCharAt) &&
@@ -1414,8 +1554,7 @@
StringFromCharCodeInstr* char_at =
new StringFromCharCodeInstr(new Value(load_char_code),
kOneByteStringCid);
- call->ReplaceWith(char_at, current_iterator());
- RemovePushArguments(call);
+ ReplaceCall(call, char_at);
return true;
}
@@ -1431,20 +1570,19 @@
if (class_ids[0] == kDoubleCid) {
switch (recognized_kind) {
case MethodRecognizer::kDoubleToInteger: {
- AddCheckClass(call, call->ArgumentAt(0)->value()->Copy());
+ AddReceiverCheck(call);
ASSERT(call->HasICData());
const ICData& ic_data = *call->ic_data();
+ Definition* input = call->ArgumentAt(0);
Definition* d2i_instr = NULL;
if (ic_data.deopt_reason() == kDeoptDoubleToSmi) {
// Do not repeatedly deoptimize because result didn't fit into Smi.
- d2i_instr = new DoubleToIntegerInstr(call->ArgumentAt(0)->value(),
- call);
+ d2i_instr = new DoubleToIntegerInstr(new Value(input), call);
} else {
// Optimistically assume result fits into Smi.
- d2i_instr = new DoubleToSmiInstr(call->ArgumentAt(0)->value(), call);
+ d2i_instr = new DoubleToSmiInstr(new Value(input), call);
}
- call->ReplaceWith(d2i_instr, current_iterator());
- RemovePushArguments(call);
+ ReplaceCall(call, d2i_instr);
return true;
}
case MethodRecognizer::kDoubleMod:
@@ -1458,13 +1596,12 @@
if (!CPUFeatures::double_truncate_round_supported()) {
ReplaceWithMathCFunction(call, recognized_kind);
} else {
- AddCheckClass(call, call->ArgumentAt(0)->value()->Copy());
+ AddReceiverCheck(call);
DoubleToDoubleInstr* d2d_instr =
- new DoubleToDoubleInstr(call->ArgumentAt(0)->value(),
+ new DoubleToDoubleInstr(new Value(call->ArgumentAt(0)),
call,
recognized_kind);
- call->ReplaceWith(d2d_instr, current_iterator());
- RemovePushArguments(call);
+ ReplaceCall(call, d2d_instr);
}
return true;
default:
@@ -1490,9 +1627,19 @@
array_op = BuildByteArrayViewLoad(call, class_ids[0], kUint16ArrayCid);
break;
case MethodRecognizer::kByteArrayBaseGetInt32:
+ // Check if elements fit into a smi or the platform supports unboxed
+ // mints.
+ if ((kSmiBits < 32) && !FlowGraphCompiler::SupportsUnboxedMints()) {
+ return false;
+ }
array_op = BuildByteArrayViewLoad(call, class_ids[0], kInt32ArrayCid);
break;
case MethodRecognizer::kByteArrayBaseGetUint32:
+ // Check if elements fit into a smi or the platform supports unboxed
+ // mints.
+ if ((kSmiBits < 32) && !FlowGraphCompiler::SupportsUnboxedMints()) {
+ return false;
+ }
array_op = BuildByteArrayViewLoad(call, class_ids[0], kUint32ArrayCid);
break;
case MethodRecognizer::kByteArrayBaseGetFloat32:
@@ -1506,8 +1653,7 @@
return false;
}
ASSERT(array_op != NULL);
- call->ReplaceWith(array_op, current_iterator());
- RemovePushArguments(call);
+ ReplaceCall(call, array_op);
return true;
}
return false;
@@ -1518,50 +1664,50 @@
InstanceCallInstr* call,
intptr_t receiver_cid,
intptr_t view_cid) {
- Value* array = call->ArgumentAt(0)->value();
- Value* byte_index = call->ArgumentAt(1)->value();
+ Definition* array = call->ArgumentAt(0);
+ Definition* byte_index = call->ArgumentAt(1);
- AddCheckClass(call, array->Copy());
- const bool is_immutable = true;
- LoadFieldInstr* length = new LoadFieldInstr(
- array->Copy(),
- CheckArrayBoundInstr::LengthOffsetFor(receiver_cid),
- Type::ZoneHandle(Type::SmiType()),
- is_immutable);
- length->set_result_cid(kSmiCid);
- length->set_recognized_kind(
- LoadFieldInstr::RecognizedKindFromArrayCid(receiver_cid));
- InsertBefore(call, length, NULL, Definition::kValue);
+ AddReceiverCheck(call);
+ const bool is_immutable = true;
+ LoadFieldInstr* length = new LoadFieldInstr(
+ new Value(array),
+ CheckArrayBoundInstr::LengthOffsetFor(receiver_cid),
+ Type::ZoneHandle(Type::SmiType()),
+ is_immutable);
+ length->set_result_cid(kSmiCid);
+ length->set_recognized_kind(
+ LoadFieldInstr::RecognizedKindFromArrayCid(receiver_cid));
+ InsertBefore(call, length, NULL, Definition::kValue);
- // len_in_bytes = length * kBytesPerElement(receiver)
- intptr_t element_size = FlowGraphCompiler::ElementSizeFor(receiver_cid);
- ConstantInstr* bytes_per_element =
- new ConstantInstr(Smi::Handle(Smi::New(element_size)));
- InsertBefore(call, bytes_per_element, NULL, Definition::kValue);
- BinarySmiOpInstr* len_in_bytes =
- new BinarySmiOpInstr(Token::kMUL,
- call,
- new Value(length),
- new Value(bytes_per_element));
- InsertBefore(call, len_in_bytes, call->env(), Definition::kValue);
+ // len_in_bytes = length * kBytesPerElement(receiver)
+ intptr_t element_size = FlowGraphCompiler::ElementSizeFor(receiver_cid);
+ ConstantInstr* bytes_per_element =
+ new ConstantInstr(Smi::Handle(Smi::New(element_size)));
+ InsertBefore(call, bytes_per_element, NULL, Definition::kValue);
+ BinarySmiOpInstr* len_in_bytes =
+ new BinarySmiOpInstr(Token::kMUL,
+ call,
+ new Value(length),
+ new Value(bytes_per_element));
+ InsertBefore(call, len_in_bytes, call->env(), Definition::kValue);
// Check byte_index < len_in_bytes.
- InsertBefore(call,
- new CheckArrayBoundInstr(new Value(len_in_bytes),
- byte_index->Copy(),
- receiver_cid,
- call),
- call->env(),
- Definition::kEffect);
+ InsertBefore(call,
+ new CheckArrayBoundInstr(new Value(len_in_bytes),
+ new Value(byte_index),
+ receiver_cid,
+ call),
+ call->env(),
+ Definition::kEffect);
- // TODO(fschneider): Optimistically build smi load for Int32 and Uint32
- // loads on ia32 like we do for normal array loads, and only revert to
- // mint case after deoptimizing here.
- return new LoadIndexedInstr(array,
- byte_index,
- 1, // Index scale.
- view_cid,
- Isolate::kNoDeoptId); // Can't deoptimize.
+ // TODO(fschneider): Optimistically build smi load for Int32 and Uint32
+ // loads on ia32 like we do for normal array loads, and only revert to
+ // mint case after deoptimizing here.
+ return new LoadIndexedInstr(new Value(array),
+ new Value(byte_index),
+ 1, // Index scale.
+ view_cid,
+ Isolate::kNoDeoptId); // Can't deoptimize.
}
@@ -1597,37 +1743,35 @@
// TODO(srdjan): Use ICData to check if always true or false.
void FlowGraphOptimizer::ReplaceWithInstanceOf(InstanceCallInstr* call) {
ASSERT(Token::IsTypeTestOperator(call->token_kind()));
- Value* left_val = call->ArgumentAt(0)->value();
- Value* instantiator_val = call->ArgumentAt(1)->value();
- Value* type_args_val = call->ArgumentAt(2)->value();
+ Definition* left = call->ArgumentAt(0);
+ Definition* instantiator = call->ArgumentAt(1);
+ Definition* type_args = call->ArgumentAt(2);
const AbstractType& type =
- AbstractType::Cast(call->ArgumentAt(3)->value()->BoundConstant());
+ AbstractType::Cast(call->ArgumentAt(3)->AsConstant()->value());
const bool negate =
- Bool::Cast(call->ArgumentAt(4)->value()->BoundConstant()).value();
+ Bool::Cast(call->ArgumentAt(4)->AsConstant()->value()).value();
const ICData& unary_checks =
ICData::ZoneHandle(call->ic_data()->AsUnaryClassChecks());
if (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks) {
Bool& as_bool = Bool::ZoneHandle(InstanceOfAsBool(unary_checks, type));
if (!as_bool.IsNull()) {
- AddCheckClass(call, left_val->Copy());
+ AddReceiverCheck(call);
if (negate) {
- as_bool = as_bool.value() ? Bool::False().raw() : Bool::True().raw();
+ as_bool = Bool::Get(!as_bool.value());
}
ConstantInstr* bool_const = new ConstantInstr(as_bool);
- call->ReplaceWith(bool_const, current_iterator());
- RemovePushArguments(call);
+ ReplaceCall(call, bool_const);
return;
}
}
InstanceOfInstr* instance_of =
new InstanceOfInstr(call->token_pos(),
- left_val,
- instantiator_val,
- type_args_val,
+ new Value(left),
+ new Value(instantiator),
+ new Value(type_args),
type,
negate);
- call->ReplaceWith(instance_of, current_iterator());
- RemovePushArguments(call);
+ ReplaceCall(call, instance_of);
}
@@ -1635,7 +1779,6 @@
// (e.g, binary op, field load, ..).
void FlowGraphOptimizer::VisitInstanceCall(InstanceCallInstr* instr) {
if (!instr->HasICData() || (instr->ic_data()->NumberOfChecks() == 0)) {
- // An instance call without ICData will trigger deoptimization.
return;
}
@@ -1703,7 +1846,7 @@
bool call_with_checks;
if (has_one_target) {
// Type propagation has not run yet, we cannot eliminate the check.
- AddCheckClass(instr, instr->ArgumentAt(0)->value()->Copy());
+ AddReceiverCheck(instr);
// Call can still deoptimize, do not detach environment from instr.
call_with_checks = false;
} else {
@@ -1721,9 +1864,9 @@
MethodRecognizer::Kind recognized_kind =
MethodRecognizer::RecognizeKind(call->function());
if (recognized_kind == MethodRecognizer::kMathSqrt) {
- MathSqrtInstr* sqrt = new MathSqrtInstr(call->ArgumentAt(0)->value(), call);
- call->ReplaceWith(sqrt, current_iterator());
- RemovePushArguments(call);
+ MathSqrtInstr* sqrt =
+ new MathSqrtInstr(new Value(call->ArgumentAt(0)), call);
+ ReplaceCall(call, sqrt);
}
}
@@ -1761,51 +1904,50 @@
ASSERT(!field.IsNull());
if (InstanceCallNeedsClassCheck(instr)) {
- AddCheckClass(instr, instr->ArgumentAt(0)->value()->Copy());
+ AddReceiverCheck(instr);
}
bool needs_store_barrier = true;
if (ArgIsAlwaysSmi(*instr->ic_data(), 1)) {
InsertBefore(instr,
- new CheckSmiInstr(instr->ArgumentAt(1)->value()->Copy(),
+ new CheckSmiInstr(new Value(instr->ArgumentAt(1)),
instr->deopt_id()),
instr->env(),
Definition::kEffect);
needs_store_barrier = false;
}
- // Detach environment from the original instruction because it can't
- // deoptimize.
- instr->set_env(NULL);
StoreInstanceFieldInstr* store = new StoreInstanceFieldInstr(
field,
- instr->ArgumentAt(0)->value(),
- instr->ArgumentAt(1)->value(),
+ new Value(instr->ArgumentAt(0)),
+ new Value(instr->ArgumentAt(1)),
needs_store_barrier);
- instr->ReplaceWith(store, current_iterator());
- RemovePushArguments(instr);
+ // Detach environment from the original instruction because it can't
+ // deoptimize.
+ for (Environment::DeepIterator it(instr->env()); !it.Done(); it.Advance()) {
+ it.CurrentValue()->RemoveFromUseList();
+ }
+ instr->set_env(NULL);
+ ReplaceCall(instr, store);
return true;
}
-static void HandleRelationalOp(FlowGraphOptimizer* optimizer,
- RelationalOpInstr* comp,
- Instruction* instr) {
+void FlowGraphOptimizer::HandleRelationalOp(RelationalOpInstr* comp) {
if (!comp->HasICData() || (comp->ic_data()->NumberOfChecks() == 0)) {
return;
}
const ICData& ic_data = *comp->ic_data();
+ Instruction* instr = current_iterator()->Current();
if (ic_data.NumberOfChecks() == 1) {
ASSERT(ic_data.HasOneTarget());
if (HasOnlyTwoSmis(ic_data)) {
- optimizer->InsertBefore(
- instr,
- new CheckSmiInstr(comp->left()->Copy(), comp->deopt_id()),
- instr->env(),
- Definition::kEffect);
- optimizer->InsertBefore(
- instr,
- new CheckSmiInstr(comp->right()->Copy(), comp->deopt_id()),
- instr->env(),
- Definition::kEffect);
+ InsertBefore(instr,
+ new CheckSmiInstr(comp->left()->Copy(), comp->deopt_id()),
+ instr->env(),
+ Definition::kEffect);
+ InsertBefore(instr,
+ new CheckSmiInstr(comp->right()->Copy(), comp->deopt_id()),
+ instr->env(),
+ Definition::kEffect);
comp->set_operands_class_id(kSmiCid);
} else if (ShouldSpecializeForDouble(ic_data)) {
comp->set_operands_class_id(kDoubleCid);
@@ -1823,23 +1965,23 @@
void FlowGraphOptimizer::VisitRelationalOp(RelationalOpInstr* instr) {
- HandleRelationalOp(this, instr, instr);
+ HandleRelationalOp(instr);
}
template <typename T>
-static void HandleEqualityCompare(FlowGraphOptimizer* optimizer,
- EqualityCompareInstr* comp,
- T instr,
- ForwardInstructionIterator* iterator) {
+void FlowGraphOptimizer::HandleEqualityCompare(EqualityCompareInstr* comp,
+ T current_instruction) {
// If one of the inputs is null, no ICdata will be collected.
if (comp->left()->BindsToConstantNull() ||
comp->right()->BindsToConstantNull()) {
Token::Kind strict_kind = (comp->kind() == Token::kEQ) ?
Token::kEQ_STRICT : Token::kNE_STRICT;
StrictCompareInstr* strict_comp =
- new StrictCompareInstr(strict_kind, comp->left(), comp->right());
- instr->ReplaceWith(strict_comp, iterator);
+ new StrictCompareInstr(strict_kind,
+ comp->left()->Copy(),
+ comp->right()->Copy());
+ current_instruction->ReplaceWith(strict_comp, current_iterator());
return;
}
if (!comp->HasICData() || (comp->ic_data()->NumberOfChecks() == 0)) {
@@ -1853,16 +1995,14 @@
// TODO(srdjan): allow for mixed mode int/double comparison.
if ((class_ids[0] == kSmiCid) && (class_ids[1] == kSmiCid)) {
- optimizer->InsertBefore(
- instr,
- new CheckSmiInstr(comp->left()->Copy(), comp->deopt_id()),
- instr->env(),
- Definition::kEffect);
- optimizer->InsertBefore(
- instr,
- new CheckSmiInstr(comp->right()->Copy(), comp->deopt_id()),
- instr->env(),
- Definition::kEffect);
+ InsertBefore(current_instruction,
+ new CheckSmiInstr(comp->left()->Copy(), comp->deopt_id()),
+ current_instruction->env(),
+ Definition::kEffect);
+ InsertBefore(current_instruction,
+ new CheckSmiInstr(comp->right()->Copy(), comp->deopt_id()),
+ current_instruction->env(),
+ Definition::kEffect);
comp->set_receiver_class_id(kSmiCid);
} else if ((class_ids[0] == kDoubleCid) && (class_ids[1] == kDoubleCid)) {
comp->set_receiver_class_id(kDoubleCid);
@@ -1889,63 +2029,40 @@
GrowableArray<intptr_t> smi_or_null(2);
smi_or_null.Add(kSmiCid);
smi_or_null.Add(kNullCid);
- if (ICDataHasOnlyReceiverArgumentClassIds(
- *comp->ic_data(), smi_or_null, smi_or_null)) {
+ if (ICDataHasOnlyReceiverArgumentClassIds(*comp->ic_data(),
+ smi_or_null,
+ smi_or_null)) {
const ICData& unary_checks_0 =
ICData::ZoneHandle(comp->ic_data()->AsUnaryClassChecks());
- const intptr_t deopt_id = comp->deopt_id();
- if ((unary_checks_0.NumberOfChecks() == 1) &&
- (unary_checks_0.GetReceiverClassIdAt(0) == kSmiCid)) {
- // Smi only.
- optimizer->InsertBefore(
- instr,
- new CheckSmiInstr(comp->left()->Copy(), deopt_id),
- instr->env(),
- Definition::kEffect);
- } else {
- // Smi or NULL.
- optimizer->InsertBefore(
- instr,
- new CheckClassInstr(comp->left()->Copy(), deopt_id, unary_checks_0),
- instr->env(),
- Definition::kEffect);
- }
+ AddCheckClass(comp->left()->definition(),
+ unary_checks_0,
+ comp->deopt_id(),
+ current_instruction->env(),
+ current_instruction);
const ICData& unary_checks_1 =
ICData::ZoneHandle(comp->ic_data()->AsUnaryClassChecksForArgNr(1));
- if ((unary_checks_1.NumberOfChecks() == 1) &&
- (unary_checks_1.GetReceiverClassIdAt(0) == kSmiCid)) {
- // Smi only.
- optimizer->InsertBefore(
- instr,
- new CheckSmiInstr(comp->right()->Copy(), deopt_id),
- instr->env(),
- Definition::kEffect);
- } else {
- // Smi or NULL.
- optimizer->InsertBefore(
- instr,
- new CheckClassInstr(comp->right()->Copy(), deopt_id, unary_checks_1),
- instr->env(),
- Definition::kEffect);
- }
+ AddCheckClass(comp->right()->definition(),
+ unary_checks_1,
+ comp->deopt_id(),
+ current_instruction->env(),
+ current_instruction);
comp->set_receiver_class_id(kSmiCid);
}
}
void FlowGraphOptimizer::VisitEqualityCompare(EqualityCompareInstr* instr) {
- HandleEqualityCompare(this, instr, instr, current_iterator());
+ HandleEqualityCompare(instr, instr);
}
void FlowGraphOptimizer::VisitBranch(BranchInstr* instr) {
ComparisonInstr* comparison = instr->comparison();
if (comparison->IsRelationalOp()) {
- HandleRelationalOp(this, comparison->AsRelationalOp(), instr);
+ HandleRelationalOp(comparison->AsRelationalOp());
} else if (comparison->IsEqualityCompare()) {
- HandleEqualityCompare(this, comparison->AsEqualityCompare(), instr,
- current_iterator());
+ HandleEqualityCompare(comparison->AsEqualityCompare(), instr);
} else {
ASSERT(comparison->IsStrictCompare());
// Nothing to do.
@@ -2232,15 +2349,11 @@
// No need to constrain constants.
if (defn->IsConstant()) return NULL;
- Value* value = new Value(defn);
- ConstraintInstr* constraint = new ConstraintInstr(value, constraint_range);
- constraint->InsertAfter(after);
- constraint->set_ssa_temp_index(flow_graph_->alloc_ssa_temp_index());
- RenameDominatedUses(defn, after, constraint);
+ ConstraintInstr* constraint =
+ new ConstraintInstr(new Value(defn), constraint_range);
+ flow_graph_->InsertAfter(after, constraint, NULL, Definition::kValue);
+ RenameDominatedUses(defn, constraint, constraint);
constraints_.Add(constraint);
- value->set_instruction(constraint);
- value->set_use_index(0);
- defn->AddInputUse(value);
return constraint;
}
@@ -2641,7 +2754,7 @@
current->value()->set_definition(non_smi_input_defn);
non_smi_input_defn->AddInputUse(current->value());
- phi->Type()->ReplaceWith(CompileType::FromCid(kSmiCid));
+ phi->UpdateType(CompileType::FromCid(kSmiCid));
}
@@ -4140,14 +4253,23 @@
!b.Done();
b.Advance()) {
BlockEntryInstr* block = b.Current();
+ JoinEntryInstr* join = block->AsJoinEntry();
if (!reachable_->Contains(block->preorder_number())) {
if (FLAG_trace_constant_propagation) {
OS::Print("Unreachable B%"Pd"\n", block->block_id());
}
+ // Remove all uses in unreachable blocks.
+ if (join != NULL) {
+ for (PhiIterator it(join); !it.Done(); it.Advance()) {
+ it.Current()->UnuseAllInputs();
+ }
+ }
+ for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
+ it.Current()->UnuseAllInputs();
+ }
continue;
}
- JoinEntryInstr* join = block->AsJoinEntry();
if (join != NULL) {
// Remove phi inputs corresponding to unreachable predecessor blocks.
// Predecessors will be recomputed (in block id order) after removing
@@ -4163,18 +4285,32 @@
for (intptr_t phi_idx = 0; phi_idx < phis->length(); ++phi_idx) {
PhiInstr* phi = (*phis)[phi_idx];
if (phi == NULL) continue;
- phi->inputs_[live_count] = phi->inputs_[pred_idx];
+ Value* input = phi->inputs_[pred_idx];
+ input->set_use_index(live_count);
+ phi->inputs_[live_count] = input;
}
}
++live_count;
+ } else {
+ for (intptr_t phi_idx = 0; phi_idx < phis->length(); ++phi_idx) {
+ PhiInstr* phi = (*phis)[phi_idx];
+ if (phi == NULL) continue;
+ phi->inputs_[pred_idx]->RemoveFromUseList();
+ }
}
}
if (live_count < pred_count) {
for (intptr_t phi_idx = 0; phi_idx < phis->length(); ++phi_idx) {
PhiInstr* phi = (*phis)[phi_idx];
if (phi == NULL) continue;
- phi->inputs_.TruncateTo(live_count);
- if (live_count == 1) redundant_phis.Add(phi);
+ if (FLAG_remove_redundant_phis && (live_count == 1)) {
+ Value* input = phi->InputAt(0);
+ phi->ReplaceUsesWith(input->definition());
+ input->RemoveFromUseList();
+ (*phis)[phi_idx] = NULL;
+ } else {
+ phi->inputs_.TruncateTo(live_count);
+ }
}
}
}
@@ -4199,7 +4335,7 @@
defn->ssa_temp_index(),
defn->constant_value().ToCString());
}
- i.ReplaceCurrentWith(new ConstantInstr(defn->constant_value()));
+ defn->ReplaceWith(new ConstantInstr(defn->constant_value()), &i);
}
}
@@ -4236,6 +4372,7 @@
// Replace the false target entry with the new join entry. We will
// recompute the dominators after this pass.
join->LinkTo(next);
+ branch->UnuseAllInputs();
}
}
}
@@ -4243,15 +4380,6 @@
graph_->DiscoverBlocks();
GrowableArray<BitVector*> dominance_frontier;
graph_->ComputeDominators(&dominance_frontier);
- graph_->ComputeUseLists();
-
- if (FLAG_remove_redundant_phis) {
- for (intptr_t i = 0; i < redundant_phis.length(); i++) {
- PhiInstr* phi = redundant_phis[i];
- phi->ReplaceUsesWith(phi->InputAt(0)->definition());
- phi->mark_dead();
- }
- }
if (FLAG_trace_constant_propagation) {
OS::Print("\n==== After constant propagation ====\n");
diff --git a/runtime/vm/flow_graph_optimizer.h b/runtime/vm/flow_graph_optimizer.h
index a0922ef..6ec0e35 100644
--- a/runtime/vm/flow_graph_optimizer.h
+++ b/runtime/vm/flow_graph_optimizer.h
@@ -21,12 +21,18 @@
flow_graph_(flow_graph) { }
virtual ~FlowGraphOptimizer() {}
+ FlowGraph* flow_graph() const { return flow_graph_; }
+
// Use ICData to optimize, replace or eliminate instructions.
void ApplyICData();
// Use propagated class ids to optimize, replace or eliminate instructions.
void ApplyClassIds();
+ // Optimize (a << b) & c pattern: if c is a positive Smi or zero, then the
+ // shift can be a truncating Smi shift-left and result is always Smi.
+ void TryOptimizeLeftShiftWithBitAndPattern();
+
void Canonicalize();
void EliminateDeadPhis();
@@ -47,7 +53,9 @@
void InsertBefore(Instruction* next,
Instruction* instr,
Environment* env,
- Definition::UseKind use_kind);
+ Definition::UseKind use_kind) {
+ flow_graph_->InsertBefore(next, instr, env, use_kind);
+ }
private:
// Attempt to build ICData for call using propagated class-ids.
@@ -57,9 +65,13 @@
intptr_t PrepareIndexedOp(InstanceCallInstr* call,
intptr_t class_id,
- Value** array,
- Value** index);
+ Definition** array,
+ Definition** index);
bool TryReplaceWithStoreIndexed(InstanceCallInstr* call);
+ bool TryInlineByteArraySetIndexed(InstanceCallInstr* call);
+ void BuildStoreIndexed(InstanceCallInstr* call,
+ const ICData& value_check,
+ intptr_t class_id);
bool TryReplaceWithLoadIndexed(InstanceCallInstr* call);
bool TryReplaceWithBinaryOp(InstanceCallInstr* call, Token::Kind op_kind);
@@ -79,12 +91,28 @@
intptr_t receiver_cid,
intptr_t view_cid);
- void AddCheckClass(InstanceCallInstr* call, Value* value);
+ // Insert a check of 'to_check' determined by 'unary_checks'. If the
+ // check fails it will deoptimize to 'deopt_id' using the deoptimization
+ // environment 'deopt_environment'. The check is inserted immediately
+ // before 'insert_before'.
+ void AddCheckClass(Definition* to_check,
+ const ICData& unary_checks,
+ intptr_t deopt_id,
+ Environment* deopt_environment,
+ Instruction* insert_before);
- void InsertAfter(Instruction* prev,
- Instruction* instr,
- Environment* env,
- Definition::UseKind use_kind);
+ // Insert a Smi check if needed.
+ void AddCheckSmi(Definition* to_check,
+ intptr_t deopt_id,
+ Environment* deopt_environment,
+ Instruction* insert_before);
+
+ // Add a class check for a call's first argument immediately before the
+ // call, using the call's IC data to determine the check, and the call's
+ // deopt ID and deoptimization environment if the check fails.
+ void AddReceiverCheck(InstanceCallInstr* call);
+
+ void ReplaceCall(Definition* call, Definition* replacement);
void InsertConversionsFor(Definition* def);
@@ -112,6 +140,18 @@
void ReplaceWithMathCFunction(InstanceCallInstr* call,
MethodRecognizer::Kind recognized_kind);
+ void HandleRelationalOp(RelationalOpInstr* comp);
+
+ // Visit an equality compare. The current instruction can be the
+ // comparison itself or a branch on the comparison.
+ template <typename T>
+ void HandleEqualityCompare(EqualityCompareInstr* comp,
+ T current_instruction);
+
+ void OptimizeLeftShiftBitAndSmiOp(Definition* bit_and_instr,
+ Definition* left_instr,
+ Definition* right_instr);
+
FlowGraph* flow_graph_;
DISALLOW_COPY_AND_ASSIGN(FlowGraphOptimizer);
diff --git a/runtime/vm/flow_graph_type_propagator.cc b/runtime/vm/flow_graph_type_propagator.cc
index 020172c..8810fbc 100644
--- a/runtime/vm/flow_graph_type_propagator.cc
+++ b/runtime/vm/flow_graph_type_propagator.cc
@@ -6,6 +6,7 @@
#include "vm/cha.h"
#include "vm/bit_vector.h"
+#include "vm/il_printer.h"
namespace dart {
@@ -20,27 +21,46 @@
: FlowGraphVisitor(flow_graph->reverse_postorder()),
flow_graph_(flow_graph),
types_(flow_graph->current_ssa_temp_index()),
- in_worklist_(new BitVector(flow_graph->current_ssa_temp_index())) {
+ in_worklist_(new BitVector(flow_graph->current_ssa_temp_index())),
+ asserts_(NULL),
+ collected_asserts_(NULL) {
for (intptr_t i = 0; i < flow_graph->current_ssa_temp_index(); i++) {
types_.Add(NULL);
}
+
+ if (FLAG_enable_type_checks) {
+ asserts_ = new ZoneGrowableArray<AssertAssignableInstr*>(
+ flow_graph->current_ssa_temp_index());
+ for (intptr_t i = 0; i < flow_graph->current_ssa_temp_index(); i++) {
+ asserts_->Add(NULL);
+ }
+
+ collected_asserts_ = new ZoneGrowableArray<intptr_t>(10);
+ }
}
void FlowGraphTypePropagator::Propagate() {
- // Walk dominator tree and propagate reaching types to all Values.
- // Collect all phis for a fix point iteration.
+ if (FLAG_trace_type_propagation) {
+ OS::Print("Before type propagation:\n");
+ FlowGraphPrinter printer(*flow_graph_);
+ printer.PrintBlocks();
+ }
+
+ // Walk the dominator tree and propagate reaching types to all Values.
+ // Collect all phis for a fixed point iteration.
PropagateRecursive(flow_graph_->graph_entry());
#ifdef DEBUG
- // Initially work-list contains only phis.
+ // Initially the worklist contains only phis.
for (intptr_t i = 0; i < worklist_.length(); i++) {
ASSERT(worklist_[i]->IsPhi());
ASSERT(worklist_[i]->Type()->IsNone());
}
#endif
- // Iterate until fix point is reached updating types of definitions.
+ // Iterate until a fixed point is reached, updating the types of
+ // definitions.
while (!worklist_.is_empty()) {
Definition* def = RemoveLastFromWorklist();
if (FLAG_trace_type_propagation) {
@@ -62,12 +82,22 @@
}
}
}
+
+ if (FLAG_trace_type_propagation) {
+ OS::Print("After type propagation:\n");
+ FlowGraphPrinter printer(*flow_graph_);
+ printer.PrintBlocks();
+ }
}
void FlowGraphTypePropagator::PropagateRecursive(BlockEntryInstr* block) {
const intptr_t rollback_point = rollback_.length();
+ if (FLAG_enable_type_checks) {
+ StrengthenAsserts(block);
+ }
+
block->Accept(this);
for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
@@ -76,6 +106,9 @@
for (intptr_t i = 0; i < instr->InputCount(); i++) {
VisitValue(instr->InputAt(i));
}
+ if (instr->IsDefinition()) {
+ instr->AsDefinition()->RecomputeType();
+ }
instr->Accept(this);
}
@@ -123,7 +156,7 @@
CompileType* current = TypeOf(def);
if (current->ToCid() == cid) return;
- SetTypeOf(def, CompileType::FromCid(cid));
+ SetTypeOf(def, ZoneCompileType::Wrap(CompileType::FromCid(cid)));
}
@@ -189,13 +222,93 @@
}
+// Unwrap all assert assignable and get a real definition of the value.
+static Definition* UnwrapAsserts(Definition* defn) {
+ while (defn->IsAssertAssignable()) {
+ defn = defn->AsAssertAssignable()->value()->definition();
+ }
+ return defn;
+}
+
+
+// In the given block strengthen type assertions by hoisting first class or smi
+// check over the same value up to the point before the assertion. This allows
+// to eliminate type assertions that are postdominated by class or smi checks as
+// these checks are strongly stricter than type assertions.
+void FlowGraphTypePropagator::StrengthenAsserts(BlockEntryInstr* block) {
+ for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
+ Instruction* instr = it.Current();
+
+ if (instr->IsCheckSmi() || instr->IsCheckClass()) {
+ StrengthenAssertWith(instr);
+ }
+
+ // If this is the first type assertion checking given value record it.
+ AssertAssignableInstr* assert = instr->AsAssertAssignable();
+ if (assert != NULL) {
+ Definition* defn = UnwrapAsserts(assert->value()->definition());
+ if ((*asserts_)[defn->ssa_temp_index()] == NULL) {
+ (*asserts_)[defn->ssa_temp_index()] = assert;
+ collected_asserts_->Add(defn->ssa_temp_index());
+ }
+ }
+ }
+
+ for (intptr_t i = 0; i < collected_asserts_->length(); i++) {
+ (*asserts_)[(*collected_asserts_)[i]] = NULL;
+ }
+
+ collected_asserts_->TruncateTo(0);
+}
+
+
+void FlowGraphTypePropagator::StrengthenAssertWith(Instruction* check) {
+ // Marker that is used to mark values that already had type assertion
+ // strengthened.
+ AssertAssignableInstr* kStrengthenedAssertMarker =
+ reinterpret_cast<AssertAssignableInstr*>(-1);
+
+ Definition* defn = UnwrapAsserts(check->InputAt(0)->definition());
+
+ AssertAssignableInstr* assert = (*asserts_)[defn->ssa_temp_index()];
+ if ((assert == NULL) || (assert == kStrengthenedAssertMarker)) {
+ return;
+ }
+
+ Instruction* check_clone = NULL;
+ if (check->IsCheckSmi()) {
+ check_clone =
+ new CheckSmiInstr(assert->value()->Copy(),
+ assert->env()->deopt_id());
+ } else {
+ ASSERT(check->IsCheckClass());
+ check_clone =
+ new CheckClassInstr(assert->value()->Copy(),
+ assert->env()->deopt_id(),
+ check->AsCheckClass()->unary_checks());
+ }
+ ASSERT(check_clone != NULL);
+ ASSERT(assert->deopt_id() == assert->env()->deopt_id());
+
+ Value* use = check_clone->InputAt(0);
+ use->set_instruction(check_clone);
+ use->set_use_index(0);
+ use->definition()->AddInputUse(use);
+
+ assert->env()->DeepCopyTo(check_clone);
+ check_clone->InsertBefore(assert);
+
+ (*asserts_)[defn->ssa_temp_index()] = kStrengthenedAssertMarker;
+}
+
+
void CompileType::Union(CompileType* other) {
if (other->IsNone()) {
return;
}
if (IsNone()) {
- ReplaceWith(other);
+ *this = *other;
return;
}
@@ -233,38 +346,38 @@
}
-CompileType* CompileType::New(intptr_t cid, const AbstractType& type) {
- return new CompileType(IsNullableCid(cid), cid, &type);
+CompileType CompileType::Create(intptr_t cid, const AbstractType& type) {
+ return CompileType(IsNullableCid(cid), cid, &type);
}
-CompileType* CompileType::FromAbstractType(const AbstractType& type,
+CompileType CompileType::FromAbstractType(const AbstractType& type,
bool is_nullable) {
- return new CompileType(is_nullable, kIllegalCid, &type);
+ return CompileType(is_nullable, kIllegalCid, &type);
}
-CompileType* CompileType::FromCid(intptr_t cid) {
- return new CompileType(IsNullableCid(cid), cid, NULL);
+CompileType CompileType::FromCid(intptr_t cid) {
+ return CompileType(IsNullableCid(cid), cid, NULL);
}
-CompileType* CompileType::Dynamic() {
- return New(kDynamicCid, Type::ZoneHandle(Type::DynamicType()));
+CompileType CompileType::Dynamic() {
+ return Create(kDynamicCid, Type::ZoneHandle(Type::DynamicType()));
}
-CompileType* CompileType::Null() {
- return New(kNullCid, Type::ZoneHandle(Type::NullType()));
+CompileType CompileType::Null() {
+ return Create(kNullCid, Type::ZoneHandle(Type::NullType()));
}
-CompileType* CompileType::Bool() {
- return New(kBoolCid, Type::ZoneHandle(Type::BoolType()));
+CompileType CompileType::Bool() {
+ return Create(kBoolCid, Type::ZoneHandle(Type::BoolType()));
}
-CompileType* CompileType::Int() {
+CompileType CompileType::Int() {
return FromAbstractType(Type::ZoneHandle(Type::IntType()), kNonNullable);
}
@@ -290,6 +403,8 @@
const intptr_t cid = Class::Handle(type_->type_class()).id();
if (!CHA::HasSubclasses(cid)) {
cid_ = cid;
+ } else {
+ cid_ = kDynamicCid;
}
} else {
cid_ = kDynamicCid;
@@ -394,7 +509,16 @@
bool CompileType::IsMoreSpecificThan(const AbstractType& other) {
- return !IsNone() && ToAbstractType()->IsMoreSpecificThan(other, NULL);
+ if (IsNone()) {
+ return false;
+ }
+
+ if (other.IsVoidType()) {
+ // The only value assignable to void is null.
+ return IsNull();
+ }
+
+ return ToAbstractType()->IsMoreSpecificThan(other, NULL);
}
@@ -406,7 +530,7 @@
}
-CompileType* PhiInstr::ComputeInitialType() const {
+CompileType PhiInstr::ComputeType() const {
// Initially type of phis is unknown until type propagation is run
// for the first time.
return CompileType::None();
@@ -418,7 +542,7 @@
return false;
}
- CompileType* result = CompileType::None();
+ CompileType result = CompileType::None();
for (intptr_t i = 0; i < InputCount(); i++) {
if (FLAG_trace_type_propagation) {
@@ -428,45 +552,59 @@
InputAt(i)->definition()->ssa_temp_index(),
InputAt(i)->Type()->ToCString());
}
- result->Union(InputAt(i)->Type());
+ result.Union(InputAt(i)->Type());
}
- if (result->IsNone()) {
+ if (result.IsNone()) {
ASSERT(Type()->IsNone());
return false;
}
- if (Type()->IsNone() || !Type()->IsEqualTo(result)) {
- Type()->ReplaceWith(result);
- return true;
- }
-
- return false;
+ return UpdateType(result);
}
+static bool CanTrustParameterType(const Function& function, intptr_t index) {
+ // Parameter is receiver.
+ if (index == 0) {
+ return function.IsDynamicFunction() || function.IsConstructor();
+ }
-CompileType* ParameterInstr::ComputeInitialType() const {
+ // Parameter is the constructor phase.
+ return (index == 1) && function.IsConstructor();
+}
+
+
+CompileType ParameterInstr::ComputeType() const {
// Note that returning the declared type of the formal parameter would be
// incorrect, because ParameterInstr is used as input to the type check
// verifying the run time type of the passed-in parameter and this check would
// always be wrongly eliminated.
+ // However there are parameters that are known to match their declared type:
+ // for example receiver and construction phase.
+ if (!CanTrustParameterType(block_->parsed_function().function(),
+ index())) {
+ return CompileType::Dynamic();
+ }
+
+ LocalScope* scope = block_->parsed_function().node_sequence()->scope();
+ return CompileType::FromAbstractType(scope->VariableAt(index())->type(),
+ CompileType::kNonNullable);
+}
+
+
+CompileType PushArgumentInstr::ComputeType() const {
return CompileType::Dynamic();
}
-CompileType* PushArgumentInstr::ComputeInitialType() const {
- return CompileType::Dynamic();
-}
-
-
-CompileType* ConstantInstr::ComputeInitialType() const {
+CompileType ConstantInstr::ComputeType() const {
if (value().IsNull()) {
return CompileType::Null();
}
if (value().IsInstance()) {
- return CompileType::New(
+ return CompileType::Create(
Class::Handle(value().clazz()).id(),
AbstractType::ZoneHandle(Instance::Cast(value()).GetType()));
} else {
@@ -478,10 +616,17 @@
CompileType* AssertAssignableInstr::ComputeInitialType() const {
CompileType* value_type = value()->Type();
+
if (value_type->IsMoreSpecificThan(dst_type())) {
return value_type;
}
- return CompileType::FromAbstractType(dst_type());
+
+ if (dst_type().IsVoidType()) {
+ // The only value assignable to void is null.
+ return ZoneCompileType::Wrap(CompileType::Null());
+ }
+
+ return ZoneCompileType::Wrap(CompileType::FromAbstractType(dst_type()));
}
@@ -491,69 +636,77 @@
return false;
}
- if (value_type->IsMoreSpecificThan(dst_type()) &&
- !Type()->IsEqualTo(value_type)) {
- Type()->ReplaceWith(value_type);
- return true;
+ if (value_type->IsMoreSpecificThan(dst_type())) {
+ return UpdateType(*value_type);
}
return false;
}
-CompileType* AssertBooleanInstr::ComputeInitialType() const {
+CompileType AssertBooleanInstr::ComputeType() const {
return CompileType::Bool();
}
-CompileType* ArgumentDefinitionTestInstr::ComputeInitialType() const {
+CompileType ArgumentDefinitionTestInstr::ComputeType() const {
return CompileType::Bool();
}
-CompileType* BooleanNegateInstr::ComputeInitialType() const {
+CompileType BooleanNegateInstr::ComputeType() const {
return CompileType::Bool();
}
-CompileType* InstanceOfInstr::ComputeInitialType() const {
+CompileType InstanceOfInstr::ComputeType() const {
return CompileType::Bool();
}
-CompileType* StrictCompareInstr::ComputeInitialType() const {
+CompileType StrictCompareInstr::ComputeType() const {
return CompileType::Bool();
}
-CompileType* EqualityCompareInstr::ComputeInitialType() const {
+CompileType EqualityCompareInstr::ComputeType() const {
return IsInlinedNumericComparison() ? CompileType::Bool()
: CompileType::Dynamic();
}
-CompileType* RelationalOpInstr::ComputeInitialType() const {
+bool EqualityCompareInstr::RecomputeType() {
+ return UpdateType(ComputeType());
+}
+
+
+CompileType RelationalOpInstr::ComputeType() const {
return IsInlinedNumericComparison() ? CompileType::Bool()
: CompileType::Dynamic();
}
-CompileType* CurrentContextInstr::ComputeInitialType() const {
+bool RelationalOpInstr::RecomputeType() {
+ return UpdateType(ComputeType());
+}
+
+
+CompileType CurrentContextInstr::ComputeType() const {
return CompileType::FromCid(kContextCid);
}
-CompileType* CloneContextInstr::ComputeInitialType() const {
+CompileType CloneContextInstr::ComputeType() const {
return CompileType::FromCid(kContextCid);
}
-CompileType* AllocateContextInstr::ComputeInitialType() const {
+CompileType AllocateContextInstr::ComputeType() const {
return CompileType::FromCid(kContextCid);
}
-CompileType* StaticCallInstr::ComputeInitialType() const {
+CompileType StaticCallInstr::ComputeType() const {
if (result_cid_ != kDynamicCid) {
return CompileType::FromCid(result_cid_);
}
@@ -567,7 +720,7 @@
}
-CompileType* LoadLocalInstr::ComputeInitialType() const {
+CompileType LoadLocalInstr::ComputeType() const {
if (FLAG_enable_type_checks) {
return CompileType::FromAbstractType(local().type());
}
@@ -581,7 +734,7 @@
}
-CompileType* StringFromCharCodeInstr::ComputeInitialType() const {
+CompileType StringFromCharCodeInstr::ComputeType() const {
return CompileType::FromCid(cid_);
}
@@ -591,7 +744,7 @@
}
-CompileType* LoadStaticFieldInstr::ComputeInitialType() const {
+CompileType LoadStaticFieldInstr::ComputeType() const {
if (FLAG_enable_type_checks) {
return CompileType::FromAbstractType(
AbstractType::ZoneHandle(field().type()));
@@ -605,12 +758,12 @@
}
-CompileType* CreateArrayInstr::ComputeInitialType() const {
+CompileType CreateArrayInstr::ComputeType() const {
return CompileType::FromAbstractType(type(), CompileType::kNonNullable);
}
-CompileType* CreateClosureInstr::ComputeInitialType() const {
+CompileType CreateClosureInstr::ComputeType() const {
const Function& fun = function();
const Class& signature_class = Class::Handle(fun.signature_class());
return CompileType::FromAbstractType(
@@ -619,13 +772,13 @@
}
-CompileType* AllocateObjectInstr::ComputeInitialType() const {
+CompileType AllocateObjectInstr::ComputeType() const {
// TODO(vegorov): Incorporate type arguments into the returned type.
return CompileType::FromCid(cid_);
}
-CompileType* LoadFieldInstr::ComputeInitialType() const {
+CompileType LoadFieldInstr::ComputeType() const {
// Type may be null if the field is a VM field, e.g. context parent.
// Keep it as null for debug purposes and do not return dynamic in production
// mode, since misuse of the type would remain undetected.
@@ -646,87 +799,87 @@
}
-CompileType* BinarySmiOpInstr::ComputeInitialType() const {
+CompileType BinarySmiOpInstr::ComputeType() const {
return CompileType::FromCid(kSmiCid);
}
-CompileType* UnarySmiOpInstr::ComputeInitialType() const {
+CompileType UnarySmiOpInstr::ComputeType() const {
return CompileType::FromCid(kSmiCid);
}
-CompileType* DoubleToSmiInstr::ComputeInitialType() const {
+CompileType DoubleToSmiInstr::ComputeType() const {
return CompileType::FromCid(kSmiCid);
}
-CompileType* ConstraintInstr::ComputeInitialType() const {
+CompileType ConstraintInstr::ComputeType() const {
return CompileType::FromCid(kSmiCid);
}
-CompileType* BinaryMintOpInstr::ComputeInitialType() const {
+CompileType BinaryMintOpInstr::ComputeType() const {
return CompileType::Int();
}
-CompileType* ShiftMintOpInstr::ComputeInitialType() const {
+CompileType ShiftMintOpInstr::ComputeType() const {
return CompileType::Int();
}
-CompileType* UnaryMintOpInstr::ComputeInitialType() const {
+CompileType UnaryMintOpInstr::ComputeType() const {
return CompileType::Int();
}
-CompileType* BoxIntegerInstr::ComputeInitialType() const {
+CompileType BoxIntegerInstr::ComputeType() const {
return CompileType::Int();
}
-CompileType* UnboxIntegerInstr::ComputeInitialType() const {
+CompileType UnboxIntegerInstr::ComputeType() const {
return CompileType::Int();
}
-CompileType* DoubleToIntegerInstr::ComputeInitialType() const {
+CompileType DoubleToIntegerInstr::ComputeType() const {
return CompileType::Int();
}
-CompileType* BinaryDoubleOpInstr::ComputeInitialType() const {
+CompileType BinaryDoubleOpInstr::ComputeType() const {
return CompileType::FromCid(kDoubleCid);
}
-CompileType* MathSqrtInstr::ComputeInitialType() const {
+CompileType MathSqrtInstr::ComputeType() const {
return CompileType::FromCid(kDoubleCid);
}
-CompileType* UnboxDoubleInstr::ComputeInitialType() const {
+CompileType UnboxDoubleInstr::ComputeType() const {
return CompileType::FromCid(kDoubleCid);
}
-CompileType* BoxDoubleInstr::ComputeInitialType() const {
+CompileType BoxDoubleInstr::ComputeType() const {
return CompileType::FromCid(kDoubleCid);
}
-CompileType* SmiToDoubleInstr::ComputeInitialType() const {
+CompileType SmiToDoubleInstr::ComputeType() const {
return CompileType::FromCid(kDoubleCid);
}
-CompileType* DoubleToDoubleInstr::ComputeInitialType() const {
+CompileType DoubleToDoubleInstr::ComputeType() const {
return CompileType::FromCid(kDoubleCid);
}
-CompileType* InvokeMathCFunctionInstr::ComputeInitialType() const {
+CompileType InvokeMathCFunctionInstr::ComputeType() const {
return CompileType::FromCid(kDoubleCid);
}
diff --git a/runtime/vm/flow_graph_type_propagator.h b/runtime/vm/flow_graph_type_propagator.h
index fafd1b6..f01dc5f 100644
--- a/runtime/vm/flow_graph_type_propagator.h
+++ b/runtime/vm/flow_graph_type_propagator.h
@@ -38,6 +38,10 @@
void AddToWorklist(Definition* defn);
Definition* RemoveLastFromWorklist();
+ // Type assertion strengthening.
+ void StrengthenAsserts(BlockEntryInstr* block);
+ void StrengthenAssertWith(Instruction* check);
+
FlowGraph* flow_graph_;
// Mapping between SSA values and their current reaching types. Valid
@@ -48,6 +52,9 @@
GrowableArray<Definition*> worklist_;
BitVector* in_worklist_;
+ ZoneGrowableArray<AssertAssignableInstr*>* asserts_;
+ ZoneGrowableArray<intptr_t>* collected_asserts_;
+
// RollbackEntry is used to track and rollback changed in the types_ array
// done during dominator tree traversal.
class RollbackEntry {
diff --git a/runtime/vm/gdbjit_android.cc b/runtime/vm/gdbjit_android.cc
index 101f378..1141fbf 100644
--- a/runtime/vm/gdbjit_android.cc
+++ b/runtime/vm/gdbjit_android.cc
@@ -2,9 +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.
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
+#include "vm/globals.h"
+#if defined(TARGET_OS_ANDROID)
+
+#include <stdint.h> // NOLINT
+#include <stdio.h> // NOLINT
+#include <stdlib.h> // NOLINT
#include "vm/gdbjit_android.h"
@@ -77,3 +80,5 @@
last_dynamic_region = NULL;
}
};
+
+#endif // defined(TARGET_OS_ANDROID)
diff --git a/runtime/vm/gdbjit_linux.cc b/runtime/vm/gdbjit_linux.cc
index 35d4f44..9e06b85 100644
--- a/runtime/vm/gdbjit_linux.cc
+++ b/runtime/vm/gdbjit_linux.cc
@@ -2,9 +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.
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
+#include "vm/globals.h"
+#if defined(TARGET_OS_LINUX)
+
+#include <stdint.h> // NOLINT
+#include <stdio.h> // NOLINT
+#include <stdlib.h> // NOLINT
#include "vm/gdbjit_linux.h"
@@ -77,3 +80,5 @@
last_dynamic_region = NULL;
}
};
+
+#endif // defined(TARGET_OS_LINUX)
diff --git a/runtime/vm/il_printer.cc b/runtime/vm/il_printer.cc
index 02db6e23..c4d74e5 100644
--- a/runtime/vm/il_printer.cc
+++ b/runtime/vm/il_printer.cc
@@ -112,8 +112,7 @@
} else {
f->Print("?, ");
}
- f->Print("%s}", (type_ != NULL) ? String::Handle(type_->Name()).ToCString()
- : "?");
+ f->Print("%s}", (type_ != NULL) ? type_->ToCString() : "?");
}
@@ -319,7 +318,7 @@
void ClosureCallInstr::PrintOperandsTo(BufferFormatter* f) const {
for (intptr_t i = 0; i < ArgumentCount(); ++i) {
if (i > 0) f->Print(", ");
- ArgumentAt(i)->value()->PrintTo(f);
+ PushArgumentAt(i)->value()->PrintTo(f);
}
}
@@ -328,7 +327,7 @@
f->Print("%s", function_name().ToCString());
for (intptr_t i = 0; i < ArgumentCount(); ++i) {
f->Print(", ");
- ArgumentAt(i)->value()->PrintTo(f);
+ PushArgumentAt(i)->value()->PrintTo(f);
}
if (HasICData()) {
PrintICData(f, *ic_data());
@@ -340,7 +339,7 @@
f->Print("%s", instance_call()->function_name().ToCString());
for (intptr_t i = 0; i < ArgumentCount(); ++i) {
f->Print(", ");
- ArgumentAt(i)->value()->PrintTo(f);
+ PushArgumentAt(i)->value()->PrintTo(f);
}
PrintICData(f, ic_data());
}
@@ -368,7 +367,7 @@
f->Print("%s ", String::Handle(function().name()).ToCString());
for (intptr_t i = 0; i < ArgumentCount(); ++i) {
if (i > 0) f->Print(", ");
- ArgumentAt(i)->value()->PrintTo(f);
+ PushArgumentAt(i)->value()->PrintTo(f);
}
}
@@ -440,7 +439,7 @@
f->Print("%s", Class::Handle(constructor().Owner()).ToCString());
for (intptr_t i = 0; i < ArgumentCount(); i++) {
f->Print(", ");
- ArgumentAt(i)->value()->PrintTo(f);
+ PushArgumentAt(i)->value()->PrintTo(f);
}
}
@@ -458,7 +457,7 @@
void CreateArrayInstr::PrintOperandsTo(BufferFormatter* f) const {
for (int i = 0; i < ArgumentCount(); ++i) {
if (i != 0) f->Print(", ");
- ArgumentAt(i)->value()->PrintTo(f);
+ PushArgumentAt(i)->value()->PrintTo(f);
}
if (ArgumentCount() > 0) f->Print(", ");
element_type()->PrintTo(f);
@@ -469,7 +468,7 @@
f->Print("%s", function().ToCString());
for (intptr_t i = 0; i < ArgumentCount(); ++i) {
if (i > 0) f->Print(", ");
- ArgumentAt(i)->value()->PrintTo(f);
+ PushArgumentAt(i)->value()->PrintTo(f);
}
}
@@ -517,8 +516,10 @@
void BinarySmiOpInstr::PrintTo(BufferFormatter* f) const {
Definition::PrintTo(f);
f->Print(" %co", overflow_ ? '+' : '-');
+ f->Print(" %ct", is_truncating() ? '+' : '-');
}
+
void BinarySmiOpInstr::PrintOperandsTo(BufferFormatter* f) const {
f->Print("%s, ", Token::Str(op_kind()));
left()->PrintTo(f);
diff --git a/runtime/vm/instructions_arm.cc b/runtime/vm/instructions_arm.cc
index 1ba1dcc..ba7ec59 100644
--- a/runtime/vm/instructions_arm.cc
+++ b/runtime/vm/instructions_arm.cc
@@ -10,34 +10,84 @@
namespace dart {
-bool InstructionPattern::TestBytesWith(const int* data, int num_bytes) const {
+uword InstructionPattern::Back(int n) const {
+ ASSERT(n > 0);
+ return *(end_ - n);
+}
+
+
+CallPattern::CallPattern(uword pc, const Code& code)
+ : InstructionPattern(pc),
+ pool_index_(DecodePoolIndex()),
+ object_pool_(Array::Handle(code.ObjectPool())) { }
+
+
+int CallPattern::DecodePoolIndex() {
+ ASSERT(Back(1) == 0xe12fff3e); // Last instruction: blx lr
+ // Decode the second to last instruction.
+ uword instr = Back(2);
+ int offset = 0;
+ if ((instr & 0xfffff000) == 0xe59ae000) { // ldr lr, [pp, #+offset]
+ offset = instr & 0xfff;
+ } else {
+ ASSERT((instr & 0xfffff000) == 0xe59ee000); // ldr lr, [lr, #+offset]
+ offset = instr & 0xfff;
+ instr = Back(3);
+ if ((instr & 0xfffff000) == 0xe28ae000) { // add lr, pp, shifter_op
+ const int rot = (instr & 0xf00) * 2;
+ const int imm8 = instr & 0xff;
+ offset |= (imm8 >> rot) | (imm8 << (32 - rot));
+ } else {
+ ASSERT(instr == 0xe08ae00e); // add lr, pp, lr
+ instr = Back(4);
+ if ((instr & 0xfff0f000) == 0xe340e000) { // movt lr, offset_hi
+ offset |= (instr & 0xf0000) << 12;
+ offset |= (instr & 0xfff) << 16;
+ instr = Back(5);
+ }
+ ASSERT((instr & 0xfff0f000) == 0xe300e000); // movw lr, offset_lo
+ ASSERT((offset & 0xffff) == 0);
+ offset |= (instr & 0xf0000) >> 4;
+ offset |= instr & 0xfff;
+ }
+ }
+ offset += kHeapObjectTag;
+ ASSERT(Utils::IsAligned(offset, 4));
+ return (offset - Array::data_offset())/4;
+}
+
+
+uword CallPattern::TargetAddress() const {
+ const Object& target_address = Object::Handle(object_pool_.At(pool_index_));
+ ASSERT(target_address.IsSmi());
+ return Smi::Cast(target_address).Value() << kSmiTagShift;
+}
+
+
+void CallPattern::SetTargetAddress(uword target_address) const {
+ ASSERT(Utils::IsAligned(target_address, 4));
+ // The address is stored in the object array as a RawSmi.
+ const Smi& smi = Smi::Handle(Smi::New(target_address >> kSmiTagShift));
+ object_pool_.SetAt(pool_index_, smi);
+}
+
+
+bool JumpPattern::IsValid() const {
UNIMPLEMENTED();
return false;
}
-uword CallOrJumpPattern::TargetAddress() const {
+uword JumpPattern::TargetAddress() const {
UNIMPLEMENTED();
return 0;
}
-void CallOrJumpPattern::SetTargetAddress(uword target) const {
+void JumpPattern::SetTargetAddress(uword target) const {
UNIMPLEMENTED();
}
-
-const int* CallPattern::pattern() const {
- UNIMPLEMENTED();
- return NULL;
-}
-
-
-const int* JumpPattern::pattern() const {
- UNIMPLEMENTED();
- return NULL;
-}
-
} // namespace dart
#endif // defined TARGET_ARCH_ARM
diff --git a/runtime/vm/instructions_arm.h b/runtime/vm/instructions_arm.h
index c3ea8ea..0ebac6f 100644
--- a/runtime/vm/instructions_arm.h
+++ b/runtime/vm/instructions_arm.h
@@ -10,86 +10,58 @@
#error Do not include instructions_arm.h directly; use instructions.h instead.
#endif
-#include "vm/allocation.h"
+#include "vm/object.h"
namespace dart {
-// Forward declarations.
-class RawClass;
-class Immediate;
-class RawObject;
-
// Abstract class for all instruction pattern classes.
class InstructionPattern : public ValueObject {
public:
- explicit InstructionPattern(uword pc) : start_(pc) {
+ explicit InstructionPattern(uword pc) : end_(reinterpret_cast<uword*>(pc)) {
ASSERT(pc != 0);
}
- virtual ~InstructionPattern() {}
-
- // Call to check if the instruction pattern at 'pc' match the instruction.
- virtual bool IsValid() const {
- return TestBytesWith(pattern(), pattern_length_in_bytes());
- }
-
- // 'pattern' returns the expected byte pattern in form of an integer array
- // with length of 'pattern_length_in_bytes'. A '-1' element means 'any byte'.
- virtual const int* pattern() const = 0;
- virtual int pattern_length_in_bytes() const = 0;
+ virtual ~InstructionPattern() { }
protected:
- uword start() const { return start_; }
+ uword Back(int n) const;
private:
- // Returns true if the 'num_bytes' bytes at 'start_' correspond to
- // array of integers 'data'. 'data' elements are either a byte or -1, which
- // represents any byte.
- bool TestBytesWith(const int* data, int num_bytes) const;
-
- const uword start_;
+ const uword* end_;
DISALLOW_COPY_AND_ASSIGN(InstructionPattern);
};
-class CallOrJumpPattern : public InstructionPattern {
+class CallPattern : public InstructionPattern {
public:
- virtual int pattern_length_in_bytes() const {
- return kLengthInBytes;
- }
+ CallPattern(uword pc, const Code& code);
+
uword TargetAddress() const;
- void SetTargetAddress(uword new_target) const;
-
- protected:
- explicit CallOrJumpPattern(uword pc) : InstructionPattern(pc) {}
- static const int kLengthInBytes = 0;
+ void SetTargetAddress(uword target_address) const;
private:
- DISALLOW_COPY_AND_ASSIGN(CallOrJumpPattern);
-};
-
-
-class CallPattern : public CallOrJumpPattern {
- public:
- explicit CallPattern(uword pc) : CallOrJumpPattern(pc) {}
- static int InstructionLength() {
- return kLengthInBytes;
- }
-
- private:
- virtual const int* pattern() const;
+ int DecodePoolIndex();
+ const int pool_index_;
+ const Array& object_pool_;
DISALLOW_COPY_AND_ASSIGN(CallPattern);
};
-class JumpPattern : public CallOrJumpPattern {
+class JumpPattern : public InstructionPattern {
public:
- explicit JumpPattern(uword pc) : CallOrJumpPattern(pc) {}
+ explicit JumpPattern(uword pc) : InstructionPattern(pc) { }
+
+ static const int kLengthInBytes = 3*kWordSize;
+
+ int pattern_length_in_bytes() const {
+ return kLengthInBytes;
+ }
+ bool IsValid() const;
+ uword TargetAddress() const;
+ void SetTargetAddress(uword target_address) const;
private:
- virtual const int* pattern() const;
-
DISALLOW_COPY_AND_ASSIGN(JumpPattern);
};
diff --git a/runtime/vm/instructions_arm_test.cc b/runtime/vm/instructions_arm_test.cc
index 86e4dc1..cf2e98c 100644
--- a/runtime/vm/instructions_arm_test.cc
+++ b/runtime/vm/instructions_arm_test.cc
@@ -15,12 +15,14 @@
#define __ assembler->
ASSEMBLER_TEST_GENERATE(Call, assembler) {
- UNIMPLEMENTED();
+ __ BranchLinkPatchable(&StubCode::InstanceFunctionLookupLabel());
+ // Do not emit any code after the call above, since we decode backwards
+ // starting from the return address, i.e. from the end of this code buffer.
}
-ASSEMBLER_TEST_RUN(Call, entry) {
- CallPattern call(entry);
+ASSEMBLER_TEST_RUN(Call, test) {
+ CallPattern call(test->entry() + test->code().Size(), test->code());
EXPECT_EQ(StubCode::InstanceFunctionLookupLabel().address(),
call.TargetAddress());
}
@@ -31,11 +33,11 @@
}
-ASSEMBLER_TEST_RUN(Jump, entry) {
- JumpPattern jump1(entry);
+ASSEMBLER_TEST_RUN(Jump, test) {
+ JumpPattern jump1(test->entry());
EXPECT_EQ(StubCode::InstanceFunctionLookupLabel().address(),
jump1.TargetAddress());
- JumpPattern jump2(entry + jump1.pattern_length_in_bytes());
+ JumpPattern jump2(test->entry() + jump1.pattern_length_in_bytes());
EXPECT_EQ(StubCode::AllocateArrayLabel().address(),
jump2.TargetAddress());
uword target1 = jump1.TargetAddress();
diff --git a/runtime/vm/instructions_ia32_test.cc b/runtime/vm/instructions_ia32_test.cc
index 12d7e3b..3ceade7 100644
--- a/runtime/vm/instructions_ia32_test.cc
+++ b/runtime/vm/instructions_ia32_test.cc
@@ -20,8 +20,8 @@
}
-ASSEMBLER_TEST_RUN(Call, entry) {
- CallPattern call(entry);
+ASSEMBLER_TEST_RUN(Call, test) {
+ CallPattern call(test->entry());
EXPECT_EQ(StubCode::InstanceFunctionLookupLabel().address(),
call.TargetAddress());
}
@@ -34,11 +34,11 @@
}
-ASSEMBLER_TEST_RUN(Jump, entry) {
- JumpPattern jump1(entry);
+ASSEMBLER_TEST_RUN(Jump, test) {
+ JumpPattern jump1(test->entry());
EXPECT_EQ(StubCode::InstanceFunctionLookupLabel().address(),
jump1.TargetAddress());
- JumpPattern jump2(entry + jump1.pattern_length_in_bytes());
+ JumpPattern jump2(test->entry() + jump1.pattern_length_in_bytes());
EXPECT_EQ(StubCode::AllocateArrayLabel().address(),
jump2.TargetAddress());
uword target1 = jump1.TargetAddress();
diff --git a/runtime/vm/instructions_mips.cc b/runtime/vm/instructions_mips.cc
index 5e115a0..5dd5a0b 100644
--- a/runtime/vm/instructions_mips.cc
+++ b/runtime/vm/instructions_mips.cc
@@ -16,17 +16,30 @@
}
-uword CallOrJumpPattern::TargetAddress() const {
+uword CallPattern::TargetAddress() const {
UNIMPLEMENTED();
return 0;
}
-void CallOrJumpPattern::SetTargetAddress(uword target) const {
+uword JumpPattern::TargetAddress() const {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+
+
+void CallPattern::SetTargetAddress(uword target) const {
UNIMPLEMENTED();
}
+void JumpPattern::SetTargetAddress(uword target) const {
+ UNIMPLEMENTED();
+}
+
+
+
const int* CallPattern::pattern() const {
UNIMPLEMENTED();
return NULL;
diff --git a/runtime/vm/instructions_mips.h b/runtime/vm/instructions_mips.h
index 6da4eb0..47400f7 100644
--- a/runtime/vm/instructions_mips.h
+++ b/runtime/vm/instructions_mips.h
@@ -11,6 +11,7 @@
#endif
#include "vm/allocation.h"
+#include "vm/object.h"
namespace dart {
@@ -22,12 +23,12 @@
// Abstract class for all instruction pattern classes.
class InstructionPattern : public ValueObject {
public:
- explicit InstructionPattern(uword pc) : start_(pc) {
+ explicit InstructionPattern(uword pc) : end_(pc) {
ASSERT(pc != 0);
}
- virtual ~InstructionPattern() {}
+ virtual ~InstructionPattern() { }
- // Call to check if the instruction pattern at 'pc' match the instruction.
+ // Check if the instruction ending at 'end_' matches the expected pattern.
virtual bool IsValid() const {
return TestBytesWith(pattern(), pattern_length_in_bytes());
}
@@ -38,54 +39,53 @@
virtual int pattern_length_in_bytes() const = 0;
protected:
- uword start() const { return start_; }
+ uword end() const { return end_; }
private:
- // Returns true if the 'num_bytes' bytes at 'start_' correspond to
- // array of integers 'data'. 'data' elements are either a byte or -1, which
- // represents any byte.
+ // Returns true if the 'num_bytes' bytes at 'num_bytes' before 'end_'
+ // correspond to array of integers 'data'. 'data' elements are either a byte
+ // or -1, which represents any byte.
bool TestBytesWith(const int* data, int num_bytes) const;
- const uword start_;
+ const uword end_;
DISALLOW_COPY_AND_ASSIGN(InstructionPattern);
};
-class CallOrJumpPattern : public InstructionPattern {
+class CallPattern : public InstructionPattern {
public:
+ CallPattern(uword pc, const Code& code)
+ : InstructionPattern(pc), code_(code) { }
+
+ static const int kLengthInBytes = 1*kWordSize;
+
virtual int pattern_length_in_bytes() const {
return kLengthInBytes;
}
uword TargetAddress() const;
void SetTargetAddress(uword new_target) const;
- protected:
- explicit CallOrJumpPattern(uword pc) : InstructionPattern(pc) {}
- static const int kLengthInBytes = 0;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CallOrJumpPattern);
-};
-
-
-class CallPattern : public CallOrJumpPattern {
- public:
- explicit CallPattern(uword pc) : CallOrJumpPattern(pc) {}
- static int InstructionLength() {
- return kLengthInBytes;
- }
-
private:
virtual const int* pattern() const;
+ const Code& code_;
+
DISALLOW_COPY_AND_ASSIGN(CallPattern);
};
-class JumpPattern : public CallOrJumpPattern {
+class JumpPattern : public InstructionPattern {
public:
- explicit JumpPattern(uword pc) : CallOrJumpPattern(pc) {}
+ explicit JumpPattern(uword pc) : InstructionPattern(pc) { }
+
+ static const int kLengthInBytes = 3*kWordSize;
+
+ virtual int pattern_length_in_bytes() const {
+ return kLengthInBytes;
+ }
+ uword TargetAddress() const;
+ void SetTargetAddress(uword new_target) const;
private:
virtual const int* pattern() const;
diff --git a/runtime/vm/instructions_mips_test.cc b/runtime/vm/instructions_mips_test.cc
index 3fec989..3db5dae 100644
--- a/runtime/vm/instructions_mips_test.cc
+++ b/runtime/vm/instructions_mips_test.cc
@@ -19,8 +19,8 @@
}
-ASSEMBLER_TEST_RUN(Call, entry) {
- CallPattern call(entry);
+ASSEMBLER_TEST_RUN(Call, test) {
+ CallPattern call(test->entry(), test->code());
EXPECT_EQ(StubCode::InstanceFunctionLookupLabel().address(),
call.TargetAddress());
}
@@ -31,11 +31,11 @@
}
-ASSEMBLER_TEST_RUN(Jump, entry) {
- JumpPattern jump1(entry);
+ASSEMBLER_TEST_RUN(Jump, test) {
+ JumpPattern jump1(test->entry());
EXPECT_EQ(StubCode::InstanceFunctionLookupLabel().address(),
jump1.TargetAddress());
- JumpPattern jump2(entry + jump1.pattern_length_in_bytes());
+ JumpPattern jump2(test->entry() + jump1.pattern_length_in_bytes());
EXPECT_EQ(StubCode::AllocateArrayLabel().address(),
jump2.TargetAddress());
uword target1 = jump1.TargetAddress();
diff --git a/runtime/vm/instructions_x64_test.cc b/runtime/vm/instructions_x64_test.cc
index 4108cdf..fb46518 100644
--- a/runtime/vm/instructions_x64_test.cc
+++ b/runtime/vm/instructions_x64_test.cc
@@ -20,8 +20,8 @@
}
-ASSEMBLER_TEST_RUN(Call, entry) {
- CallPattern call(entry);
+ASSEMBLER_TEST_RUN(Call, test) {
+ CallPattern call(test->entry());
EXPECT_EQ(StubCode::InstanceFunctionLookupLabel().address(),
call.TargetAddress());
}
@@ -34,12 +34,12 @@
}
-ASSEMBLER_TEST_RUN(Jump, entry) {
- JumpPattern jump1(entry);
+ASSEMBLER_TEST_RUN(Jump, test) {
+ JumpPattern jump1(test->entry());
jump1.IsValid();
EXPECT_EQ(StubCode::InstanceFunctionLookupLabel().address(),
jump1.TargetAddress());
- JumpPattern jump2(entry + jump1.pattern_length_in_bytes());
+ JumpPattern jump2(test->entry() + jump1.pattern_length_in_bytes());
EXPECT_EQ(StubCode::AllocateArrayLabel().address(),
jump2.TargetAddress());
uword target1 = jump1.TargetAddress();
diff --git a/runtime/vm/intermediate_language.cc b/runtime/vm/intermediate_language.cc
index 9e92536..2b10211 100644
--- a/runtime/vm/intermediate_language.cc
+++ b/runtime/vm/intermediate_language.cc
@@ -66,7 +66,6 @@
}
-
CheckClassInstr::CheckClassInstr(Value* value,
intptr_t deopt_id,
const ICData& unary_checks)
@@ -138,7 +137,8 @@
BinarySmiOpInstr* other_op = other->AsBinarySmiOp();
ASSERT(other_op != NULL);
return (op_kind() == other_op->op_kind()) &&
- (overflow_ == other_op->overflow_);
+ (overflow_ == other_op->overflow_) &&
+ (is_truncating_ == other_op->is_truncating_);
}
@@ -196,8 +196,10 @@
}
-GraphEntryInstr::GraphEntryInstr(TargetEntryInstr* normal_entry)
+GraphEntryInstr::GraphEntryInstr(const ParsedFunction& parsed_function,
+ TargetEntryInstr* normal_entry)
: BlockEntryInstr(0, CatchClauseNode::kInvalidTryIndex),
+ parsed_function_(parsed_function),
normal_entry_(normal_entry),
catch_entries_(),
initial_definitions_(),
@@ -373,21 +375,6 @@
}
-void ForwardInstructionIterator::ReplaceCurrentWith(Definition* other) {
- Definition* defn = current_->AsDefinition();
- ASSERT(defn != NULL);
- defn->ReplaceUsesWith(other);
- ASSERT(other->env() == NULL);
- other->set_env(defn->env());
- defn->set_env(NULL);
- ASSERT(!other->HasSSATemp());
- if (defn->HasSSATemp()) other->set_ssa_temp_index(defn->ssa_temp_index());
-
- other->InsertBefore(current_); // So other will be current.
- RemoveCurrentFromGraph();
-}
-
-
// Default implementation of visiting basic blocks. Can be overridden.
void FlowGraphVisitor::VisitBlocks() {
ASSERT(current_iterator_ == NULL);
@@ -544,22 +531,74 @@
void Definition::ReplaceWith(Definition* other,
ForwardInstructionIterator* iterator) {
- if ((iterator != NULL) && (this == iterator->Current())) {
- iterator->ReplaceCurrentWith(other);
- } else {
- ReplaceUsesWith(other);
- ASSERT(other->env() == NULL);
- other->set_env(env());
- set_env(NULL);
- ASSERT(!other->HasSSATemp());
- if (HasSSATemp()) other->set_ssa_temp_index(ssa_temp_index());
-
- previous()->LinkTo(other);
- other->LinkTo(next());
-
- set_previous(NULL);
- set_next(NULL);
+ // Record other's input uses.
+ for (intptr_t i = other->InputCount() - 1; i >= 0; --i) {
+ Value* input = other->InputAt(i);
+ input->definition()->AddInputUse(input);
+ input->set_instruction(other);
+ input->set_use_index(i);
}
+ // Take other's environment from this definition.
+ ASSERT(other->env() == NULL);
+ intptr_t use_index = 0;
+ for (Environment::DeepIterator it(env()); !it.Done(); it.Advance()) {
+ Value* use = it.CurrentValue();
+ use->set_instruction(other);
+ use->set_use_index(use_index++);
+ }
+ other->set_env(env());
+ set_env(NULL);
+ // Replace all uses of this definition with other.
+ ReplaceUsesWith(other);
+ // Reuse this instruction's SSA name for other.
+ ASSERT(!other->HasSSATemp());
+ if (HasSSATemp()) other->set_ssa_temp_index(ssa_temp_index());
+ // Remove this definition's input uses.
+ UnuseAllInputs();
+
+ // Finally remove this definition from the graph.
+ previous()->LinkTo(other);
+ if ((iterator != NULL) && (this == iterator->Current())) {
+ // Remove through the iterator.
+ other->LinkTo(this);
+ iterator->RemoveCurrentFromGraph();
+ } else {
+ other->LinkTo(next());
+ }
+ set_previous(NULL);
+ set_next(NULL);
+}
+
+
+// A misleadingly named function for use in template functions that replace
+// both definitions with definitions and branch comparisons with
+// comparisons. In the branch case, leave the branch intact and replace its
+// comparison with another comparison.
+void BranchInstr::ReplaceWith(ComparisonInstr* other,
+ ForwardInstructionIterator* ignored) {
+ // Record the new comparison's input uses.
+ for (intptr_t i = other->InputCount() - 1; i >= 0; --i) {
+ Value* input = other->InputAt(i);
+ input->definition()->AddInputUse(input);
+ }
+ SetComparison(other);
+}
+
+
+void BranchInstr::SetComparison(ComparisonInstr* comp) {
+ // The new comparison's input uses are already recorded in their
+ // definition's use lists.
+ for (intptr_t i = comp->InputCount() - 1; i >= 0; --i) {
+ Value* input = comp->InputAt(i);
+ input->set_instruction(this);
+ input->set_use_index(i);
+ }
+ // There should be no need to copy or unuse an environment.
+ ASSERT(comparison()->env() == NULL);
+ // Remove the current comparison's input uses.
+ comparison()->UnuseAllInputs();
+ ASSERT(!comp->HasUses());
+ comparison_ = comp;
}
@@ -843,6 +882,14 @@
return (right_range == NULL)
|| !right_range->IsWithin(0, RangeBoundary::kPlusInfinity);
}
+ case Token::kSHL: {
+ Range* right_range = this->right()->definition()->range();
+ if ((right_range != NULL) && is_truncating()) {
+ // Can deoptimize if right can be negative.
+ return !right_range->IsWithin(0, RangeBoundary::kPlusInfinity);
+ }
+ return true;
+ }
default:
return overflow_;
}
@@ -1083,7 +1130,7 @@
if (call != NULL &&
call->is_known_constructor() &&
(call->Type()->ToCid() == kArrayCid)) {
- return call->ArgumentAt(1)->value()->definition();
+ return call->ArgumentAt(1);
}
return this;
}
@@ -1104,7 +1151,7 @@
return value()->definition();
}
- // (3) For uninstantiated target types: If the instantiator type arguments
+ // For uninstantiated target types: If the instantiator type arguments
// are constant, instantiate the target type here.
if (dst_type().IsInstantiated()) return this;
@@ -1156,17 +1203,13 @@
// It is safe to pass a NULL iterator because we're replacing the
// comparison wrapped in a BranchInstr which does not modify the
// linked list of instructions.
- ReplaceWith(comp, NULL /* ignored */);
- for (intptr_t i = 0; i < comp->InputCount(); ++i) {
- Value* operand = comp->InputAt(i);
- operand->set_instruction(this);
- }
+ SetComparison(comp);
if (FLAG_trace_optimization) {
OS::Print("Merging comparison v%"Pd"\n", comp->ssa_temp_index());
}
- // Clear the comparison's use list, temp index and ssa temp index since
- // the value of the comparison is not used outside the branch anymore.
- comp->set_input_use_list(NULL);
+ // Clear the comparison's temp index and ssa temp index since the
+ // value of the comparison is not used outside the branch anymore.
+ ASSERT(comp->input_use_list() == NULL);
comp->ClearSSATempIndex();
comp->ClearTempIndex();
}
diff --git a/runtime/vm/intermediate_language.h b/runtime/vm/intermediate_language.h
index 7e875e1..4d25a91 100644
--- a/runtime/vm/intermediate_language.h
+++ b/runtime/vm/intermediate_language.h
@@ -22,11 +22,12 @@
class Definition;
class Environment;
class FlowGraphCompiler;
+class FlowGraphOptimizer;
class FlowGraphVisitor;
class Instruction;
class LocalVariable;
+class ParsedFunction;
class Range;
-class FlowGraphOptimizer;
// TODO(srdjan): Unify with INTRINSIC_LIST.
@@ -44,6 +45,30 @@
V(_ByteArrayBase, _getUint32, ByteArrayBaseGetUint32, 261365835) \
V(_ByteArrayBase, _getFloat32, ByteArrayBaseGetFloat32, 434247298) \
V(_ByteArrayBase, _getFloat64, ByteArrayBaseGetFloat64, 434247298) \
+ V(_Float32Array, _getIndexed, Float32ArrayGetIndexed, 734006846) \
+ V(_Float64Array, _getIndexed, Float64ArrayGetIndexed, 498074772) \
+ V(_Int8Array, _getIndexed, Int8ArrayGetIndexed, 712069760) \
+ V(_Uint8Array, _getIndexed, Uint8ArrayGetIndexed, 535849990) \
+ V(_Uint8ClampedArray, _getIndexed, Uint8ClampedArrayGetIndexed, 873344956) \
+ V(_ExternalUint8Array, _getIndexed, ExternalUint8ArrayGetIndexed, 402720239) \
+ V(_ExternalUint8ClampedArray, _getIndexed, \
+ ExternalUint8ClampedArrayGetIndexed, 682839007) \
+ V(_Int16Array, _getIndexed, Int16ArrayGetIndexed, 313999108) \
+ V(_Uint16Array, _getIndexed, Uint16ArrayGetIndexed, 539701175) \
+ V(_Int32Array, _getIndexed, Int32ArrayGetIndexed, 655321526) \
+ V(_Uint32Array, _getIndexed, Uint32ArrayGetIndexed, 1060443550) \
+ V(_Float32Array, _setIndexed, Float32ArraySetIndexed, 1040992157) \
+ V(_Float64Array, _setIndexed, Float64ArraySetIndexed, 330158324) \
+ V(_Int8Array, _setIndexed, Int8ArraySetIndexed, 680713569) \
+ V(_Uint8Array, _setIndexed, Uint8ArraySetIndexed, 785627791) \
+ V(_Uint8ClampedArray, _setIndexed, Uint8ClampedArraySetIndexed, 464766374) \
+ V(_ExternalUint8Array, _setIndexed, ExternalUint8ArraySetIndexed, 159706697) \
+ V(_ExternalUint8ClampedArray, _setIndexed, \
+ ExternalUint8ClampedArraySetIndexed, 335716123) \
+ V(_Int16Array, _setIndexed, Int16ArraySetIndexed, 12169534) \
+ V(_Uint16Array, _setIndexed, Uint16ArraySetIndexed, 36054302) \
+ V(_Int32Array, _setIndexed, Int32ArraySetIndexed, 306194131) \
+ V(_Uint32Array, _setIndexed, Uint32ArraySetIndexed, 410753485) \
V(_GrowableObjectArray, get:length, GrowableArrayLength, 725548050) \
V(_GrowableObjectArray, get:_capacity, GrowableArrayCapacity, 725548050) \
V(_StringBase, get:length, StringBaseLength, 320803993) \
@@ -88,7 +113,7 @@
// Values of CompileType form a lattice with a None type as a bottom and a
// nullable Dynamic type as a top element. Method Union provides a join
// operation for the lattice.
-class CompileType : public ZoneAllocated {
+class CompileType {
public:
static const bool kNullable = true;
static const bool kNonNullable = false;
@@ -125,34 +150,34 @@
// Create a new CompileType representing given combination of class id and
// abstract type. The pair is assumed to be coherent.
- static CompileType* New(intptr_t cid, const AbstractType& type);
+ static CompileType Create(intptr_t cid, const AbstractType& type);
// Create a new CompileType representing given abstract type. By default
// values as assumed to be nullable.
- static CompileType* FromAbstractType(const AbstractType& type,
+ static CompileType FromAbstractType(const AbstractType& type,
bool is_nullable = kNullable);
// Create a new CompileType representing an value with the given class id.
// Resulting CompileType is nullable only if cid is kDynamicCid or kNullCid.
- static CompileType* FromCid(intptr_t cid);
+ static CompileType FromCid(intptr_t cid);
// Create None CompileType. It is the bottom of the lattice and is used to
// represent type of the phi that was not yet inferred.
- static CompileType* None() {
- return new CompileType(true, kIllegalCid, NULL);
+ static CompileType None() {
+ return CompileType(true, kIllegalCid, NULL);
}
// Create Dynamic CompileType. It is the top of the lattice and is used to
// represent unknown type.
- static CompileType* Dynamic();
+ static CompileType Dynamic();
- static CompileType* Null();
+ static CompileType Null();
// Create non-nullable Bool type.
- static CompileType* Bool();
+ static CompileType Bool();
// Create non-nullable Int type.
- static CompileType* Int();
+ static CompileType Int();
// Perform a join operation over the type lattice.
void Union(CompileType* other);
@@ -164,13 +189,6 @@
(ToAbstractType()->Equals(*other->ToAbstractType()));
}
- // Replaces this type with other.
- void ReplaceWith(CompileType* other) {
- is_nullable_ = other->is_nullable_;
- cid_ = other->cid_;
- type_ = other->type_;
- }
-
bool IsNone() const {
return (cid_ == kIllegalCid) && (type_ == NULL);
}
@@ -192,6 +210,21 @@
};
+// Zone allocated wrapper for the CompileType value.
+class ZoneCompileType : public ZoneAllocated {
+ public:
+ static CompileType* Wrap(const CompileType& type) {
+ ZoneCompileType* zone_type = new ZoneCompileType(type);
+ return &zone_type->type_;
+ }
+
+ private:
+ explicit ZoneCompileType(const CompileType& type) : type_(type) { }
+
+ CompileType type_;
+};
+
+
class Value : public ZoneAllocated {
public:
// A forward iterator that allows removing the current value from the
@@ -228,6 +261,10 @@
Value* next_use() const { return next_use_; }
void set_next_use(Value* next) { next_use_ = next; }
+ bool IsSingleUse() const {
+ return (next_use_ == NULL) && (previous_use_ == NULL);
+ }
+
Instruction* instruction() const { return instruction_; }
void set_instruction(Instruction* instruction) { instruction_ = instruction; }
@@ -239,6 +276,14 @@
Value* Copy() { return new Value(definition_); }
+ // This function must only be used when the new Value is dominated by
+ // the original Value.
+ Value* CopyWithType() {
+ Value* copy = new Value(definition_);
+ copy->reaching_type_ = reaching_type_;
+ return copy;
+ }
+
CompileType* Type();
void SetReachingType(CompileType* type) {
@@ -249,6 +294,8 @@
const char* DebugName() const { return "Value"; }
+ bool IsSmiValue() { return Type()->ToCid() == kSmiCid; }
+
// Return true if the value represents a constant.
bool BindsToConstant() const;
@@ -472,10 +519,11 @@
// Call instructions override this function and return the number of
// pushed arguments.
virtual intptr_t ArgumentCount() const = 0;
- virtual PushArgumentInstr* ArgumentAt(intptr_t index) const {
+ virtual PushArgumentInstr* PushArgumentAt(intptr_t index) const {
UNREACHABLE();
return NULL;
- };
+ }
+ inline Definition* ArgumentAt(intptr_t index) const;
// Returns true, if this instruction can deoptimize.
virtual bool CanDeoptimize() const = 0;
@@ -957,10 +1005,6 @@
// Removes 'current_' from graph and sets 'current_' to previous instruction.
void RemoveCurrentFromGraph();
- // Inserts replaces 'current_', which must be a definition, with another
- // definition. The new definition becomes 'current_'.
- void ReplaceCurrentWith(Definition* other);
-
Instruction* Current() const { return current_; }
private:
@@ -993,7 +1037,8 @@
class GraphEntryInstr : public BlockEntryInstr {
public:
- explicit GraphEntryInstr(TargetEntryInstr* normal_entry);
+ GraphEntryInstr(const ParsedFunction& parsed_function,
+ TargetEntryInstr* normal_entry);
DECLARE_INSTRUCTION(GraphEntry)
@@ -1022,12 +1067,17 @@
TargetEntryInstr* normal_entry() const { return normal_entry_; }
+ const ParsedFunction& parsed_function() const {
+ return parsed_function_;
+ }
+
virtual void PrintTo(BufferFormatter* f) const;
private:
virtual void ClearPredecessors() {}
virtual void AddPredecessor(BlockEntryInstr* predecessor) { UNREACHABLE(); }
+ const ParsedFunction& parsed_function_;
TargetEntryInstr* normal_entry_;
GrowableArray<TargetEntryInstr*> catch_entries_;
GrowableArray<Definition*> initial_definitions_;
@@ -1088,7 +1138,7 @@
public:
explicit PhiIterator(JoinEntryInstr* join)
: phis_(join->phis()), index_(-1) {
- if (!Done()) Advance(); // Advance to the first smi.
+ if (!Done()) Advance(); // Advance to the first phi.
}
void Advance() {
@@ -1207,10 +1257,14 @@
return type_;
}
- // Compute initial compile type for this definition. It is safe to use this
+ virtual CompileType* ComputeInitialType() const {
+ return ZoneCompileType::Wrap(ComputeType());
+ }
+
+ // Compute compile type for this definition. It is safe to use this
// approximation even before type propagator was run (e.g. during graph
// building).
- virtual CompileType* ComputeInitialType() const {
+ virtual CompileType ComputeType() const {
return CompileType::Dynamic();
}
@@ -1219,6 +1273,20 @@
return false;
}
+ bool UpdateType(CompileType new_type) {
+ if (type_ == NULL) {
+ type_ = ZoneCompileType::Wrap(new_type);
+ return true;
+ }
+
+ if (type_->IsNone() || !type_->IsEqualTo(&new_type)) {
+ *type_ = new_type;
+ return true;
+ }
+
+ return false;
+ }
+
bool HasUses() const {
return (input_use_list_ != NULL) || (env_use_list_ != NULL);
}
@@ -1318,7 +1386,7 @@
virtual BlockEntryInstr* GetBlock() const { return block(); }
JoinEntryInstr* block() const { return block_; }
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
virtual bool RecomputeType();
virtual intptr_t ArgumentCount() const { return 0; }
@@ -1336,7 +1404,6 @@
// Phi is alive if it reaches a non-environment use.
bool is_alive() const { return is_alive_; }
void mark_alive() { is_alive_ = true; }
- void mark_dead() { is_alive_ = false; }
virtual Representation RequiredInputRepresentation(intptr_t i) const {
return representation_;
@@ -1415,7 +1482,7 @@
virtual void PrintOperandsTo(BufferFormatter* f) const;
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
private:
const intptr_t index_;
@@ -1446,7 +1513,7 @@
virtual intptr_t ArgumentCount() const { return 0; }
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
Value* value() const { return value_; }
@@ -1476,6 +1543,11 @@
};
+inline Definition* Instruction::ArgumentAt(intptr_t index) const {
+ return PushArgumentAt(index)->value()->definition();
+}
+
+
class ReturnInstr : public TemplateInstruction<1> {
public:
ReturnInstr(intptr_t token_pos, Value* value)
@@ -1633,7 +1705,7 @@
virtual bool HasSideEffect() const;
ComparisonInstr* comparison() const { return comparison_; }
- void set_comparison(ComparisonInstr* value) { comparison_ = value; }
+ void SetComparison(ComparisonInstr* comp);
bool is_checked() const { return is_checked_; }
@@ -1641,11 +1713,13 @@
virtual intptr_t DeoptimizationTarget() const;
virtual Representation RequiredInputRepresentation(intptr_t i) const;
- // Replace the comparison with another, leaving the branch intact.
+ // A misleadingly named function for use in template functions that also
+ // replace definitions. In this case, leave the branch intact and replace
+ // its comparison with another comparison that has been removed from the
+ // graph but still has uses properly linked into their definition's use
+ // list.
void ReplaceWith(ComparisonInstr* other,
- ForwardInstructionIterator* ignored) {
- comparison_ = other;
- }
+ ForwardInstructionIterator* ignored);
virtual Instruction* Canonicalize(FlowGraphOptimizer* optimizer);
@@ -1885,7 +1959,7 @@
return (inputs_[1] == NULL) ? 1 : 2;
}
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
virtual bool CanDeoptimize() const { return false; }
@@ -1932,7 +2006,7 @@
: value_(value) { }
DECLARE_INSTRUCTION(Constant)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
const Object& value() const { return value_; }
@@ -2019,7 +2093,7 @@
}
DECLARE_INSTRUCTION(AssertBoolean)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
intptr_t token_pos() const { return token_pos_; }
Value* value() const { return inputs_[0]; }
@@ -2052,7 +2126,7 @@
}
DECLARE_INSTRUCTION(ArgumentDefinitionTest)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
intptr_t token_pos() const { return ast_node_.token_pos(); }
intptr_t formal_parameter_index() const {
@@ -2084,7 +2158,7 @@
CurrentContextInstr() { }
DECLARE_INSTRUCTION(CurrentContext)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
virtual bool CanDeoptimize() const { return false; }
@@ -2108,7 +2182,7 @@
intptr_t token_pos() const { return ast_node_.token_pos(); }
virtual intptr_t ArgumentCount() const { return arguments_->length(); }
- PushArgumentInstr* ArgumentAt(intptr_t index) const {
+ virtual PushArgumentInstr* PushArgumentAt(intptr_t index) const {
return (*arguments_)[index];
}
@@ -2167,7 +2241,7 @@
const String& function_name() const { return function_name_; }
Token::Kind token_kind() const { return token_kind_; }
virtual intptr_t ArgumentCount() const { return arguments_->length(); }
- PushArgumentInstr* ArgumentAt(intptr_t index) const {
+ virtual PushArgumentInstr* PushArgumentAt(intptr_t index) const {
return (*arguments_)[index];
}
const Array& argument_names() const { return argument_names_; }
@@ -2213,8 +2287,8 @@
virtual intptr_t ArgumentCount() const {
return instance_call()->ArgumentCount();
}
- PushArgumentInstr* ArgumentAt(intptr_t index) const {
- return instance_call()->ArgumentAt(index);
+ virtual PushArgumentInstr* PushArgumentAt(intptr_t index) const {
+ return instance_call()->PushArgumentAt(index);
}
DECLARE_INSTRUCTION(PolymorphicInstanceCall)
@@ -2321,7 +2395,7 @@
StrictCompareInstr(Token::Kind kind, Value* left, Value* right);
DECLARE_INSTRUCTION(StrictCompare)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
virtual void PrintOperandsTo(BufferFormatter* f) const;
@@ -2364,7 +2438,8 @@
}
DECLARE_INSTRUCTION(EqualityCompare)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
+ virtual bool RecomputeType();
const ICData* ic_data() const { return ic_data_; }
bool HasICData() const {
@@ -2433,7 +2508,8 @@
}
DECLARE_INSTRUCTION(RelationalOp)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
+ virtual bool RecomputeType();
const ICData* ic_data() const { return ic_data_; }
bool HasICData() const {
@@ -2506,7 +2582,7 @@
}
DECLARE_INSTRUCTION(StaticCall)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
// Accessors forwarded to the AST node.
const Function& function() const { return function_; }
@@ -2514,7 +2590,7 @@
intptr_t token_pos() const { return token_pos_; }
virtual intptr_t ArgumentCount() const { return arguments_->length(); }
- PushArgumentInstr* ArgumentAt(intptr_t index) const {
+ virtual PushArgumentInstr* PushArgumentAt(intptr_t index) const {
return (*arguments_)[index];
}
@@ -2552,7 +2628,7 @@
context_level_(context_level) { }
DECLARE_INSTRUCTION(LoadLocal)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
const LocalVariable& local() const { return local_; }
intptr_t context_level() const { return context_level_; }
@@ -2687,7 +2763,7 @@
explicit LoadStaticFieldInstr(const Field& field) : field_(field) {}
DECLARE_INSTRUCTION(LoadStaticField);
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
const Field& field() const { return field_; }
@@ -2751,7 +2827,7 @@
}
DECLARE_INSTRUCTION(LoadIndexed)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
Value* array() const { return inputs_[0]; }
Value* index() const { return inputs_[1]; }
@@ -2792,7 +2868,7 @@
}
DECLARE_INSTRUCTION(StringFromCharCode)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
Value* char_code() const { return inputs_[0]; }
@@ -2871,7 +2947,7 @@
}
DECLARE_INSTRUCTION(BooleanNegate)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
Value* value() const { return inputs_[0]; }
@@ -2905,7 +2981,7 @@
}
DECLARE_INSTRUCTION(InstanceOf)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
Value* value() const { return inputs_[0]; }
Value* instantiator() const { return inputs_[1]; }
@@ -2945,10 +3021,10 @@
}
DECLARE_INSTRUCTION(AllocateObject)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
virtual intptr_t ArgumentCount() const { return arguments_->length(); }
- PushArgumentInstr* ArgumentAt(intptr_t index) const {
+ virtual PushArgumentInstr* PushArgumentAt(intptr_t index) const {
return (*arguments_)[index];
}
@@ -3019,7 +3095,7 @@
}
DECLARE_INSTRUCTION(CreateArray)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
intptr_t num_elements() const { return num_elements_; }
@@ -3052,13 +3128,13 @@
token_pos_(token_pos) { }
DECLARE_INSTRUCTION(CreateClosure)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
intptr_t token_pos() const { return token_pos_; }
const Function& function() const { return function_; }
virtual intptr_t ArgumentCount() const { return arguments_->length(); }
- PushArgumentInstr* ArgumentAt(intptr_t index) const {
+ virtual PushArgumentInstr* PushArgumentAt(intptr_t index) const {
return (*arguments_)[index];
}
@@ -3094,7 +3170,7 @@
}
DECLARE_INSTRUCTION(LoadField)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
Value* value() const { return inputs_[0]; }
intptr_t offset_in_bytes() const { return offset_in_bytes_; }
@@ -3280,7 +3356,7 @@
num_context_variables_(num_context_variables) {}
DECLARE_INSTRUCTION(AllocateContext);
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
intptr_t token_pos() const { return token_pos_; }
intptr_t num_context_variables() const { return num_context_variables_; }
@@ -3333,7 +3409,7 @@
Value* context_value() const { return inputs_[0]; }
DECLARE_INSTRUCTION(CloneContext)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
virtual bool CanDeoptimize() const { return true; }
@@ -3433,7 +3509,7 @@
}
DECLARE_INSTRUCTION(BoxDouble)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
private:
const intptr_t token_pos_;
@@ -3464,7 +3540,7 @@
}
DECLARE_INSTRUCTION(BoxInteger)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
private:
DISALLOW_COPY_AND_ASSIGN(BoxIntegerInstr);
@@ -3496,7 +3572,7 @@
virtual bool AttributesEqual(Instruction* other) const { return true; }
DECLARE_INSTRUCTION(UnboxDouble)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
private:
DISALLOW_COPY_AND_ASSIGN(UnboxDoubleInstr);
@@ -3520,7 +3596,7 @@
virtual bool HasSideEffect() const { return false; }
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
virtual Representation representation() const {
return kUnboxedMint;
@@ -3571,7 +3647,7 @@
}
DECLARE_INSTRUCTION(MathSqrt)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
private:
DISALLOW_COPY_AND_ASSIGN(MathSqrtInstr);
@@ -3625,7 +3701,7 @@
}
DECLARE_INSTRUCTION(BinaryDoubleOp)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
virtual Definition* Canonicalize(FlowGraphOptimizer* optimizer);
@@ -3642,7 +3718,8 @@
Value* left,
Value* right,
InstanceCallInstr* instance_call)
- : op_kind_(op_kind) {
+ : op_kind_(op_kind),
+ instance_call_(instance_call) {
ASSERT(left != NULL);
ASSERT(right != NULL);
inputs_[0] = left;
@@ -3655,6 +3732,8 @@
Token::Kind op_kind() const { return op_kind_; }
+ InstanceCallInstr* instance_call() const { return instance_call_; }
+
virtual void PrintOperandsTo(BufferFormatter* f) const;
virtual bool CanDeoptimize() const {
@@ -3666,10 +3745,11 @@
virtual bool AffectedBySideEffect() const { return false; }
virtual bool AttributesEqual(Instruction* other) const {
+ ASSERT(other->IsBinaryMintOp());
return op_kind() == other->AsBinaryMintOp()->op_kind();
}
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
virtual Representation representation() const {
return kUnboxedMint;
@@ -3692,6 +3772,7 @@
private:
const Token::Kind op_kind_;
+ InstanceCallInstr* instance_call_;
DISALLOW_COPY_AND_ASSIGN(BinaryMintOpInstr);
};
@@ -3729,7 +3810,7 @@
return op_kind() == other->AsShiftMintOp()->op_kind();
}
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
virtual Representation representation() const {
return kUnboxedMint;
@@ -3783,7 +3864,7 @@
return op_kind() == other->AsUnaryMintOp()->op_kind();
}
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
virtual Representation representation() const {
return kUnboxedMint;
@@ -3817,7 +3898,8 @@
Value* right)
: op_kind_(op_kind),
instance_call_(instance_call),
- overflow_(true) {
+ overflow_(true),
+ is_truncating_(false) {
ASSERT(left != NULL);
ASSERT(right != NULL);
inputs_[0] = left;
@@ -3838,7 +3920,7 @@
DECLARE_INSTRUCTION(BinarySmiOp)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
virtual bool CanDeoptimize() const;
@@ -3851,6 +3933,11 @@
overflow_ = overflow;
}
+ void set_is_truncating(bool value) {
+ is_truncating_ = value;
+ }
+ bool is_truncating() const { return is_truncating_; }
+
void PrintTo(BufferFormatter* f) const;
virtual void InferRange();
@@ -3865,6 +3952,7 @@
const Token::Kind op_kind_;
InstanceCallInstr* instance_call_;
bool overflow_;
+ bool is_truncating_;
DISALLOW_COPY_AND_ASSIGN(BinarySmiOpInstr);
};
@@ -3889,7 +3977,7 @@
virtual void PrintOperandsTo(BufferFormatter* f) const;
DECLARE_INSTRUCTION(UnarySmiOp)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
virtual bool CanDeoptimize() const { return op_kind() == Token::kNEGATE; }
@@ -3932,7 +4020,7 @@
InstanceCallInstr* instance_call() const { return instance_call_; }
DECLARE_INSTRUCTION(SmiToDouble)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
virtual intptr_t ArgumentCount() const { return 1; }
@@ -3959,7 +4047,7 @@
InstanceCallInstr* instance_call() const { return instance_call_; }
DECLARE_INSTRUCTION(DoubleToInteger)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
virtual intptr_t ArgumentCount() const { return 1; }
@@ -3987,7 +4075,7 @@
Value* value() const { return inputs_[0]; }
DECLARE_INSTRUCTION(DoubleToSmi)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
virtual bool CanDeoptimize() const { return true; }
@@ -4021,7 +4109,7 @@
MethodRecognizer::Kind recognized_kind() const { return recognized_kind_; }
DECLARE_INSTRUCTION(DoubleToDouble)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
virtual bool CanDeoptimize() const { return false; }
@@ -4062,7 +4150,7 @@
MethodRecognizer::Kind recognized_kind() const { return recognized_kind_; }
DECLARE_INSTRUCTION(InvokeMathCFunction)
- virtual CompileType* ComputeInitialType() const;
+ virtual CompileType ComputeType() const;
virtual void PrintOperandsTo(BufferFormatter* f) const;
virtual bool CanDeoptimize() const { return false; }
diff --git a/runtime/vm/intermediate_language_arm.cc b/runtime/vm/intermediate_language_arm.cc
index 2e3f226..f35ed33 100644
--- a/runtime/vm/intermediate_language_arm.cc
+++ b/runtime/vm/intermediate_language_arm.cc
@@ -172,9 +172,9 @@
}
-CompileType* LoadIndexedInstr::ComputeInitialType() const {
+CompileType LoadIndexedInstr::ComputeType() const {
UNIMPLEMENTED();
- return NULL;
+ return CompileType::Dynamic();
}
diff --git a/runtime/vm/intermediate_language_ia32.cc b/runtime/vm/intermediate_language_ia32.cc
index a475fd6..50a4972 100644
--- a/runtime/vm/intermediate_language_ia32.cc
+++ b/runtime/vm/intermediate_language_ia32.cc
@@ -796,7 +796,6 @@
ASSERT((kind() == Token::kNE) || (kind() == Token::kEQ));
BranchInstr* kNoBranch = NULL;
if (receiver_class_id() == kSmiCid) {
- // Deoptimizes if both arguments not Smi.
EmitSmiComparisonOp(compiler, *locs(), kind(), kNoBranch);
return;
}
@@ -1095,7 +1094,7 @@
}
-CompileType* LoadIndexedInstr::ComputeInitialType() const {
+CompileType LoadIndexedInstr::ComputeType() const {
switch (class_id_) {
case kArrayCid:
case kImmutableArrayCid:
@@ -1314,7 +1313,7 @@
return kTagged;
case kInt32ArrayCid:
case kUint32ArrayCid:
- return kUnboxedMint;
+ return value()->IsSmiValue() ? kTagged : kUnboxedMint;
case kFloat32ArrayCid :
case kFloat64ArrayCid :
return kUnboxedDouble;
@@ -1368,12 +1367,18 @@
// Writable register because the value must be untagged before storing.
locs->set_in(2, Location::WritableRegister());
break;
+ case kInt32ArrayCid:
+ case kUint32ArrayCid:
+ // Mints are stored in XMM registers. For smis, use a writable register
+ // because the value must be untagged before storing.
+ locs->set_in(2, value()->IsSmiValue()
+ ? Location::WritableRegister()
+ : Location::RequiresFpuRegister());
+ break;
case kFloat32ArrayCid:
// Need temp register for float-to-double conversion.
locs->AddTemp(Location::RequiresFpuRegister());
// Fall through.
- case kInt32ArrayCid:
- case kUint32ArrayCid:
case kFloat64ArrayCid:
// TODO(srdjan): Support Float64 constants.
locs->set_in(2, Location::RequiresFpuRegister());
@@ -1483,7 +1488,15 @@
}
case kInt32ArrayCid:
case kUint32ArrayCid:
+ if (value()->IsSmiValue()) {
+ ASSERT(RequiredInputRepresentation(2) == kTagged);
+ Register value = locs()->in(2).reg();
+ __ SmiUntag(value);
+ __ movl(element_address, value);
+ } else {
+ ASSERT(RequiredInputRepresentation(2) == kUnboxedMint);
__ movss(element_address, locs()->in(2).fpu_reg());
+ }
break;
case kFloat32ArrayCid:
// Convert to single precision.
@@ -1986,6 +1999,131 @@
}
+static void EmitSmiShiftLeft(FlowGraphCompiler* compiler,
+ BinarySmiOpInstr* shift_left) {
+ const bool is_truncating = shift_left->is_truncating();
+ const LocationSummary& locs = *shift_left->locs();
+ Register left = locs.in(0).reg();
+ Register result = locs.out().reg();
+ ASSERT(left == result);
+ Label* deopt = shift_left->CanDeoptimize() ?
+ compiler->AddDeoptStub(shift_left->deopt_id(), kDeoptBinarySmiOp) : NULL;
+ if (locs.in(1).IsConstant()) {
+ const Object& constant = locs.in(1).constant();
+ ASSERT(constant.IsSmi());
+ // shll operation masks the count to 5 bits.
+ const intptr_t kCountLimit = 0x1F;
+ const intptr_t value = Smi::Cast(constant).Value();
+ if (value == 0) {
+ // No code needed.
+ } else if ((value < 0) || (value >= kCountLimit)) {
+ // This condition may not be known earlier in some cases because
+ // of constant propagation, inlining, etc.
+ if ((value >=kCountLimit) && is_truncating) {
+ __ xorl(result, result);
+ } else {
+ // Result is Mint or exception.
+ __ jmp(deopt);
+ }
+ } else {
+ if (!is_truncating) {
+ // Check for overflow.
+ Register temp = locs.temp(0).reg();
+ __ movl(temp, left);
+ __ shll(left, Immediate(value));
+ __ sarl(left, Immediate(value));
+ __ cmpl(left, temp);
+ __ j(NOT_EQUAL, deopt); // Overflow.
+ }
+ // Shift for result now we know there is no overflow.
+ __ shll(left, Immediate(value));
+ }
+ return;
+ }
+
+ // Right (locs.in(1)) is not constant.
+ Register right = locs.in(1).reg();
+ Range* right_range = shift_left->right()->definition()->range();
+ if (shift_left->left()->BindsToConstant() && !is_truncating) {
+ // TODO(srdjan): Implement code below for is_truncating().
+ // If left is constant, we know the maximal allowed size for right.
+ const Object& obj = shift_left->left()->BoundConstant();
+ if (obj.IsSmi()) {
+ const intptr_t left_int = Smi::Cast(obj).Value();
+ if (left_int == 0) {
+ __ cmpl(right, Immediate(0));
+ __ j(NEGATIVE, deopt);
+ return;
+ }
+ intptr_t tmp = (left_int > 0) ? left_int : ~left_int;
+ intptr_t max_right = kSmiBits;
+ while ((tmp >>= 1) != 0) {
+ max_right--;
+ }
+ const bool right_needs_check =
+ (right_range == NULL) ||
+ !right_range->IsWithin(0, max_right - 1);
+ if (right_needs_check) {
+ __ cmpl(right,
+ Immediate(reinterpret_cast<int32_t>(Smi::New(max_right))));
+ __ j(ABOVE_EQUAL, deopt);
+ }
+ __ SmiUntag(right);
+ __ shll(left, right);
+ }
+ return;
+ }
+
+ const bool right_needs_check =
+ (right_range == NULL) || !right_range->IsWithin(0, (Smi::kBits - 1));
+ ASSERT(right == ECX); // Count must be in ECX
+ if (is_truncating) {
+ if (right_needs_check) {
+ const bool right_may_be_negative =
+ (right_range == NULL) ||
+ !right_range->IsWithin(0, RangeBoundary::kPlusInfinity);
+ if (right_may_be_negative) {
+ ASSERT(shift_left->CanDeoptimize());
+ __ cmpl(right, Immediate(0));
+ __ j(NEGATIVE, deopt);
+ }
+ Label done, is_not_zero;
+ __ cmpl(right,
+ Immediate(reinterpret_cast<int32_t>(Smi::New(Smi::kBits))));
+ __ j(BELOW, &is_not_zero, Assembler::kNearJump);
+ __ xorl(left, left);
+ __ jmp(&done, Assembler::kNearJump);
+ __ Bind(&is_not_zero);
+ __ SmiUntag(right);
+ __ shll(left, right);
+ __ Bind(&done);
+ } else {
+ __ SmiUntag(right);
+ __ shll(left, right);
+ }
+ } else {
+ if (right_needs_check) {
+ ASSERT(shift_left->CanDeoptimize());
+ __ cmpl(right,
+ Immediate(reinterpret_cast<int32_t>(Smi::New(Smi::kBits))));
+ __ j(ABOVE_EQUAL, deopt);
+ }
+ // Left is not a constant.
+ Register temp = locs.temp(0).reg();
+ // Check if count too large for handling it inlined.
+ __ movl(temp, left);
+ __ SmiUntag(right);
+ // Overflow test (preserve temp and right);
+ __ shll(left, right);
+ __ sarl(left, right);
+ __ cmpl(left, temp);
+ __ j(NOT_EQUAL, deopt); // Overflow.
+ // Shift for result now we know there is no overflow.
+ __ shll(left, right);
+ }
+}
+
+
LocationSummary* BinarySmiOpInstr::MakeLocationSummary() const {
const intptr_t kNumInputs = 2;
if (op_kind() == Token::kTRUNCDIV) {
@@ -2016,12 +2154,14 @@
summary->set_out(Location::SameAsFirstInput());
return summary;
} else if (op_kind() == Token::kSHL) {
- const intptr_t kNumTemps = 1;
+ const intptr_t kNumTemps = 0;
LocationSummary* summary =
new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
summary->set_in(0, Location::RequiresRegister());
summary->set_in(1, Location::FixedRegisterOrSmiConstant(right(), ECX));
- summary->set_temp(0, Location::RequiresRegister());
+ if (!is_truncating()) {
+ summary->AddTemp(Location::RequiresRegister());
+ }
summary->set_out(Location::SameAsFirstInput());
return summary;
} else {
@@ -2037,6 +2177,12 @@
void BinarySmiOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ if (op_kind() == Token::kSHL) {
+ EmitSmiShiftLeft(compiler, this);
+ return;
+ }
+
+ ASSERT(!is_truncating());
Register left = locs()->in(0).reg();
Register result = locs()->out().reg();
ASSERT(left == result);
@@ -2134,27 +2280,6 @@
__ SmiTag(left);
break;
}
- case Token::kSHL: {
- // shll operation masks the count to 5 bits.
- const intptr_t kCountLimit = 0x1F;
- intptr_t value = Smi::Cast(constant).Value();
- if (value == 0) break;
- if ((value < 0) || (value >= kCountLimit)) {
- // This condition may not be known earlier in some cases because
- // of constant propagation, inlining, etc.
- __ jmp(deopt);
- break;
- }
- Register temp = locs()->temp(0).reg();
- __ movl(temp, left);
- __ shll(left, Immediate(value));
- __ sarl(left, Immediate(value));
- __ cmpl(left, temp);
- __ j(NOT_EQUAL, deopt); // Overflow.
- // Shift for result now we know there is no overflow.
- __ shll(left, Immediate(value));
- break;
- }
default:
UNREACHABLE();
@@ -2238,57 +2363,6 @@
__ SmiTag(left);
break;
}
- case Token::kSHL: {
- Range* right_range = this->right()->definition()->range();
- if (this->left()->BindsToConstant()) {
- // If left is constant, we know the maximal allowed size for right.
- const Object& obj = this->left()->BoundConstant();
- if (obj.IsSmi()) {
- const intptr_t left_int = Smi::Cast(obj).Value();
- if (left_int == 0) {
- __ cmpl(right, Immediate(0));
- __ j(NEGATIVE, deopt);
- break;
- }
- intptr_t tmp = (left_int > 0) ? left_int : ~left_int;
- intptr_t max_right = kSmiBits;
- while ((tmp >>= 1) != 0) {
- max_right--;
- }
- const bool right_needs_check =
- (right_range == NULL) ||
- !right_range->IsWithin(0, max_right - 1);
- if (right_needs_check) {
- __ cmpl(right,
- Immediate(reinterpret_cast<int32_t>(Smi::New(max_right))));
- __ j(ABOVE_EQUAL, deopt);
- }
- __ SmiUntag(right);
- __ shll(left, right);
- break;
- }
- }
- Register temp = locs()->temp(0).reg();
- // Check if count too large for handling it inlined.
- __ movl(temp, left);
- const bool right_needs_check =
- (right_range == NULL) || !right_range->IsWithin(0, (Smi::kBits - 1));
- if (right_needs_check) {
- __ cmpl(right,
- Immediate(reinterpret_cast<int32_t>(Smi::New(Smi::kBits))));
- __ j(ABOVE_EQUAL, deopt);
- }
- ASSERT(right == ECX); // Count must be in ECX
- __ SmiUntag(right);
- // Overflow test (preserve temp and right);
- __ shll(left, right);
- __ sarl(left, right);
- __ cmpl(left, temp);
- __ j(NOT_EQUAL, deopt); // Overflow.
- // Shift for result now we know there is no overflow.
- __ shll(left, right);
- break;
- }
case Token::kDIV: {
// Dispatches to 'Double./'.
// TODO(srdjan): Implement as conversion to double and double division.
@@ -2315,25 +2389,37 @@
LocationSummary* CheckEitherNonSmiInstr::MakeLocationSummary() const {
- ASSERT((left()->Type()->ToCid() != kDoubleCid) &&
- (right()->Type()->ToCid() != kDoubleCid));
+ intptr_t left_cid = left()->Type()->ToCid();
+ intptr_t right_cid = right()->Type()->ToCid();
+ ASSERT((left_cid != kDoubleCid) && (right_cid != kDoubleCid));
const intptr_t kNumInputs = 2;
- const intptr_t kNumTemps = 1;
+ const bool need_temp = (left_cid != kSmiCid) && (right_cid != kSmiCid);
+ const intptr_t kNumTemps = need_temp ? 1 : 0;
LocationSummary* summary =
new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
summary->set_in(0, Location::RequiresRegister());
summary->set_in(1, Location::RequiresRegister());
- summary->set_temp(0, Location::RequiresRegister());
+ if (need_temp) summary->set_temp(0, Location::RequiresRegister());
return summary;
}
void CheckEitherNonSmiInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
Label* deopt = compiler->AddDeoptStub(deopt_id(), kDeoptBinaryDoubleOp);
- Register temp = locs()->temp(0).reg();
- __ movl(temp, locs()->in(0).reg());
- __ orl(temp, locs()->in(1).reg());
- __ testl(temp, Immediate(kSmiTagMask));
+ intptr_t left_cid = left()->Type()->ToCid();
+ intptr_t right_cid = right()->Type()->ToCid();
+ Register left = locs()->in(0).reg();
+ Register right = locs()->in(1).reg();
+ if (left_cid == kSmiCid) {
+ __ testl(right, Immediate(kSmiTagMask));
+ } else if (right_cid == kSmiCid) {
+ __ testl(left, Immediate(kSmiTagMask));
+ } else {
+ Register temp = locs()->temp(0).reg();
+ __ movl(temp, left);
+ __ orl(temp, right);
+ __ testl(temp, Immediate(kSmiTagMask));
+ }
__ j(ZERO, deopt);
}
diff --git a/runtime/vm/intermediate_language_mips.cc b/runtime/vm/intermediate_language_mips.cc
index 2ec2dc6..5b392ce 100644
--- a/runtime/vm/intermediate_language_mips.cc
+++ b/runtime/vm/intermediate_language_mips.cc
@@ -172,9 +172,9 @@
}
-CompileType* LoadIndexedInstr::ComputeInitialType() const {
+CompileType LoadIndexedInstr::ComputeType() const {
UNIMPLEMENTED();
- return NULL;
+ return CompileType::Dynamic();
}
diff --git a/runtime/vm/intermediate_language_x64.cc b/runtime/vm/intermediate_language_x64.cc
index 41b82f9..7b8f669 100644
--- a/runtime/vm/intermediate_language_x64.cc
+++ b/runtime/vm/intermediate_language_x64.cc
@@ -959,7 +959,7 @@
}
-CompileType* LoadIndexedInstr::ComputeInitialType() const {
+CompileType LoadIndexedInstr::ComputeType() const {
switch (class_id_) {
case kArrayCid:
case kImmutableArrayCid:
@@ -984,7 +984,7 @@
default:
UNIMPLEMENTED();
- return NULL;
+ return CompileType::Dynamic();
}
}
@@ -1817,6 +1817,131 @@
}
+static void EmitSmiShiftLeft(FlowGraphCompiler* compiler,
+ BinarySmiOpInstr* shift_left) {
+ const bool is_truncating = shift_left->is_truncating();
+ const LocationSummary& locs = *shift_left->locs();
+ Register left = locs.in(0).reg();
+ Register result = locs.out().reg();
+ ASSERT(left == result);
+ Label* deopt = shift_left->CanDeoptimize() ?
+ compiler->AddDeoptStub(shift_left->deopt_id(), kDeoptBinarySmiOp) : NULL;
+ if (locs.in(1).IsConstant()) {
+ const Object& constant = locs.in(1).constant();
+ ASSERT(constant.IsSmi());
+ // shll operation masks the count to 6 bits.
+ const intptr_t kCountLimit = 0x3F;
+ const intptr_t value = Smi::Cast(constant).Value();
+ if (value == 0) {
+ // No code needed.
+ } else if ((value < 0) || (value >= kCountLimit)) {
+ // This condition may not be known earlier in some cases because
+ // of constant propagation, inlining, etc.
+ if ((value >=kCountLimit) && is_truncating) {
+ __ xorq(result, result);
+ } else {
+ // Result is Mint or exception.
+ __ jmp(deopt);
+ }
+ } else {
+ if (!is_truncating) {
+ // Check for overflow.
+ Register temp = locs.temp(0).reg();
+ __ movq(temp, left);
+ __ shlq(left, Immediate(value));
+ __ sarq(left, Immediate(value));
+ __ cmpq(left, temp);
+ __ j(NOT_EQUAL, deopt); // Overflow.
+ }
+ // Shift for result now we know there is no overflow.
+ __ shlq(left, Immediate(value));
+ }
+ return;
+ }
+
+ // Right (locs.in(1)) is not constant.
+ Register right = locs.in(1).reg();
+ Range* right_range = shift_left->right()->definition()->range();
+ if (shift_left->left()->BindsToConstant() && !is_truncating) {
+ // TODO(srdjan): Implement code below for is_truncating().
+ // If left is constant, we know the maximal allowed size for right.
+ const Object& obj = shift_left->left()->BoundConstant();
+ if (obj.IsSmi()) {
+ const intptr_t left_int = Smi::Cast(obj).Value();
+ if (left_int == 0) {
+ __ cmpq(right, Immediate(0));
+ __ j(NEGATIVE, deopt);
+ return;
+ }
+ intptr_t tmp = (left_int > 0) ? left_int : ~left_int;
+ intptr_t max_right = kSmiBits;
+ while ((tmp >>= 1) != 0) {
+ max_right--;
+ }
+ const bool right_needs_check =
+ (right_range == NULL) ||
+ !right_range->IsWithin(0, max_right - 1);
+ if (right_needs_check) {
+ __ cmpq(right,
+ Immediate(reinterpret_cast<int64_t>(Smi::New(max_right))));
+ __ j(ABOVE_EQUAL, deopt);
+ }
+ __ SmiUntag(right);
+ __ shlq(left, right);
+ }
+ return;
+ }
+
+ const bool right_needs_check =
+ (right_range == NULL) || !right_range->IsWithin(0, (Smi::kBits - 1));
+ ASSERT(right == RCX); // Count must be in RCX
+ if (is_truncating) {
+ if (right_needs_check) {
+ const bool right_may_be_negative =
+ (right_range == NULL) ||
+ !right_range->IsWithin(0, RangeBoundary::kPlusInfinity);
+ if (right_may_be_negative) {
+ ASSERT(shift_left->CanDeoptimize());
+ __ cmpq(right, Immediate(0));
+ __ j(NEGATIVE, deopt);
+ }
+ Label done, is_not_zero;
+ __ cmpq(right,
+ Immediate(reinterpret_cast<int64_t>(Smi::New(Smi::kBits))));
+ __ j(BELOW, &is_not_zero, Assembler::kNearJump);
+ __ xorq(left, left);
+ __ jmp(&done, Assembler::kNearJump);
+ __ Bind(&is_not_zero);
+ __ SmiUntag(right);
+ __ shlq(left, right);
+ __ Bind(&done);
+ } else {
+ __ SmiUntag(right);
+ __ shlq(left, right);
+ }
+ } else {
+ if (right_needs_check) {
+ ASSERT(shift_left->CanDeoptimize());
+ __ cmpq(right,
+ Immediate(reinterpret_cast<int64_t>(Smi::New(Smi::kBits))));
+ __ j(ABOVE_EQUAL, deopt);
+ }
+ // Left is not a constant.
+ Register temp = locs.temp(0).reg();
+ // Check if count too large for handling it inlined.
+ __ movq(temp, left);
+ __ SmiUntag(right);
+ // Overflow test (preserve temp and right);
+ __ shlq(left, right);
+ __ sarq(left, right);
+ __ cmpq(left, temp);
+ __ j(NOT_EQUAL, deopt); // Overflow.
+ // Shift for result now we know there is no overflow.
+ __ shlq(left, right);
+ }
+}
+
+
static bool CanBeImmediate(const Object& constant) {
return constant.IsSmi() &&
Immediate(reinterpret_cast<int64_t>(constant.raw())).is_int32();
@@ -1869,12 +1994,14 @@
summary->set_out(Location::SameAsFirstInput());
return summary;
} else if (op_kind() == Token::kSHL) {
- const intptr_t kNumTemps = 1;
+ const intptr_t kNumTemps = 0;
LocationSummary* summary =
new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
summary->set_in(0, Location::RequiresRegister());
summary->set_in(1, Location::FixedRegisterOrSmiConstant(right(), RCX));
- summary->set_temp(0, Location::RequiresRegister());
+ if (!is_truncating()) {
+ summary->AddTemp(Location::RequiresRegister());
+ }
summary->set_out(Location::SameAsFirstInput());
return summary;
} else {
@@ -1889,6 +2016,12 @@
}
void BinarySmiOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ if (op_kind() == Token::kSHL) {
+ EmitSmiShiftLeft(compiler, this);
+ return;
+ }
+
+ ASSERT(!is_truncating());
Register left = locs()->in(0).reg();
Register result = locs()->out().reg();
ASSERT(left == result);
@@ -1990,27 +2123,6 @@
__ SmiTag(left);
break;
}
- case Token::kSHL: {
- // shlq operation masks the count to 6 bits.
- const intptr_t kCountLimit = 0x3F;
- intptr_t value = Smi::Cast(constant).Value();
- if (value == 0) break;
- if ((value < 0) || (value >= kCountLimit)) {
- // This condition may not be known earlier in some cases because
- // of constant propagation, inlining, etc.
- __ jmp(deopt);
- break;
- }
- Register temp = locs()->temp(0).reg();
- __ movq(temp, left);
- __ shlq(left, Immediate(value));
- __ sarq(left, Immediate(value));
- __ cmpq(left, temp);
- __ j(NOT_EQUAL, deopt); // Overflow.
- // Shift for result now we know there is no overflow.
- __ shlq(left, Immediate(value));
- break;
- }
default:
UNREACHABLE();
@@ -2094,57 +2206,6 @@
__ SmiTag(left);
break;
}
- case Token::kSHL: {
- Range* right_range = this->right()->definition()->range();
- if (this->left()->BindsToConstant()) {
- // If left is constant, we know the maximal allowed size for right.
- const Object& obj = this->left()->BoundConstant();
- if (obj.IsSmi()) {
- const intptr_t left_int = Smi::Cast(obj).Value();
- if (left_int == 0) {
- __ cmpq(right, Immediate(0));
- __ j(NEGATIVE, deopt);
- break;
- }
- intptr_t tmp = (left_int > 0) ? left_int : ~left_int;
- intptr_t max_right = kSmiBits;
- while ((tmp >>= 1) != 0) {
- max_right--;
- }
- const bool right_needs_check =
- (right_range == NULL) ||
- !right_range->IsWithin(0, max_right - 1);
- if (right_needs_check) {
- __ cmpq(right,
- Immediate(reinterpret_cast<int64_t>(Smi::New(max_right))));
- __ j(ABOVE_EQUAL, deopt);
- }
- __ SmiUntag(right);
- __ shlq(left, right);
- break;
- }
- }
- Register temp = locs()->temp(0).reg();
- // Check if count too large for handling it inlined.
- __ movq(temp, left);
- const bool right_needs_check =
- (right_range == NULL) || !right_range->IsWithin(0, (Smi::kBits - 1));
- if (right_needs_check) {
- __ cmpq(right,
- Immediate(reinterpret_cast<int64_t>(Smi::New(Smi::kBits))));
- __ j(ABOVE_EQUAL, deopt);
- }
- ASSERT(right == RCX); // Count must be in RCX
- __ SmiUntag(right);
- // Overflow test (preserve temp and right);
- __ shlq(left, right);
- __ sarq(left, right);
- __ cmpq(left, temp);
- __ j(NOT_EQUAL, deopt); // Overflow.
- // Shift for result now we know there is no overflow.
- __ shlq(left, right);
- break;
- }
case Token::kDIV: {
// Dispatches to 'Double./'.
// TODO(srdjan): Implement as conversion to double and double division.
@@ -2171,25 +2232,37 @@
LocationSummary* CheckEitherNonSmiInstr::MakeLocationSummary() const {
- ASSERT((left()->Type()->ToCid() != kDoubleCid) &&
- (right()->Type()->ToCid() != kDoubleCid));
+ intptr_t left_cid = left()->Type()->ToCid();
+ intptr_t right_cid = right()->Type()->ToCid();
+ ASSERT((left_cid != kDoubleCid) && (right_cid != kDoubleCid));
const intptr_t kNumInputs = 2;
- const intptr_t kNumTemps = 1;
+ const bool need_temp = (left_cid != kSmiCid) && (right_cid != kSmiCid);
+ const intptr_t kNumTemps = need_temp ? 1 : 0;
LocationSummary* summary =
new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
summary->set_in(0, Location::RequiresRegister());
summary->set_in(1, Location::RequiresRegister());
- summary->set_temp(0, Location::RequiresRegister());
+ if (need_temp) summary->set_temp(0, Location::RequiresRegister());
return summary;
}
void CheckEitherNonSmiInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
Label* deopt = compiler->AddDeoptStub(deopt_id(), kDeoptBinaryDoubleOp);
- Register temp = locs()->temp(0).reg();
- __ movq(temp, locs()->in(0).reg());
- __ orq(temp, locs()->in(1).reg());
- __ testl(temp, Immediate(kSmiTagMask));
+ intptr_t left_cid = left()->Type()->ToCid();
+ intptr_t right_cid = right()->Type()->ToCid();
+ Register left = locs()->in(0).reg();
+ Register right = locs()->in(1).reg();
+ if (left_cid == kSmiCid) {
+ __ testq(right, Immediate(kSmiTagMask));
+ } else if (right_cid == kSmiCid) {
+ __ testq(left, Immediate(kSmiTagMask));
+ } else {
+ Register temp = locs()->temp(0).reg();
+ __ movq(temp, left);
+ __ orq(temp, right);
+ __ testq(temp, Immediate(kSmiTagMask));
+ }
__ j(ZERO, deopt);
}
diff --git a/runtime/vm/intrinsifier.h b/runtime/vm/intrinsifier.h
index 5d4bbf4..379c6c1 100644
--- a/runtime/vm/intrinsifier.h
+++ b/runtime/vm/intrinsifier.h
@@ -64,7 +64,7 @@
V(_ObjectArray, get:length, Array_getLength, 405297088) \
V(_ObjectArray, [], Array_getIndexed, 71937385) \
V(_ObjectArray, []=, Array_setIndexed, 255863719) \
- V(_GrowableObjectArray, .withData, GArray_Allocate, 816132033) \
+ V(_GrowableObjectArray, .withData, GrowableArray_Allocate, 816132033) \
V(_GrowableObjectArray, get:length, GrowableArray_getLength, 725548050) \
V(_GrowableObjectArray, get:_capacity, GrowableArray_getCapacity, 725548050) \
V(_GrowableObjectArray, [], GrowableArray_getIndexed, 581838973) \
@@ -89,39 +89,19 @@
#define SCALARLIST_LIB_INTRINSIC_LIST(V) \
V(_ByteArrayBase, get:length, ByteArrayBase_getLength, 1098081765) \
- V(_Int8Array, [], Int8Array_getIndexed, 1295306322) \
- V(_Int8Array, []=, Int8Array_setIndexed, 1709956322) \
V(_Int8Array, _new, Int8Array_new, 535958453) \
- V(_Uint8Array, [], Uint8Array_getIndexed, 578331916) \
- V(_Uint8Array, []=, Uint8Array_setIndexed, 121509844) \
V(_Uint8Array, _new, Uint8Array_new, 604355565) \
- V(_Uint8ClampedArray, [], UintClamped8Array_getIndexed, 327062422) \
- V(_Uint8ClampedArray, []=, Uint8ClampedArray_setIndexed, 2054663547) \
V(_Uint8ClampedArray, _new, Uint8ClampedArray_new, 1070949952) \
- V(_Int16Array, [], Int16Array_getIndexed, 870098766) \
V(_Int16Array, _new, Int16Array_new, 903723993) \
- V(_Uint16Array, [], Uint16Array_getIndexed, 1019828411) \
- V(_Uint16Array, []=, Uint16Array_setIndexed, 1457955615) \
V(_Uint16Array, _new, Uint16Array_new, 133542762) \
- V(_Int32Array, [], Int32Array_getIndexed, 1999321436) \
V(_Int32Array, _new, Int32Array_new, 8218286) \
- V(_Uint32Array, [], Uint32Array_getIndexed, 1750764660) \
V(_Uint32Array, _new, Uint32Array_new, 469402161) \
V(_Int64Array, [], Int64Array_getIndexed, 504894128) \
V(_Int64Array, _new, Int64Array_new, 60605075) \
V(_Uint64Array, [], Uint64Array_getIndexed, 31272531) \
V(_Uint64Array, _new, Uint64Array_new, 624354107) \
- V(_Float32Array, [], Float32Array_getIndexed, 147582932) \
- V(_Float32Array, []=, Float32Array_setIndexed, 664454270) \
V(_Float32Array, _new, Float32Array_new, 109944959) \
- V(_Float64Array, [], Float64Array_getIndexed, 638830526) \
- V(_Float64Array, []=, Float64Array_setIndexed, 1948811847) \
V(_Float64Array, _new, Float64Array_new, 147668392) \
- V(_ExternalUint8Array, [], ExternalUint8Array_getIndexed, 753790851) \
- V(_ExternalUint8ClampedArray, [], ExternalUint8ClampedArray_getIndexed, \
- 823759763) \
- V(_ExternalUint8ClampedArray, []=, ExternalUint8ClampedArray_setIndexed, \
- 654373808) \
// TODO(srdjan): Implement _FixedSizeArrayIterator, get:current and
diff --git a/runtime/vm/intrinsifier_arm.cc b/runtime/vm/intrinsifier_arm.cc
index e606969..c2b7c2b 100644
--- a/runtime/vm/intrinsifier_arm.cc
+++ b/runtime/vm/intrinsifier_arm.cc
@@ -39,7 +39,7 @@
}
-bool Intrinsifier::GArray_Allocate(Assembler* assembler) {
+bool Intrinsifier::GrowableArray_Allocate(Assembler* assembler) {
return false;
}
@@ -84,91 +84,36 @@
}
-bool Intrinsifier::Int8Array_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
-bool Intrinsifier::Int8Array_setIndexed(Assembler* assembler) {
- return false;
-}
-
-
bool Intrinsifier::Int8Array_new(Assembler* assembler) {
return false;
}
-bool Intrinsifier::Uint8Array_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
-bool Intrinsifier::Uint8Array_setIndexed(Assembler* assembler) {
- return false;
-}
-
-
bool Intrinsifier::Uint8Array_new(Assembler* assembler) {
return false;
}
-bool Intrinsifier::UintClamped8Array_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
-bool Intrinsifier::Uint8ClampedArray_setIndexed(Assembler* assembler) {
- return false;
-}
-
-
bool Intrinsifier::Uint8ClampedArray_new(Assembler* assembler) {
return false;
}
-bool Intrinsifier::Int16Array_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
bool Intrinsifier::Int16Array_new(Assembler* assembler) {
return false;
}
-bool Intrinsifier::Uint16Array_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
-bool Intrinsifier::Uint16Array_setIndexed(Assembler* assembler) {
- return false;
-}
-
-
bool Intrinsifier::Uint16Array_new(Assembler* assembler) {
return false;
}
-bool Intrinsifier::Int32Array_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
bool Intrinsifier::Int32Array_new(Assembler* assembler) {
return false;
}
-bool Intrinsifier::Uint32Array_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
bool Intrinsifier::Uint32Array_new(Assembler* assembler) {
return false;
}
@@ -194,51 +139,16 @@
}
-bool Intrinsifier::Float32Array_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
-bool Intrinsifier::Float32Array_setIndexed(Assembler* assembler) {
- return false;
-}
-
-
bool Intrinsifier::Float32Array_new(Assembler* assembler) {
return false;
}
-bool Intrinsifier::Float64Array_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
-bool Intrinsifier::Float64Array_setIndexed(Assembler* assembler) {
- return false;
-}
-
-
bool Intrinsifier::Float64Array_new(Assembler* assembler) {
return false;
}
-bool Intrinsifier::ExternalUint8Array_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
-bool Intrinsifier::ExternalUint8ClampedArray_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
-bool Intrinsifier::ExternalUint8ClampedArray_setIndexed(Assembler* assembler) {
- return false;
-}
-
-
bool Intrinsifier::Integer_addFromInteger(Assembler* assembler) {
return false;
}
diff --git a/runtime/vm/intrinsifier_ia32.cc b/runtime/vm/intrinsifier_ia32.cc
index b939f78..7bd0c89 100644
--- a/runtime/vm/intrinsifier_ia32.cc
+++ b/runtime/vm/intrinsifier_ia32.cc
@@ -249,7 +249,7 @@
// Allocate a GrowableObjectArray using the backing array specified.
// On stack: type argument (+2), data (+1), return-address (+0).
-bool Intrinsifier::GArray_Allocate(Assembler* assembler) {
+bool Intrinsifier::GrowableArray_Allocate(Assembler* assembler) {
// This snippet of inlined code uses the following registers:
// EAX, EBX
// and the newly allocated object is returned in EAX.
@@ -468,81 +468,6 @@
}
-// Tests if index is a valid length (Smi and within valid index range),
-// jumps to fall_through if it is not.
-// Returns index in EBX, array in EAX.
-// This should be used only on getIndexed intrinsics.
-static void TestByteArrayGetIndex(Assembler* assembler, Label* fall_through) {
- __ movl(EAX, Address(ESP, + 2 * kWordSize)); // Array.
- __ movl(EBX, Address(ESP, + 1 * kWordSize)); // Index.
- __ testl(EBX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, fall_through, Assembler::kNearJump); // Non-smi index.
- // Range check.
- __ cmpl(EBX, FieldAddress(EAX, ByteArray::length_offset()));
- // Runtime throws exception.
- __ j(ABOVE_EQUAL, fall_through, Assembler::kNearJump);
-}
-
-
-// Tests if index is a valid length (Smi and within valid index range),
-// jumps to fall_through if it is not.
-// Returns index in EBX, array in EAX.
-// This should be used only for setIndexed intrinsics.
-static void TestByteArraySetIndex(Assembler* assembler, Label* fall_through) {
- __ movl(EAX, Address(ESP, + 3 * kWordSize)); // Array.
- __ movl(EBX, Address(ESP, + 2 * kWordSize)); // Index.
- __ testl(EBX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, fall_through, Assembler::kNearJump); // Non-smi index.
- // Range check.
- __ cmpl(EBX, FieldAddress(EAX, ByteArray::length_offset()));
- // Runtime throws exception.
- __ j(ABOVE_EQUAL, fall_through, Assembler::kNearJump);
-}
-
-
-bool Intrinsifier::Int8Array_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // EBX: index as Smi.
- // EAX: array.
- __ SmiUntag(EBX);
- __ movsxb(EAX, FieldAddress(EAX,
- EBX,
- TIMES_1,
- Int8Array::data_offset()));
- __ SmiTag(EAX);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
-bool Intrinsifier::Int8Array_setIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArraySetIndex(assembler, &fall_through);
- // EBX: index as Smi.
- // EAX: array.
- __ SmiUntag(EBX);
- // Free EBX for the value since we want a byte register.
- __ movl(EDI, EBX);
- __ movl(EBX, Address(ESP, + 1 * kWordSize)); // Value.
- __ testl(EBX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, &fall_through, Assembler::kNearJump);
- __ SmiUntag(EBX);
- // Check that the value is a byte. Add 128 to EBX to bring it into
- // the range 0..FF.
- __ addl(EBX, Immediate(128));
- __ cmpl(EBX, Immediate(0xFF));
- __ j(ABOVE, &fall_through, Assembler::kNearJump);
- // Undo addition.
- __ subl(EBX, Immediate(128));
- __ movb(FieldAddress(EAX, EDI, TIMES_1, Int8Array::data_offset()), BL);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
#define TYPED_ARRAY_ALLOCATION(type_name, scale_factor) \
Label fall_through; \
const intptr_t kArrayLengthStackOffset = 1 * kWordSize; \
@@ -635,210 +560,36 @@
}
-bool Intrinsifier::Uint8Array_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // EBX: index as Smi.
- // EAX: array.
- __ SmiUntag(EBX);
- __ movzxb(EAX, FieldAddress(EAX,
- EBX,
- TIMES_1,
- Uint8Array::data_offset()));
- __ SmiTag(EAX);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
-bool Intrinsifier::Uint8Array_setIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArraySetIndex(assembler, &fall_through);
- // EBX: index as Smi.
- // EAX: array.
- __ SmiUntag(EBX);
- // Free EBX for the value since we want a byte register.
- __ movl(EDI, EBX);
- __ movl(EBX, Address(ESP, + 1 * kWordSize)); // Value.
- __ testl(EBX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, &fall_through, Assembler::kNearJump);
- __ SmiUntag(EBX);
- // Check that the value is a byte.
- __ cmpl(EBX, Immediate(0xFF));
- __ j(ABOVE, &fall_through, Assembler::kNearJump);
- __ movb(FieldAddress(EAX, EDI, TIMES_1, Uint8Array::data_offset()), BL);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
bool Intrinsifier::Uint8Array_new(Assembler* assembler) {
TYPED_ARRAY_ALLOCATION(Uint8Array, TIMES_1);
return false;
}
-bool Intrinsifier::UintClamped8Array_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // EBX: index as Smi.
- // EAX: array.
- __ SmiUntag(EBX);
- __ movzxb(EAX, FieldAddress(EAX,
- EBX,
- TIMES_1,
- Uint8ClampedArray::data_offset()));
- __ SmiTag(EAX);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
-bool Intrinsifier::Uint8ClampedArray_setIndexed(Assembler* assembler) {
- Label fall_through, store_value, load_0xff;
- TestByteArraySetIndex(assembler, &fall_through);
- // EBX: index as Smi.
- // EAX: array.
- __ SmiUntag(EBX);
- // Free EBX for the value since we need a byte register.
- __ leal(EAX, FieldAddress(EAX, EBX, TIMES_1,
- Uint8ClampedArray::data_offset()));
- __ movl(EBX, Address(ESP, + 1 * kWordSize)); // Value.
- __ testl(EBX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, &fall_through, Assembler::kNearJump);
-
- __ SmiUntag(EBX);
- __ cmpl(EBX, Immediate(0xFF));
- __ j(BELOW_EQUAL, &store_value, Assembler::kNearJump);
- // Clamp to 0x00 or 0xFF respectively.
- __ j(GREATER, &load_0xff, Assembler::kNearJump);
- __ xorl(EBX, EBX); // Zero.
- __ jmp(&store_value, Assembler::kNearJump);
- __ Bind(&load_0xff);
- __ movl(EBX, Immediate(0xFF));
-
- __ Bind(&store_value);
- __ movb(Address(EAX, 0), BL);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
bool Intrinsifier::Uint8ClampedArray_new(Assembler* assembler) {
TYPED_ARRAY_ALLOCATION(Uint8ClampedArray, TIMES_1);
return false;
}
-bool Intrinsifier::Int16Array_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // EBX: index as Smi.
- // EAX: array.
- __ movsxw(EAX, FieldAddress(EAX,
- EBX,
- TIMES_1,
- Int16Array::data_offset()));
- __ SmiTag(EAX);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
bool Intrinsifier::Int16Array_new(Assembler* assembler) {
TYPED_ARRAY_ALLOCATION(Int16Array, TIMES_2);
return false;
}
-bool Intrinsifier::Uint16Array_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // EBX: index as Smi.
- // EAX: array.
- __ movzxw(EAX, FieldAddress(EAX,
- EBX,
- TIMES_1,
- Uint16Array::data_offset()));
- __ SmiTag(EAX);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
-bool Intrinsifier::Uint16Array_setIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArraySetIndex(assembler, &fall_through);
- // EBX: index as Smi.
- // EAX: array.
- __ movl(EDI, Address(ESP, + 1 * kWordSize));
- __ SmiUntag(EDI);
- // EDI: undtagged value.
- __ testl(EDI, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, &fall_through, Assembler::kNearJump);
- __ movw(FieldAddress(EAX, EBX, TIMES_1, Uint16Array::data_offset()), EDI);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
bool Intrinsifier::Uint16Array_new(Assembler* assembler) {
TYPED_ARRAY_ALLOCATION(Uint16Array, TIMES_2);
return false;
}
-bool Intrinsifier::Int32Array_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // EBX: index as Smi.
- // EAX: array.
- __ movl(EAX, FieldAddress(EAX,
- EBX,
- TIMES_2,
- Int32Array::data_offset()));
- // Verify that the signed value in EAX can fit inside a Smi.
- __ cmpl(EAX, Immediate(0xC0000000));
- __ j(NEGATIVE, &fall_through, Assembler::kNearJump); // Won't fit Smi.
- __ SmiTag(EAX);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
bool Intrinsifier::Int32Array_new(Assembler* assembler) {
TYPED_ARRAY_ALLOCATION(Int32Array, TIMES_4);
return false;
}
-bool Intrinsifier::Uint32Array_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // EBX: index as Smi.
- // EAX: array.
- __ movl(EAX, FieldAddress(EAX,
- EBX,
- TIMES_2,
- Uint32Array::data_offset()));
- // Verify that the unsigned value in EAX can be stored in a Smi.
- __ testl(EAX, Immediate(0xC0000000));
- __ j(NOT_ZERO, &fall_through, Assembler::kNearJump); // Won't fit Smi.
- __ SmiTag(EAX);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
bool Intrinsifier::Uint32Array_new(Assembler* assembler) {
TYPED_ARRAY_ALLOCATION(Uint32Array, TIMES_4);
return false;
@@ -867,172 +618,18 @@
}
-bool Intrinsifier::Float32Array_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // EBX: index as Smi.
- // EAX: array.
- // Load single precision float into XMM7.
- __ movss(XMM7, FieldAddress(EAX, EBX, TIMES_2,
- Float32Array::data_offset()));
- // Convert into a double precision float.
- __ cvtss2sd(XMM7, XMM7);
- // Allocate a double instance.
- const Class& double_class = Class::Handle(
- Isolate::Current()->object_store()->double_class());
- AssemblerMacros::TryAllocate(assembler,
- double_class,
- &fall_through,
- Assembler::kNearJump, EAX);
- // Store XMM7 into double instance.
- __ movsd(FieldAddress(EAX, Double::value_offset()), XMM7);
- __ ret();
- __ Bind(&fall_through);
-
- return false;
-}
-
-
-bool Intrinsifier::Float32Array_setIndexed(Assembler* assembler) {
- Label fall_through;
- __ movl(EAX, Address(ESP, + 1 * kWordSize)); // Value.
- // If EAX is not an instance of double, jump to fall through.
- __ testl(EAX, Immediate(kSmiTagMask));
- __ j(ZERO, &fall_through);
- __ CompareClassId(EAX, kDoubleCid, EDI);
- __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump);
- // Load double value into XMM7.
- __ movsd(XMM7, FieldAddress(EAX, Double::value_offset()));
- TestByteArraySetIndex(assembler, &fall_through);
- // EBX: index as Smi.
- // EAX: array.
- // Convert from double precision float to single precision float.
- __ cvtsd2ss(XMM7, XMM7);
- // Store into array.
- __ movss(FieldAddress(EAX, EBX, TIMES_2, Float32Array::data_offset()), XMM7);
- // End fast path.
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
bool Intrinsifier::Float32Array_new(Assembler* assembler) {
TYPED_ARRAY_ALLOCATION(Float32Array, TIMES_4);
return false;
}
-bool Intrinsifier::Float64Array_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // EBX: index as Smi.
- // EAX: array.
- // Load double precision float into XMM7.
- __ movsd(XMM7, FieldAddress(EAX, EBX, TIMES_4,
- Float64Array::data_offset()));
- // Allocate a double instance.
- const Class& double_class = Class::Handle(
- Isolate::Current()->object_store()->double_class());
- AssemblerMacros::TryAllocate(assembler,
- double_class,
- &fall_through,
- Assembler::kNearJump, EAX);
- // Store XMM7 into double instance.
- __ movsd(FieldAddress(EAX, Double::value_offset()), XMM7);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
-bool Intrinsifier::Float64Array_setIndexed(Assembler* assembler) {
- Label fall_through;
- __ movl(EAX, Address(ESP, + 1 * kWordSize)); // Value.
- // If EAX is not an instance of double, jump to fall through.
- __ testl(EAX, Immediate(kSmiTagMask));
- __ j(ZERO, &fall_through, Assembler::kNearJump);
- __ CompareClassId(EAX, kDoubleCid, EDI);
- __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump);
- // Load double value into XMM7.
- __ movsd(XMM7, FieldAddress(EAX, Double::value_offset()));
- TestByteArraySetIndex(assembler, &fall_through);
- // EBX: index as Smi.
- // EAX: array.
- __ movsd(FieldAddress(EAX, EBX, TIMES_4, Float64Array::data_offset()), XMM7);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
bool Intrinsifier::Float64Array_new(Assembler* assembler) {
TYPED_ARRAY_ALLOCATION(Float64Array, TIMES_8);
return false;
}
-bool Intrinsifier::ExternalUint8Array_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // EBX: index as Smi.
- // EAX: array.
- __ SmiUntag(EBX);
- __ movl(EAX, FieldAddress(EAX, ExternalUint8Array::data_offset()));
- __ movzxb(EAX, Address(EAX, EBX, TIMES_1, 0));
- __ SmiTag(EAX);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
-bool Intrinsifier::ExternalUint8ClampedArray_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // EBX: index as Smi.
- // EAX: array.
- __ SmiUntag(EBX);
- __ movl(EAX, FieldAddress(EAX, ExternalUint8ClampedArray::data_offset()));
- __ movzxb(EAX, Address(EAX, EBX, TIMES_1, 0));
- __ SmiTag(EAX);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
-bool Intrinsifier::ExternalUint8ClampedArray_setIndexed(Assembler* assembler) {
- Label fall_through, store_value, load_0xff;
- TestByteArraySetIndex(assembler, &fall_through);
- // EBX: index as Smi.
- // EAX: array.
- __ SmiUntag(EBX);
- __ movl(EAX, FieldAddress(EAX, ExternalUint8ClampedArray::data_offset()));
- // Free EBX for the value since we need a byte register.
- __ leal(EAX, Address(EAX, EBX, TIMES_1, 0));
- __ movl(EBX, Address(ESP, + 1 * kWordSize)); // Value.
- __ testl(EBX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, &fall_through, Assembler::kNearJump);
-
- __ SmiUntag(EBX);
- __ cmpl(EBX, Immediate(0xFF));
- __ j(BELOW_EQUAL, &store_value, Assembler::kNearJump);
- // Clamp to 0x00 or 0xFF respectively.
- __ j(GREATER, &load_0xff, Assembler::kNearJump);
- __ xorl(EBX, EBX); // Zero.
- __ jmp(&store_value, Assembler::kNearJump);
- __ Bind(&load_0xff);
- __ movl(EBX, Immediate(0xFF));
-
- __ Bind(&store_value);
- __ movb(Address(EAX, 0), BL);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
// Tests if two top most arguments are smis, jumps to label not_smi if not.
// Topmost argument is in EAX.
static void TestBothArgumentsSmis(Assembler* assembler, Label* not_smi) {
diff --git a/runtime/vm/intrinsifier_mips.cc b/runtime/vm/intrinsifier_mips.cc
index 5c07f27..70540a6 100644
--- a/runtime/vm/intrinsifier_mips.cc
+++ b/runtime/vm/intrinsifier_mips.cc
@@ -39,7 +39,7 @@
}
-bool Intrinsifier::GArray_Allocate(Assembler* assembler) {
+bool Intrinsifier::GrowableArray_Allocate(Assembler* assembler) {
return false;
}
@@ -84,91 +84,36 @@
}
-bool Intrinsifier::Int8Array_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
-bool Intrinsifier::Int8Array_setIndexed(Assembler* assembler) {
- return false;
-}
-
-
bool Intrinsifier::Int8Array_new(Assembler* assembler) {
return false;
}
-bool Intrinsifier::Uint8Array_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
-bool Intrinsifier::Uint8Array_setIndexed(Assembler* assembler) {
- return false;
-}
-
-
bool Intrinsifier::Uint8Array_new(Assembler* assembler) {
return false;
}
-bool Intrinsifier::UintClamped8Array_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
-bool Intrinsifier::Uint8ClampedArray_setIndexed(Assembler* assembler) {
- return false;
-}
-
-
bool Intrinsifier::Uint8ClampedArray_new(Assembler* assembler) {
return false;
}
-bool Intrinsifier::Int16Array_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
bool Intrinsifier::Int16Array_new(Assembler* assembler) {
return false;
}
-bool Intrinsifier::Uint16Array_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
-bool Intrinsifier::Uint16Array_setIndexed(Assembler* assembler) {
- return false;
-}
-
-
bool Intrinsifier::Uint16Array_new(Assembler* assembler) {
return false;
}
-bool Intrinsifier::Int32Array_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
bool Intrinsifier::Int32Array_new(Assembler* assembler) {
return false;
}
-bool Intrinsifier::Uint32Array_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
bool Intrinsifier::Uint32Array_new(Assembler* assembler) {
return false;
}
@@ -194,51 +139,16 @@
}
-bool Intrinsifier::Float32Array_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
-bool Intrinsifier::Float32Array_setIndexed(Assembler* assembler) {
- return false;
-}
-
-
bool Intrinsifier::Float32Array_new(Assembler* assembler) {
return false;
}
-bool Intrinsifier::Float64Array_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
-bool Intrinsifier::Float64Array_setIndexed(Assembler* assembler) {
- return false;
-}
-
-
bool Intrinsifier::Float64Array_new(Assembler* assembler) {
return false;
}
-bool Intrinsifier::ExternalUint8Array_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
-bool Intrinsifier::ExternalUint8ClampedArray_getIndexed(Assembler* assembler) {
- return false;
-}
-
-
-bool Intrinsifier::ExternalUint8ClampedArray_setIndexed(Assembler* assembler) {
- return false;
-}
-
-
bool Intrinsifier::Integer_addFromInteger(Assembler* assembler) {
return false;
}
diff --git a/runtime/vm/intrinsifier_x64.cc b/runtime/vm/intrinsifier_x64.cc
index 8d75a2e..694c384 100644
--- a/runtime/vm/intrinsifier_x64.cc
+++ b/runtime/vm/intrinsifier_x64.cc
@@ -202,7 +202,7 @@
// Allocate a GrowableObjectArray using the backing array specified.
// On stack: type argument (+2), data (+1), return-address (+0).
-bool Intrinsifier::GArray_Allocate(Assembler* assembler) {
+bool Intrinsifier::GrowableArray_Allocate(Assembler* assembler) {
// This snippet of inlined code uses the following registers:
// RAX, RCX, R13
// and the newly allocated object is returned in RAX.
@@ -440,63 +440,6 @@
}
-// Tests if index is a valid length (Smi and within valid index range),
-// jumps to fall_through if it is not.
-// Returns index in R12, array in RAX.
-// This should be used only for setIndexed intrinsics.
-static void TestByteArraySetIndex(Assembler* assembler, Label* fall_through) {
- __ movq(RAX, Address(RSP, + 3 * kWordSize)); // Array.
- __ movq(R12, Address(RSP, + 2 * kWordSize)); // Index.
- __ testq(R12, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, fall_through, Assembler::kNearJump); // Non-smi index.
- // Range check.
- __ cmpq(R12, FieldAddress(RAX, ByteArray::length_offset()));
- // Runtime throws exception.
- __ j(ABOVE_EQUAL, fall_through, Assembler::kNearJump);
-}
-
-
-bool Intrinsifier::Int8Array_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // R12: index as Smi.
- // RAX: array.
- __ SmiUntag(R12);
- __ movsxb(RAX, FieldAddress(RAX,
- R12,
- TIMES_1,
- Int8Array::data_offset()));
- __ SmiTag(RAX);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
-bool Intrinsifier::Int8Array_setIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArraySetIndex(assembler, &fall_through);
- // R12: index as Smi.
- // RAX: array.
- __ SmiUntag(R12);
- __ movq(RDI, Address(RSP, + 1 * kWordSize)); // Value.
- __ testq(RDI, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, &fall_through, Assembler::kNearJump);
- __ SmiUntag(RDI);
- // Check that the value is a byte. Add 128 to the value to bring it into
- // the range 0..FF.
- __ addq(RDI, Immediate(128));
- __ cmpq(RDI, Immediate(0xFF));
- __ j(ABOVE, &fall_through, Assembler::kNearJump);
- // Undo addition.
- __ subq(RDI, Immediate(128));
- __ movb(FieldAddress(RAX, R12, TIMES_1, Uint8Array::data_offset()), RDI);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
#define TYPED_ARRAY_ALLOCATION(type_name, scale_factor) \
Label fall_through; \
const intptr_t kArrayLengthStackOffset = 1 * kWordSize; \
@@ -594,199 +537,36 @@
}
-bool Intrinsifier::Uint8Array_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // R12: index as Smi.
- // RAX: array.
- __ SmiUntag(R12);
- __ movzxb(RAX, FieldAddress(RAX,
- R12,
- TIMES_1,
- Uint8Array::data_offset()));
- __ SmiTag(RAX);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
-bool Intrinsifier::Uint8Array_setIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArraySetIndex(assembler, &fall_through);
- // R12: index as Smi.
- // RAX: array.
- __ SmiUntag(R12);
- __ movq(RDI, Address(RSP, + 1 * kWordSize)); // Value.
- __ testq(RDI, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, &fall_through, Assembler::kNearJump);
- __ SmiUntag(RDI);
- // Check that value is a byte.
- __ cmpq(RDI, Immediate(0xFF));
- __ j(ABOVE, &fall_through, Assembler::kNearJump);
- __ movb(FieldAddress(RAX, R12, TIMES_1, Uint8Array::data_offset()), RDI);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
bool Intrinsifier::Uint8Array_new(Assembler* assembler) {
TYPED_ARRAY_ALLOCATION(Uint8Array, TIMES_1);
return false;
}
-bool Intrinsifier::UintClamped8Array_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // R12: index as Smi.
- // RAX: array.
- __ SmiUntag(R12);
- __ movzxb(RAX, FieldAddress(RAX,
- R12,
- TIMES_1,
- Uint8ClampedArray::data_offset()));
- __ SmiTag(RAX);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
-bool Intrinsifier::Uint8ClampedArray_setIndexed(Assembler* assembler) {
- Label fall_through, store_value, load_0xff;
- TestByteArraySetIndex(assembler, &fall_through);
- // R12: index as Smi.
- // RAX: array.
- __ SmiUntag(R12);
- __ movq(RDI, Address(RSP, + 1 * kWordSize)); // Value.
- __ testq(RDI, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, &fall_through, Assembler::kNearJump);
-
- __ SmiUntag(RDI);
- __ cmpq(RDI, Immediate(0xFF));
- __ j(BELOW_EQUAL, &store_value, Assembler::kNearJump);
- __ j(GREATER, &load_0xff, Assembler::kNearJump);
- __ xorq(RDI, RDI); // Zero.
- __ jmp(&store_value, Assembler::kNearJump);
- __ Bind(&load_0xff);
- __ movq(RDI, Immediate(0xFF));
-
- __ Bind(&store_value);
- __ movb(
- FieldAddress(RAX, R12, TIMES_1, Uint8ClampedArray::data_offset()), RDI);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
bool Intrinsifier::Uint8ClampedArray_new(Assembler* assembler) {
TYPED_ARRAY_ALLOCATION(Uint8ClampedArray, TIMES_1);
return false;
}
-bool Intrinsifier::Int16Array_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // R12: index as Smi.
- // RAX: array.
- __ movsxw(RAX, FieldAddress(RAX,
- R12,
- TIMES_1,
- Int16Array::data_offset()));
- __ SmiTag(RAX);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
bool Intrinsifier::Int16Array_new(Assembler* assembler) {
TYPED_ARRAY_ALLOCATION(Int16Array, TIMES_2);
return false;
}
-bool Intrinsifier::Uint16Array_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // R12: index as Smi.
- // RAX: array.
- __ movzxw(RAX, FieldAddress(RAX,
- R12,
- TIMES_1,
- Uint16Array::data_offset()));
- __ SmiTag(RAX);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
-bool Intrinsifier::Uint16Array_setIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArraySetIndex(assembler, &fall_through);
- // R12: index as Smi.
- // RAX: array.
- __ movq(RDI, Address(RSP, + 1 * kWordSize));
- __ SmiUntag(RDI);
- // RDI: untagged value.
- __ testl(RDI, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, &fall_through, Assembler::kNearJump);
- __ movw(FieldAddress(RAX, R12, TIMES_1, Uint16Array::data_offset()), RDI);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
bool Intrinsifier::Uint16Array_new(Assembler* assembler) {
TYPED_ARRAY_ALLOCATION(Uint16Array, TIMES_2);
return false;
}
-bool Intrinsifier::Int32Array_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // R12: index as Smi.
- // RAX: array.
- __ movsxl(RAX, FieldAddress(RAX,
- R12,
- TIMES_2,
- Int32Array::data_offset()));
- __ SmiTag(RAX);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
bool Intrinsifier::Int32Array_new(Assembler* assembler) {
TYPED_ARRAY_ALLOCATION(Int32Array, TIMES_4);
return false;
}
-bool Intrinsifier::Uint32Array_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // R12: index as Smi.
- // RAX: array.
- __ movl(RAX, FieldAddress(RAX,
- R12,
- TIMES_2,
- Uint32Array::data_offset()));
- __ SmiTag(RAX);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
bool Intrinsifier::Uint32Array_new(Assembler* assembler) {
TYPED_ARRAY_ALLOCATION(Uint32Array, TIMES_4);
return false;
@@ -850,169 +630,18 @@
}
-bool Intrinsifier::Float32Array_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // R12: index as Smi.
- // RAX: array.
- // Load single precision float into XMM7.
- __ movss(XMM7, FieldAddress(RAX, R12, TIMES_2,
- Float32Array::data_offset()));
- // Convert into a double precision float.
- __ cvtss2sd(XMM7, XMM7);
- // Allocate a double instance.
- const Class& double_class = Class::Handle(
- Isolate::Current()->object_store()->double_class());
- AssemblerMacros::TryAllocate(assembler,
- double_class,
- &fall_through,
- Assembler::kNearJump, RAX);
- // Store XMM7 into double instance.
- __ movsd(FieldAddress(RAX, Double::value_offset()), XMM7);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
-bool Intrinsifier::Float32Array_setIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArraySetIndex(assembler, &fall_through);
- // R12: index as Smi.
- // RAX: array.
- __ movq(RDX, Address(RSP, + 1 * kWordSize)); // Value.
- // If RDX is not an instance of double, jump to fall through.
- __ testq(RDX, Immediate(kSmiTagMask));
- __ j(ZERO, &fall_through, Assembler::kNearJump);
- __ CompareClassId(RDX, kDoubleCid);
- __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump);
- // Load double value into XMM7.
- __ movsd(XMM7, FieldAddress(RDX, Double::value_offset()));
- // Convert from double precision float to single precision float.
- __ cvtsd2ss(XMM7, XMM7);
- // Store into array.
- __ movss(FieldAddress(RAX, R12, TIMES_2, Float32Array::data_offset()), XMM7);
- // End fast path.
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
bool Intrinsifier::Float32Array_new(Assembler* assembler) {
TYPED_ARRAY_ALLOCATION(Float32Array, TIMES_4);
return false;
}
-bool Intrinsifier::Float64Array_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // R12: index as Smi.
- // RAX: array.
- // Load double precision float into XMM7.
- __ movsd(XMM7, FieldAddress(RAX, R12, TIMES_4,
- Float64Array::data_offset()));
- // Allocate a double instance.
- const Class& double_class = Class::Handle(
- Isolate::Current()->object_store()->double_class());
- AssemblerMacros::TryAllocate(assembler,
- double_class,
- &fall_through,
- Assembler::kNearJump, RAX);
- // Store XMM7 into double instance.
- __ movsd(FieldAddress(RAX, Double::value_offset()), XMM7);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
-bool Intrinsifier::Float64Array_setIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArraySetIndex(assembler, &fall_through);
- // R12: index as Smi.
- // RAX: array.
- __ movq(RDX, Address(RSP, + 1 * kWordSize)); // Value.
- // If RDX is not an instance of double, jump to fall through.
- __ testq(RDX, Immediate(kSmiTagMask));
- __ j(ZERO, &fall_through, Assembler::kNearJump);
- __ CompareClassId(RDX, kDoubleCid);
- __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump);
- // Load double value into XMM7.
- __ movsd(XMM7, FieldAddress(RDX, Double::value_offset()));
- // Store into array.
- __ movsd(FieldAddress(RAX, R12, TIMES_4, Float64Array::data_offset()), XMM7);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
bool Intrinsifier::Float64Array_new(Assembler* assembler) {
TYPED_ARRAY_ALLOCATION(Float64Array, TIMES_8);
return false;
}
-bool Intrinsifier::ExternalUint8Array_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // R12: index as Smi.
- // RAX: array.
- __ SmiUntag(R12);
- __ movq(RAX, FieldAddress(RAX, ExternalUint8Array::data_offset()));
- __ movzxb(RAX, Address(RAX, R12, TIMES_1, 0));
- __ SmiTag(RAX);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
-bool Intrinsifier::ExternalUint8ClampedArray_getIndexed(Assembler* assembler) {
- Label fall_through;
- TestByteArrayGetIndex(assembler, &fall_through);
- // R12: index as Smi.
- // RAX: array.
- __ SmiUntag(R12);
- __ movq(RAX, FieldAddress(RAX, ExternalUint8ClampedArray::data_offset()));
- __ movzxb(RAX, Address(RAX, R12, TIMES_1, 0));
- __ SmiTag(RAX);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
-bool Intrinsifier::ExternalUint8ClampedArray_setIndexed(Assembler* assembler) {
- Label fall_through, store_value, load_0xff;
- TestByteArraySetIndex(assembler, &fall_through);
- // R12: index as Smi.
- // RAX: array.
- __ SmiUntag(R12);
- __ movq(RDI, Address(RSP, + 1 * kWordSize)); // Value.
- __ testq(RDI, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, &fall_through, Assembler::kNearJump);
-
- __ SmiUntag(RDI);
- __ cmpq(RDI, Immediate(0xFF));
- __ j(BELOW_EQUAL, &store_value, Assembler::kNearJump);
- __ j(GREATER, &load_0xff, Assembler::kNearJump);
- __ xorq(RDI, RDI); // Zero.
- __ jmp(&store_value, Assembler::kNearJump);
- __ Bind(&load_0xff);
- __ movq(RDI, Immediate(0xFF));
-
- __ Bind(&store_value);
- __ movq(RAX, FieldAddress(RAX, ExternalUint8ClampedArray::data_offset()));
- __ movb(Address(RAX, R12, TIMES_1, 0), RDI);
- __ ret();
- __ Bind(&fall_through);
- return false;
-}
-
-
// Tests if two top most arguments are smis, jumps to label not_smi if not.
// Topmost argument is in RAX.
static void TestBothArgumentsSmis(Assembler* assembler, Label* not_smi) {
diff --git a/runtime/vm/native_entry_test.cc b/runtime/vm/native_entry_test.cc
index 3ed8980..217c080 100644
--- a/runtime/vm/native_entry_test.cc
+++ b/runtime/vm/native_entry_test.cc
@@ -94,9 +94,9 @@
DartFrameIterator iterator;
iterator.NextFrame(); // Skip native call.
StackFrame* static_caller_frame = iterator.NextFrame();
- uword target_address =
- CodePatcher::GetStaticCallTargetAt(static_caller_frame->pc());
const Code& code = Code::Handle(static_caller_frame->LookupDartCode());
+ uword target_address =
+ CodePatcher::GetStaticCallTargetAt(static_caller_frame->pc(), code);
const Function& target_function =
Function::Handle(code.GetStaticCallTargetFunctionAt(
static_caller_frame->pc()));
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 5ed104a..b2dba5a 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -631,6 +631,16 @@
lib.AddClass(cls);
}
+#define INIT_LIBRARY(name, raw_script, raw_lib) \
+ script ^= raw_script; \
+ Library::Init##name##Library(isolate); \
+ lib ^= raw_lib; \
+ ASSERT(!lib.IsNull()); \
+ error = Bootstrap::Compile(lib, script); \
+ if (!error.IsNull()) { \
+ return error.raw(); \
+ } \
+
RawError* Object::Init(Isolate* isolate) {
TIMERSCOPE(time_bootstrap);
@@ -761,7 +771,7 @@
pending_classes.Add(cls, Heap::kOld);
// Initialize the base interfaces used by the core VM classes.
- const Script& script = Script::Handle(Bootstrap::LoadCoreScript(false));
+ Script& script = Script::Handle(Bootstrap::LoadCoreScript(false));
// Allocate and initialize the pre-allocated classes in the core library.
cls = Class::New<Instance>(kInstanceCid);
@@ -1084,6 +1094,19 @@
if (!error.IsNull()) {
return error.raw();
}
+ Library& lib = Library::Handle();
+ INIT_LIBRARY(Crypto,
+ Bootstrap::LoadCryptoScript(false),
+ Library::CryptoLibrary());
+ INIT_LIBRARY(Json,
+ Bootstrap::LoadJsonScript(false),
+ Library::JsonLibrary());
+ INIT_LIBRARY(Utf,
+ Bootstrap::LoadUtfScript(false),
+ Library::UtfLibrary());
+ INIT_LIBRARY(Uri,
+ Bootstrap::LoadUriScript(false),
+ Library::UriLibrary());
Bootstrap::SetupNativeResolver();
// Remove the Object superclass cycle by setting the super type to null (not
@@ -2355,6 +2378,16 @@
}
+RawFunction* Class::LookupConstructorAllowPrivate(const String& name) const {
+ Function& function = Function::Handle(LookupFunctionAllowPrivate(name));
+ if (function.IsNull() || !function.IsConstructor()) {
+ return Function::null();
+ }
+ ASSERT(!function.is_static());
+ return function.raw();
+}
+
+
RawFunction* Class::LookupFactory(const String& name) const {
Function& function = Function::Handle(LookupFunction(name));
if (function.IsNull() || !function.IsFactory()) {
@@ -6373,11 +6406,15 @@
}
-void Library::InitMathLibrary(Isolate* isolate) {
- const String& url = Symbols::DartMath();
+void Library::InitCryptoLibrary(Isolate* isolate) {
+ const String& url = Symbols::DartCrypto();
const Library& lib = Library::Handle(Library::NewLibraryHelper(url, true));
lib.Register();
- isolate->object_store()->set_math_library(lib);
+ const Library& math_lib = Library::Handle(Library::MathLibrary());
+ const Namespace& math_ns = Namespace::Handle(
+ Namespace::New(math_lib, Array::Handle(), Array::Handle()));
+ lib.AddImport(math_ns);
+ isolate->object_store()->set_crypto_library(lib);
}
@@ -6393,6 +6430,22 @@
}
+void Library::InitJsonLibrary(Isolate* isolate) {
+ const String& url = Symbols::DartJson();
+ const Library& lib = Library::Handle(Library::NewLibraryHelper(url, true));
+ lib.Register();
+ isolate->object_store()->set_json_library(lib);
+}
+
+
+void Library::InitMathLibrary(Isolate* isolate) {
+ const String& url = Symbols::DartMath();
+ const Library& lib = Library::Handle(Library::NewLibraryHelper(url, true));
+ lib.Register();
+ isolate->object_store()->set_math_library(lib);
+}
+
+
void Library::InitMirrorsLibrary(Isolate* isolate) {
const String& url = Symbols::DartMirrors();
const Library& lib = Library::Handle(Library::NewLibraryHelper(url, true));
@@ -6414,19 +6467,6 @@
}
-void Library::InitScalarlistLibrary(Isolate* isolate) {
- const String& url = Symbols::DartScalarlist();
- const Library& lib = Library::Handle(Library::NewLibraryHelper(url, true));
- lib.Register();
- const Library& collection_lib =
- Library::Handle(Library::CollectionLibrary());
- const Namespace& collection_ns = Namespace::Handle(
- Namespace::New(collection_lib, Array::Handle(), Array::Handle()));
- lib.AddImport(collection_ns);
- isolate->object_store()->set_scalarlist_library(lib);
-}
-
-
void Library::InitNativeWrappersLibrary(Isolate* isolate) {
static const int kNumNativeWrappersClasses = 4;
ASSERT(kNumNativeWrappersClasses > 0 && kNumNativeWrappersClasses < 10);
@@ -6452,6 +6492,47 @@
}
+void Library::InitScalarlistLibrary(Isolate* isolate) {
+ const String& url = Symbols::DartScalarlist();
+ const Library& lib = Library::Handle(Library::NewLibraryHelper(url, true));
+ lib.Register();
+ const Library& collection_lib =
+ Library::Handle(Library::CollectionLibrary());
+ const Namespace& collection_ns = Namespace::Handle(
+ Namespace::New(collection_lib, Array::Handle(), Array::Handle()));
+ lib.AddImport(collection_ns);
+ isolate->object_store()->set_scalarlist_library(lib);
+}
+
+
+void Library::InitUriLibrary(Isolate* isolate) {
+ const String& url = Symbols::DartUri();
+ const Library& lib = Library::Handle(Library::NewLibraryHelper(url, true));
+ lib.Register();
+ const Library& math_lib = Library::Handle(Library::MathLibrary());
+ const Namespace& math_ns = Namespace::Handle(
+ Namespace::New(math_lib, Array::Handle(), Array::Handle()));
+ const Library& utf_lib = Library::Handle(Library::UtfLibrary());
+ const Namespace& utf_ns = Namespace::Handle(
+ Namespace::New(utf_lib, Array::Handle(), Array::Handle()));
+ lib.AddImport(math_ns);
+ lib.AddImport(utf_ns);
+ isolate->object_store()->set_uri_library(lib);
+}
+
+
+void Library::InitUtfLibrary(Isolate* isolate) {
+ const String& url = Symbols::DartUtf();
+ const Library& lib = Library::Handle(Library::NewLibraryHelper(url, true));
+ lib.Register();
+ const Library& async_lib = Library::Handle(Library::ASyncLibrary());
+ const Namespace& async_ns = Namespace::Handle(
+ Namespace::New(async_lib, Array::Handle(), Array::Handle()));
+ lib.AddImport(async_ns);
+ isolate->object_store()->set_utf_library(lib);
+}
+
+
RawLibrary* Library::LookupLibrary(const String &url) {
Isolate* isolate = Isolate::Current();
Library& lib = Library::Handle(isolate, Library::null());
@@ -6565,8 +6646,8 @@
}
-RawLibrary* Library::MathLibrary() {
- return Isolate::Current()->object_store()->math_library();
+RawLibrary* Library::CryptoLibrary() {
+ return Isolate::Current()->object_store()->crypto_library();
}
@@ -6575,18 +6656,38 @@
}
+RawLibrary* Library::JsonLibrary() {
+ return Isolate::Current()->object_store()->json_library();
+}
+
+
+RawLibrary* Library::MathLibrary() {
+ return Isolate::Current()->object_store()->math_library();
+}
+
+
RawLibrary* Library::MirrorsLibrary() {
return Isolate::Current()->object_store()->mirrors_library();
}
+RawLibrary* Library::NativeWrappersLibrary() {
+ return Isolate::Current()->object_store()->native_wrappers_library();
+}
+
+
RawLibrary* Library::ScalarlistLibrary() {
return Isolate::Current()->object_store()->scalarlist_library();
}
-RawLibrary* Library::NativeWrappersLibrary() {
- return Isolate::Current()->object_store()->native_wrappers_library();
+RawLibrary* Library::UriLibrary() {
+ return Isolate::Current()->object_store()->uri_library();
+}
+
+
+RawLibrary* Library::UtfLibrary() {
+ return Isolate::Current()->object_store()->utf_library();
}
@@ -7648,9 +7749,12 @@
region.Store<RawObject*>(offset_in_instrs, object->raw());
}
- // Hook up Code and Instruction objects.
+ // Hook up Code and Instructions objects.
instrs.set_code(code.raw());
code.set_instructions(instrs.raw());
+
+ // Set object pool in Instructions object.
+ instrs.set_object_pool(Array::MakeArray(assembler->object_pool()));
}
return code.raw();
}
@@ -7708,7 +7812,7 @@
if ((descriptors.DeoptId(i) == deopt_id) &&
(descriptors.DescriptorKind(i) == kind)) {
uword pc = descriptors.PC(i);
- ASSERT((EntryPoint() <= pc) && (pc < (EntryPoint() + Size())));
+ ASSERT(ContainsInstructionAt(pc));
return pc;
}
}
@@ -7777,7 +7881,8 @@
max_id = deopt_id;
}
node_ids->Add(deopt_id);
- CodePatcher::GetInstanceCallAt(descriptors.PC(i), &ic_data_obj, NULL);
+ CodePatcher::GetInstanceCallAt(descriptors.PC(i), *this,
+ &ic_data_obj, NULL);
ic_data_objs.Add(ic_data_obj);
}
}
@@ -7813,7 +7918,7 @@
if (descriptors.DescriptorKind(i) == PcDescriptors::kFuncCall) {
// Static call.
const uword target_addr =
- CodePatcher::GetStaticCallTargetAt(descriptors.PC(i));
+ CodePatcher::GetStaticCallTargetAt(descriptors.PC(i), *this);
if (target_addr == StubCode::CallStaticFunctionEntryPoint()) {
deopt_ids->Add(descriptors.DeoptId(i));
}
@@ -8116,7 +8221,6 @@
#endif // DEBUG
ASSERT(num_args_tested() == 1); // Otherwise use 'AddCheck'.
ASSERT(receiver_class_id != kIllegalCid);
- ASSERT(!target.IsNull());
const intptr_t old_num = NumberOfChecks();
Array& data = Array::Handle(ic_data());
@@ -11890,6 +11994,9 @@
RawArray* Array::MakeArray(const GrowableObjectArray& growable_array) {
+ if (growable_array.IsNull()) {
+ return Array::null();
+ }
intptr_t used_len = growable_array.Length();
intptr_t capacity_len = growable_array.Capacity();
Isolate* isolate = Isolate::Current();
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 88e93d2..a0ba8ae 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -753,6 +753,7 @@
RawFunction* LookupStaticFunction(const String& name) const;
RawFunction* LookupStaticFunctionAllowPrivate(const String& name) const;
RawFunction* LookupConstructor(const String& name) const;
+ RawFunction* LookupConstructorAllowPrivate(const String& name) const;
RawFunction* LookupFactory(const String& name) const;
RawFunction* LookupFunction(const String& name) const;
RawFunction* LookupFunctionAllowPrivate(const String& name) const;
@@ -2116,21 +2117,29 @@
static void InitCoreLibrary(Isolate* isolate);
static void InitCollectionLibrary(Isolate* isolate);
static void InitCollectionDevLibrary(Isolate* isolate);
- static void InitMathLibrary(Isolate* isolate);
+ static void InitCryptoLibrary(Isolate* isolate);
static void InitIsolateLibrary(Isolate* isolate);
+ static void InitJsonLibrary(Isolate* isolate);
+ static void InitMathLibrary(Isolate* isolate);
static void InitMirrorsLibrary(Isolate* isolate);
- static void InitScalarlistLibrary(Isolate* isolate);
static void InitNativeWrappersLibrary(Isolate* isolate);
+ static void InitScalarlistLibrary(Isolate* isolate);
+ static void InitUriLibrary(Isolate* isolate);
+ static void InitUtfLibrary(Isolate* isolate);
static RawLibrary* ASyncLibrary();
static RawLibrary* CoreLibrary();
static RawLibrary* CollectionLibrary();
static RawLibrary* CollectionDevLibrary();
- static RawLibrary* MathLibrary();
+ static RawLibrary* CryptoLibrary();
static RawLibrary* IsolateLibrary();
+ static RawLibrary* JsonLibrary();
+ static RawLibrary* MathLibrary();
static RawLibrary* MirrorsLibrary();
- static RawLibrary* ScalarlistLibrary();
static RawLibrary* NativeWrappersLibrary();
+ static RawLibrary* ScalarlistLibrary();
+ static RawLibrary* UriLibrary();
+ static RawLibrary* UtfLibrary();
// Eagerly compile all classes and functions in the library.
static RawError* CompileAll();
@@ -2224,8 +2233,9 @@
class Instructions : public Object {
public:
- intptr_t size() const { return raw_ptr()->size_; }
+ intptr_t size() const { return raw_ptr()->size_; } // Excludes HeaderSize().
RawCode* code() const { return raw_ptr()->code_; }
+ RawArray* object_pool() const { return raw_ptr()->object_pool_; }
uword EntryPoint() const {
return reinterpret_cast<uword>(raw_ptr()) + HeaderSize();
@@ -2263,9 +2273,12 @@
void set_size(intptr_t size) const {
raw_ptr()->size_ = size;
}
- void set_code(RawCode* code) {
+ void set_code(RawCode* code) const {
raw_ptr()->code_ = code;
}
+ void set_object_pool(RawArray* object_pool) const {
+ raw_ptr()->object_pool_ = object_pool;
+ }
// New is a private method as RawInstruction and RawCode objects should
// only be created using the Code::FinalizeCode method. This method creates
@@ -2608,6 +2621,15 @@
const Instructions& instr = Instructions::Handle(instructions());
return instr.size();
}
+ RawArray* ObjectPool() const {
+ const Instructions& instr = Instructions::Handle(instructions());
+ return instr.object_pool();
+ }
+ bool ContainsInstructionAt(uword addr) const {
+ const Instructions& instr = Instructions::Handle(instructions());
+ const uword offset = addr - instr.EntryPoint();
+ return offset < static_cast<uword>(instr.size());
+ }
RawPcDescriptors* pc_descriptors() const {
return raw_ptr()->pc_descriptors_;
diff --git a/runtime/vm/object_store.cc b/runtime/vm/object_store.cc
index 0d847da..5343c42 100644
--- a/runtime/vm/object_store.cc
+++ b/runtime/vm/object_store.cc
@@ -66,15 +66,20 @@
weak_property_class_(Class::null()),
symbol_table_(Array::null()),
canonical_type_arguments_(Array::null()),
+ async_library_(Library::null()),
+ builtin_library_(Library::null()),
core_library_(Library::null()),
core_impl_library_(Library::null()),
- math_library_(Library::null()),
+ crypto_library_(Library::null()),
isolate_library_(Library::null()),
+ json_library_(Library::null()),
+ math_library_(Library::null()),
mirrors_library_(Library::null()),
- scalarlist_library_(Library::null()),
native_wrappers_library_(Library::null()),
- builtin_library_(Library::null()),
root_library_(Library::null()),
+ scalarlist_library_(Library::null()),
+ uri_library_(Library::null()),
+ utf_library_(Library::null()),
libraries_(GrowableObjectArray::null()),
pending_classes_(GrowableObjectArray::null()),
sticky_error_(Error::null()),
diff --git a/runtime/vm/object_store.h b/runtime/vm/object_store.h
index bf98064..8bc26cf 100644
--- a/runtime/vm/object_store.h
+++ b/runtime/vm/object_store.h
@@ -360,6 +360,13 @@
async_library_ = value.raw();
}
+ RawLibrary* builtin_library() const {
+ return builtin_library_;
+ }
+ void set_builtin_library(const Library& value) {
+ builtin_library_ = value.raw();
+ }
+
RawLibrary* core_library() const { return core_library_; }
void set_core_library(const Library& value) {
core_library_ = value.raw();
@@ -384,11 +391,11 @@
collection_dev_library_ = value.raw();
}
- RawLibrary* math_library() const {
- return math_library_;
+ RawLibrary* crypto_library() const {
+ return crypto_library_;
}
- void set_math_library(const Library& value) {
- math_library_ = value.raw();
+ void set_crypto_library(const Library& value) {
+ crypto_library_ = value.raw();
}
RawLibrary* isolate_library() const {
@@ -398,6 +405,25 @@
isolate_library_ = value.raw();
}
+ RawLibrary* json_library() const {
+ return json_library_;
+ }
+ void set_json_library(const Library& value) {
+ json_library_ = value.raw();
+ }
+
+ RawLibrary* math_library() const {
+ return math_library_;
+ }
+ void set_math_library(const Library& value) {
+ math_library_ = value.raw();
+ }
+
+ RawLibrary* mirrors_library() const { return mirrors_library_; }
+ void set_mirrors_library(const Library& value) {
+ mirrors_library_ = value.raw();
+ }
+
RawLibrary* native_wrappers_library() const {
return native_wrappers_library_;
}
@@ -405,9 +431,9 @@
native_wrappers_library_ = value.raw();
}
- RawLibrary* mirrors_library() const { return mirrors_library_; }
- void set_mirrors_library(const Library& value) {
- mirrors_library_ = value.raw();
+ RawLibrary* root_library() const { return root_library_; }
+ void set_root_library(const Library& value) {
+ root_library_ = value.raw();
}
RawLibrary* scalarlist_library() const {
@@ -417,16 +443,18 @@
scalarlist_library_ = value.raw();
}
- RawLibrary* builtin_library() const {
- return builtin_library_;
+ RawLibrary* uri_library() const {
+ return uri_library_;
}
- void set_builtin_library(const Library& value) {
- builtin_library_ = value.raw();
+ void set_uri_library(const Library& value) {
+ uri_library_ = value.raw();
}
- RawLibrary* root_library() const { return root_library_; }
- void set_root_library(const Library& value) {
- root_library_ = value.raw();
+ RawLibrary* utf_library() const {
+ return utf_library_;
+ }
+ void set_utf_library(const Library& value) {
+ utf_library_ = value.raw();
}
RawGrowableObjectArray* libraries() const { return libraries_; }
@@ -568,17 +596,21 @@
RawArray* symbol_table_;
RawArray* canonical_type_arguments_;
RawLibrary* async_library_;
+ RawLibrary* builtin_library_;
RawLibrary* core_library_;
RawLibrary* core_impl_library_;
RawLibrary* collection_library_;
RawLibrary* collection_dev_library_;
- RawLibrary* math_library_;
+ RawLibrary* crypto_library_;
RawLibrary* isolate_library_;
+ RawLibrary* json_library_;
+ RawLibrary* math_library_;
RawLibrary* mirrors_library_;
- RawLibrary* scalarlist_library_;
RawLibrary* native_wrappers_library_;
- RawLibrary* builtin_library_;
RawLibrary* root_library_;
+ RawLibrary* scalarlist_library_;
+ RawLibrary* uri_library_;
+ RawLibrary* utf_library_;
RawGrowableObjectArray* libraries_;
RawGrowableObjectArray* pending_classes_;
RawError* sticky_error_;
diff --git a/runtime/vm/os_android.cc b/runtime/vm/os_android.cc
index 94d58b3..0bf5d36 100644
--- a/runtime/vm/os_android.cc
+++ b/runtime/vm/os_android.cc
@@ -2,17 +2,20 @@
// 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/globals.h"
+#if defined(TARGET_OS_ANDROID)
+
#include "vm/os.h"
-#include <android/log.h>
-#include <errno.h>
-#include <limits.h>
-#include <malloc.h>
-#include <time.h>
-#include <sys/resource.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <unistd.h>
+#include <android/log.h> // NOLINT
+#include <errno.h> // NOLINT
+#include <limits.h> // NOLINT
+#include <malloc.h> // NOLINT
+#include <time.h> // NOLINT
+#include <sys/resource.h> // NOLINT
+#include <sys/time.h> // NOLINT
+#include <sys/types.h> // NOLINT
+#include <unistd.h> // NOLINT
#include "platform/utils.h"
#include "vm/code_observers.h"
@@ -417,3 +420,5 @@
}
} // namespace dart
+
+#endif // defined(TARGET_OS_ANDROID)
diff --git a/runtime/vm/os_linux.cc b/runtime/vm/os_linux.cc
index 66360b7..a9dcfd9 100644
--- a/runtime/vm/os_linux.cc
+++ b/runtime/vm/os_linux.cc
@@ -2,16 +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.
+#include "vm/globals.h"
+#if defined(TARGET_OS_LINUX)
+
#include "vm/os.h"
-#include <errno.h>
-#include <limits.h>
-#include <malloc.h>
-#include <time.h>
-#include <sys/resource.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <unistd.h>
+#include <errno.h> // NOLINT
+#include <limits.h> // NOLINT
+#include <malloc.h> // NOLINT
+#include <time.h> // NOLINT
+#include <sys/resource.h> // NOLINT
+#include <sys/time.h> // NOLINT
+#include <sys/types.h> // NOLINT
+#include <unistd.h> // NOLINT
#include "platform/utils.h"
#include "vm/code_observers.h"
@@ -422,3 +425,5 @@
}
} // namespace dart
+
+#endif // defined(TARGET_OS_LINUX)
diff --git a/runtime/vm/os_macos.cc b/runtime/vm/os_macos.cc
index 35166e1..fd530de 100644
--- a/runtime/vm/os_macos.cc
+++ b/runtime/vm/os_macos.cc
@@ -2,16 +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.
+#include "vm/globals.h"
+#if defined(TARGET_OS_MACOS)
+
#include "vm/os.h"
-#include <errno.h>
-#include <limits.h>
-#include <mach/mach.h>
-#include <mach/clock.h>
-#include <mach/mach_time.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <unistd.h>
+#include <errno.h> // NOLINT
+#include <limits.h> // NOLINT
+#include <mach/mach.h> // NOLINT
+#include <mach/clock.h> // NOLINT
+#include <mach/mach_time.h> // NOLINT
+#include <sys/time.h> // NOLINT
+#include <sys/resource.h> // NOLINT
+#include <unistd.h> // NOLINT
#include "platform/utils.h"
#include "vm/isolate.h"
@@ -219,3 +222,5 @@
}
} // namespace dart
+
+#endif // defined(TARGET_OS_MACOS)
diff --git a/runtime/vm/os_win.cc b/runtime/vm/os_win.cc
index d4d51d7..7fa1b8c 100644
--- a/runtime/vm/os_win.cc
+++ b/runtime/vm/os_win.cc
@@ -2,10 +2,13 @@
// 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/globals.h"
+#if defined(TARGET_OS_WINDOWS)
+
#include "vm/os.h"
-#include <malloc.h>
-#include <time.h>
+#include <malloc.h> // NOLINT
+#include <time.h> // NOLINT
#include "platform/utils.h"
#include "platform/assert.h"
@@ -290,3 +293,5 @@
}
} // namespace dart
+
+#endif // defined(TARGET_OS_WINDOWS)
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index 0c9427e..4740688 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -4,6 +4,7 @@
#include "vm/parser.h"
+#include "lib/invocation_mirror.h"
#include "vm/bigint_operations.h"
#include "vm/class_finalizer.h"
#include "vm/compiler.h"
@@ -2394,7 +2395,13 @@
// TODO(regis): For an instance function, pass the receiver to
// NoSuchMethodError.
current_block_->statements->Add(
- ThrowNoSuchMethodError(TokenPos(), current_class(), function_name));
+ ThrowNoSuchMethodError(TokenPos(),
+ current_class(),
+ function_name,
+ func.is_static() ?
+ InvocationMirror::kStatic :
+ InvocationMirror::kDynamic,
+ InvocationMirror::kMethod));
} else {
UnexpectedToken();
}
@@ -6849,7 +6856,9 @@
// argument values? Or should the spec specify a different evaluation order?
AstNode* Parser::ThrowNoSuchMethodError(intptr_t call_pos,
const Class& cls,
- const String& function_name) {
+ const String& function_name,
+ InvocationMirror::Call im_call,
+ InvocationMirror::Type im_type) {
ArgumentListNode* arguments = new ArgumentListNode(call_pos);
// Object receiver.
// TODO(regis): For now, we pass a class literal of the unresolved
@@ -6862,6 +6871,14 @@
// String memberName.
arguments->Add(new LiteralNode(
call_pos, String::ZoneHandle(Symbols::New(function_name))));
+ // Smi invocation_type.
+ if (cls.IsTopLevel()) {
+ ASSERT(im_call == InvocationMirror::kStatic ||
+ im_call == InvocationMirror::kTopLevel);
+ im_call = InvocationMirror::kTopLevel;
+ }
+ arguments->Add(new LiteralNode(call_pos, Smi::ZoneHandle(
+ Smi::New(InvocationMirror::EncodeType(im_call, im_type)))));
// List arguments.
arguments->Add(new LiteralNode(call_pos, Array::ZoneHandle()));
// List argumentNames.
@@ -7173,7 +7190,9 @@
// TODO(tball): determine whether NoSuchMethod should be called instead.
result = ThrowNoSuchMethodError(original->token_pos(),
current_class(),
- type_name);
+ type_name,
+ InvocationMirror::kStatic,
+ InvocationMirror::kSetter);
}
if ((result != NULL) &&
(result->IsStoreIndexedNode() ||
@@ -7458,7 +7477,11 @@
return new ClosureCallNode(call_pos, closure, arguments);
}
// Could not resolve static method: throw a NoSuchMethodError.
- return ThrowNoSuchMethodError(ident_pos, cls, func_name);
+ return ThrowNoSuchMethodError(ident_pos,
+ cls,
+ func_name,
+ InvocationMirror::kStatic,
+ InvocationMirror::kMethod);
}
return new StaticCallNode(call_pos, func, arguments);
}
@@ -7529,7 +7552,11 @@
Resolver::kIsQualified);
if (func.IsNull()) {
// No field or explicit setter function, throw a NoSuchMethodError.
- return ThrowNoSuchMethodError(ident_pos, cls, field_name);
+ return ThrowNoSuchMethodError(ident_pos,
+ cls,
+ field_name,
+ InvocationMirror::kStatic,
+ InvocationMirror::kField);
}
// Explicit setter function for the field found, field does not exist.
@@ -7569,7 +7596,11 @@
func = cls.LookupStaticFunction(field_name);
if (func.IsNull()) {
// No field or explicit getter function, throw a NoSuchMethodError.
- return ThrowNoSuchMethodError(ident_pos, cls, field_name);
+ return ThrowNoSuchMethodError(ident_pos,
+ cls,
+ field_name,
+ InvocationMirror::kStatic,
+ InvocationMirror::kGetter);
}
access = CreateImplicitClosureNode(func, call_pos, NULL);
} else {
@@ -7606,7 +7637,9 @@
current_function().IsInFactoryScope()) {
return ThrowNoSuchMethodError(primary->token_pos(),
current_class(),
- name);
+ name,
+ InvocationMirror::kStatic,
+ InvocationMirror::kField);
} else {
AstNode* receiver = LoadReceiver(primary->token_pos());
return CallGetter(node->token_pos(), receiver, name);
@@ -7744,7 +7777,9 @@
if (current_function().is_static()) {
selector = ThrowNoSuchMethodError(primary->token_pos(),
current_class(),
- name);
+ name,
+ InvocationMirror::kStatic,
+ InvocationMirror::kMethod);
} else {
// Treat as call to unresolved (instance) method.
AstNode* receiver = LoadReceiver(primary->token_pos());
@@ -8255,7 +8290,14 @@
}
// Try to find the identifier in the class scope of the current class.
- Class& cls = Class::Handle(isolate, current_class().raw());
+ // If the current class is the result of a mixin application, we must
+ // use the class scope of the class from which the function originates.
+ Class& cls = Class::Handle(isolate);
+ if (current_class().mixin() == Type::null()) {
+ cls = current_class().raw();
+ } else {
+ cls = parsed_function()->function().origin();
+ }
Function& func = Function::Handle(isolate, Function::null());
Field& field = Field::Handle(isolate, Field::null());
@@ -8656,7 +8698,11 @@
// the unresolved name to an instance field access, since a
// subclass might define a field with this name.
if (current_function().is_static()) {
- resolved = ThrowNoSuchMethodError(ident_pos, current_class(), ident);
+ resolved = ThrowNoSuchMethodError(ident_pos,
+ current_class(),
+ ident,
+ InvocationMirror::kStatic,
+ InvocationMirror::kField);
} else {
// Treat as call to unresolved instance field.
resolved = CallGetter(ident_pos, LoadReceiver(ident_pos), ident);
@@ -9256,7 +9302,9 @@
}
return ThrowNoSuchMethodError(call_pos,
type_class,
- external_constructor_name);
+ external_constructor_name,
+ InvocationMirror::kConstructor,
+ InvocationMirror::kMethod);
} else if (constructor.IsRedirectingFactory()) {
Type& redirect_type = Type::Handle(constructor.RedirectionType());
if (!redirect_type.IsMalformed() && !redirect_type.IsInstantiated()) {
@@ -9316,7 +9364,9 @@
}
return ThrowNoSuchMethodError(call_pos,
type_class,
- external_constructor_name);
+ external_constructor_name,
+ InvocationMirror::kConstructor,
+ InvocationMirror::kMethod);
}
// Return a throw in case of a malformed type or report a compile-time error
diff --git a/runtime/vm/parser.h b/runtime/vm/parser.h
index f0ca6d5..ba83c47 100644
--- a/runtime/vm/parser.h
+++ b/runtime/vm/parser.h
@@ -7,6 +7,7 @@
#include "include/dart_api.h"
+#include "lib/invocation_mirror.h"
#include "vm/ast.h"
#include "vm/class_finalizer.h"
#include "vm/compiler_stats.h"
@@ -614,7 +615,9 @@
AstNode* ThrowTypeError(intptr_t type_pos, const AbstractType& type);
AstNode* ThrowNoSuchMethodError(intptr_t call_pos,
const Class& cls,
- const String& function_name);
+ const String& function_name,
+ InvocationMirror::Call call,
+ InvocationMirror::Type type);
void CheckOperatorArity(const MemberDesc& member);
diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc
index de55c74..ecf2a79 100644
--- a/runtime/vm/raw_object.cc
+++ b/runtime/vm/raw_object.cc
@@ -472,7 +472,7 @@
intptr_t RawInstructions::VisitInstructionsPointers(
RawInstructions* raw_obj, ObjectPointerVisitor* visitor) {
RawInstructions* obj = raw_obj->ptr();
- visitor->VisitPointer(reinterpret_cast<RawObject**>(&obj->code_));
+ visitor->VisitPointers(raw_obj->from(), raw_obj->to());
return Instructions::InstanceSize(obj->size_);
}
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 9dd8faa..2889ff9 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -791,7 +791,14 @@
class RawInstructions : public RawObject {
RAW_HEAP_OBJECT_IMPLEMENTATION(Instructions);
+ RawObject** from() {
+ return reinterpret_cast<RawObject**>(&ptr()->code_);
+ }
RawCode* code_;
+ RawArray* object_pool_;
+ RawObject** to() {
+ return reinterpret_cast<RawObject**>(&ptr()->object_pool_);
+ }
intptr_t size_;
// Variable length data follows here.
diff --git a/runtime/vm/stack_frame_arm.cc b/runtime/vm/stack_frame_arm.cc
index 2d90b32..8d4ee28 100644
--- a/runtime/vm/stack_frame_arm.cc
+++ b/runtime/vm/stack_frame_arm.cc
@@ -50,4 +50,4 @@
} // namespace dart
-#endif // defined TARGET_ARCH_ARM
+#endif // defined(TARGET_ARCH_ARM)
diff --git a/runtime/vm/stub_code.cc b/runtime/vm/stub_code.cc
index b942c17..8160cfb 100644
--- a/runtime/vm/stub_code.cc
+++ b/runtime/vm/stub_code.cc
@@ -50,8 +50,10 @@
void StubCode::InitOnce() {
- // TODO(regis): Re-enable this after we are able to generate arm code.
-#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64)
+ // TODO(regis): Re-enable this after we are able to generate mips code.
+#if defined(TARGET_ARCH_IA32) || \
+ defined(TARGET_ARCH_X64) || \
+ defined(TARGET_ARCH_ARM)
// Generate all the stubs.
Code& code = Code::Handle();
VM_STUB_CODE_LIST(STUB_CODE_GENERATE);
@@ -60,8 +62,10 @@
void StubCode::GenerateFor(Isolate* init) {
- // TODO(regis): Re-enable this after we are able to generate arm code.
-#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64)
+ // TODO(regis): Re-enable this after we are able to generate mips code.
+#if defined(TARGET_ARCH_IA32) || \
+ defined(TARGET_ARCH_X64) || \
+ defined(TARGET_ARCH_ARM)
// Generate all the stubs.
Code& code = Code::Handle();
STUB_CODE_LIST(STUB_CODE_GENERATE);
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 0e6e274..ee82911 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -204,6 +204,10 @@
V(DartScalarlist, "dart:scalarlist") \
V(DartNativeWrappers, "dart:nativewrappers") \
V(DartAsync, "dart:async") \
+ V(DartUri, "dart:uri") \
+ V(DartUtf, "dart:utf") \
+ V(DartCrypto, "dart:crypto") \
+ V(DartJson, "dart:json") \
// Contains a list of frequently used strings in a canonicalized form. This
diff --git a/runtime/vm/unit_test.cc b/runtime/vm/unit_test.cc
index b12fa1f..06f42ce 100644
--- a/runtime/vm/unit_test.cc
+++ b/runtime/vm/unit_test.cc
@@ -78,14 +78,6 @@
// Handle imports of other built-in libraries present in the SDK.
if (DartUtils::IsDartIOLibURL(url_chars)) {
return Builtin::LoadAndCheckLibrary(Builtin::kIOLibrary);
- } else if (DartUtils::IsDartJsonLibURL(url_chars)) {
- return Builtin::LoadAndCheckLibrary(Builtin::kJsonLibrary);
- } else if (DartUtils::IsDartUriLibURL(url_chars)) {
- return Builtin::LoadAndCheckLibrary(Builtin::kUriLibrary);
- } else if (DartUtils::IsDartUtfLibURL(url_chars)) {
- return Builtin::LoadAndCheckLibrary(Builtin::kUtfLibrary);
- } else if (DartUtils::IsDartCryptoLibURL(url_chars)) {
- return Builtin::LoadAndCheckLibrary(Builtin::kCryptoLibrary);
} else if (DartUtils::IsDartBuiltinLibURL(url_chars)) {
return Builtin::LoadAndCheckLibrary(Builtin::kBuiltinLibrary);
} else {
@@ -134,24 +126,24 @@
}
-uword AssemblerTest::Assemble() {
+void AssemblerTest::Assemble() {
const String& function_name = String::ZoneHandle(Symbols::New(name_));
const Class& cls = Class::ZoneHandle(
Class::New(function_name, Script::Handle(), Scanner::kDummyTokenIndex));
Function& function = Function::ZoneHandle(
Function::New(function_name, RawFunction::kRegularFunction,
true, false, false, false, cls, 0));
- const Code& code = Code::Handle(Code::FinalizeCode(function, assembler_));
+ code_ = Code::FinalizeCode(function, assembler_);
if (FLAG_disassemble) {
OS::Print("Code for test '%s' {\n", name_);
const Instructions& instructions =
- Instructions::Handle(code.instructions());
+ Instructions::Handle(code_.instructions());
uword start = instructions.EntryPoint();
Disassembler::Disassemble(start, start + assembler_->CodeSize());
OS::Print("}\n");
}
- const Instructions& instructions = Instructions::Handle(code.instructions());
- return instructions.EntryPoint();
+ const Instructions& instructions = Instructions::Handle(code_.instructions());
+ entry_ = instructions.EntryPoint();
}
diff --git a/runtime/vm/unit_test.h b/runtime/vm/unit_test.h
index bd15b1e..26547cc 100644
--- a/runtime/vm/unit_test.h
+++ b/runtime/vm/unit_test.h
@@ -51,15 +51,16 @@
// The ASSEMBLER_TEST_RUN macro is used to execute the assembler unit
// test generated using the ASSEMBLER_TEST_GENERATE macro.
// C++ callee-saved registers are not preserved. Arguments may be passed in.
-#define ASSEMBLER_TEST_RUN(name, entry) \
- static void AssemblerTestRun##name(uword entry); \
+#define ASSEMBLER_TEST_RUN(name, test) \
+ static void AssemblerTestRun##name(AssemblerTest* test); \
TEST_CASE(name) { \
Assembler __assembler__; \
- AssemblerTest __test__(""#name, &__assembler__); \
- AssemblerTestGenerate##name(__test__.assembler()); \
- AssemblerTestRun##name(__test__.Assemble()); \
+ AssemblerTest test(""#name, &__assembler__); \
+ AssemblerTestGenerate##name(test.assembler()); \
+ test.Assemble(); \
+ AssemblerTestRun##name(&test); \
} \
- static void AssemblerTestRun##name(uword entry)
+ static void AssemblerTestRun##name(AssemblerTest* test)
// Populate node list with AST nodes.
#define CODEGEN_TEST_GENERATE(name, test) \
@@ -271,7 +272,8 @@
public:
AssemblerTest(const char* name, Assembler* assembler)
: name_(name),
- assembler_(assembler) {
+ assembler_(assembler),
+ code_(Code::ZoneHandle()) {
ASSERT(name != NULL);
ASSERT(assembler != NULL);
}
@@ -279,12 +281,18 @@
Assembler* assembler() const { return assembler_; }
- // Assemble test and return entry.
- uword Assemble();
+ const Code& code() const { return code_; }
+
+ uword entry() const { return entry_; }
+
+ // Assemble test and set code_ and entry_.
+ void Assemble();
private:
const char* name_;
Assembler* assembler_;
+ Code& code_;
+ uword entry_;
DISALLOW_COPY_AND_ASSIGN(AssemblerTest);
};
diff --git a/runtime/vm/virtual_memory_android.cc b/runtime/vm/virtual_memory_android.cc
index e852de5..966644d 100644
--- a/runtime/vm/virtual_memory_android.cc
+++ b/runtime/vm/virtual_memory_android.cc
@@ -2,10 +2,13 @@
// 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/globals.h"
+#if defined(TARGET_OS_ANDROID)
+
#include "vm/virtual_memory.h"
-#include <sys/mman.h>
-#include <unistd.h>
+#include <sys/mman.h> // NOLINT
+#include <unistd.h> // NOLINT
#include "platform/assert.h"
#include "platform/utils.h"
@@ -96,3 +99,5 @@
}
} // namespace dart
+
+#endif // defined(TARGET_OS_ANDROID)
diff --git a/runtime/vm/virtual_memory_linux.cc b/runtime/vm/virtual_memory_linux.cc
index 189c0fc..7a0d02b 100644
--- a/runtime/vm/virtual_memory_linux.cc
+++ b/runtime/vm/virtual_memory_linux.cc
@@ -2,11 +2,14 @@
// 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/globals.h"
+#if defined(TARGET_OS_LINUX)
+
#include "vm/virtual_memory.h"
-#include <sys/mman.h>
-#include <sys/unistd.h>
-#include <unistd.h>
+#include <sys/mman.h> // NOLINT
+#include <sys/unistd.h> // NOLINT
+#include <unistd.h> // NOLINT
#include "platform/assert.h"
#include "platform/utils.h"
@@ -96,3 +99,5 @@
}
} // namespace dart
+
+#endif // defined(TARGET_OS_LINUX)
diff --git a/runtime/vm/virtual_memory_macos.cc b/runtime/vm/virtual_memory_macos.cc
index ccfa167..c99a349 100644
--- a/runtime/vm/virtual_memory_macos.cc
+++ b/runtime/vm/virtual_memory_macos.cc
@@ -2,10 +2,13 @@
// 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/globals.h"
+#if defined(TARGET_OS_MACOS)
+
#include "vm/virtual_memory.h"
-#include <sys/mman.h>
-#include <unistd.h>
+#include <sys/mman.h> // NOLINT
+#include <unistd.h> // NOLINT
#include "platform/assert.h"
#include "platform/utils.h"
@@ -96,3 +99,5 @@
}
} // namespace dart
+
+#endif // defined(TARGET_OS_MACOS)
diff --git a/runtime/vm/virtual_memory_win.cc b/runtime/vm/virtual_memory_win.cc
index 2ac6784..d1a9277 100644
--- a/runtime/vm/virtual_memory_win.cc
+++ b/runtime/vm/virtual_memory_win.cc
@@ -2,6 +2,9 @@
// 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/globals.h"
+#if defined(TARGET_OS_WINDOWS)
+
#include "vm/virtual_memory.h"
#include "platform/assert.h"
@@ -84,3 +87,5 @@
}
} // namespace dart
+
+#endif // defined(TARGET_OS_WINDOWS)
diff --git a/runtime/vm/vm.gypi b/runtime/vm/vm.gypi
index 9ed725d..ca69ca5 100644
--- a/runtime/vm/vm.gypi
+++ b/runtime/vm/vm.gypi
@@ -11,14 +11,18 @@
'corelib_patch_cc_file': '<(SHARED_INTERMEDIATE_DIR)/corelib_patch_gen.cc',
'collection_cc_file': '<(SHARED_INTERMEDIATE_DIR)/collection_gen.cc',
'collection_dev_cc_file': '<(SHARED_INTERMEDIATE_DIR)/collection_dev_gen.cc',
+ 'crypto_cc_file': '<(SHARED_INTERMEDIATE_DIR)/crypto_gen.cc',
'math_cc_file': '<(SHARED_INTERMEDIATE_DIR)/math_gen.cc',
'math_patch_cc_file': '<(SHARED_INTERMEDIATE_DIR)/math_patch_gen.cc',
'mirrors_cc_file': '<(SHARED_INTERMEDIATE_DIR)/mirrors_gen.cc',
'mirrors_patch_cc_file': '<(SHARED_INTERMEDIATE_DIR)/mirrors_patch_gen.cc',
'isolate_cc_file': '<(SHARED_INTERMEDIATE_DIR)/isolate_gen.cc',
'isolate_patch_cc_file': '<(SHARED_INTERMEDIATE_DIR)/isolate_patch_gen.cc',
+ 'json_cc_file': '<(SHARED_INTERMEDIATE_DIR)/json_gen.cc',
'scalarlist_cc_file': '<(SHARED_INTERMEDIATE_DIR)/scalarlist_gen.cc',
'scalarlist_patch_cc_file': '<(SHARED_INTERMEDIATE_DIR)/scalarlist_patch_gen.cc',
+ 'uri_cc_file': '<(SHARED_INTERMEDIATE_DIR)/uri_gen.cc',
+ 'utf_cc_file': '<(SHARED_INTERMEDIATE_DIR)/utf_gen.cc',
'snapshot_test_dat_file': '<(SHARED_INTERMEDIATE_DIR)/snapshot_test.dat',
'snapshot_test_in_dat_file': 'snapshot_test_in.dat',
'snapshot_test_dart_file': 'snapshot_test.dart',
@@ -91,14 +95,18 @@
'generate_corelib_patch_cc_file',
'generate_collection_cc_file',
'generate_collection_dev_cc_file',
+ 'generate_crypto_cc_file',
'generate_math_cc_file',
'generate_math_patch_cc_file',
'generate_isolate_cc_file',
'generate_isolate_patch_cc_file',
+ 'generate_json_cc_file',
'generate_mirrors_cc_file',
'generate_mirrors_patch_cc_file',
'generate_scalarlist_cc_file',
'generate_scalarlist_patch_cc_file',
+ 'generate_uri_cc_file',
+ 'generate_utf_cc_file',
],
'includes': [
'../lib/async_sources.gypi',
@@ -117,14 +125,18 @@
'<(corelib_patch_cc_file)',
'<(collection_cc_file)',
'<(collection_dev_cc_file)',
+ '<(crypto_cc_file)',
'<(math_cc_file)',
'<(math_patch_cc_file)',
'<(isolate_cc_file)',
'<(isolate_patch_cc_file)',
+ '<(json_cc_file)',
'<(mirrors_cc_file)',
'<(mirrors_patch_cc_file)',
'<(scalarlist_cc_file)',
'<(scalarlist_patch_cc_file)',
+ '<(uri_cc_file)',
+ '<(utf_cc_file)',
],
'include_dirs': [
'..',
@@ -413,6 +425,56 @@
]
},
{
+ 'target_name': 'generate_crypto_cc_file',
+ 'type': 'none',
+ 'variables': {
+ 'crypto_dart': '<(SHARED_INTERMEDIATE_DIR)/crypto_gen.dart',
+ },
+ 'includes': [
+ # Load the shared crypto sources.
+ '../../sdk/lib/crypto/crypto_sources.gypi',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'generate_crypto_dart',
+ 'inputs': [
+ '../tools/concat_library.py',
+ '<@(_sources)',
+ ],
+ 'outputs': [
+ '<(crypto_dart)',
+ ],
+ 'action': [
+ 'python',
+ '<@(_inputs)',
+ '--output', '<(crypto_dart)',
+ ],
+ 'message': 'Generating ''<(crypto_dart)'' file.',
+ },
+ {
+ 'action_name': 'generate_crypto_cc',
+ 'inputs': [
+ '../tools/create_string_literal.py',
+ '<(builtin_in_cc_file)',
+ '<(crypto_dart)',
+ ],
+ 'outputs': [
+ '<(crypto_cc_file)',
+ ],
+ 'action': [
+ 'python',
+ 'tools/create_string_literal.py',
+ '--output', '<(crypto_cc_file)',
+ '--input_cc', '<(builtin_in_cc_file)',
+ '--include', 'vm/bootstrap.h',
+ '--var_name', 'dart::Bootstrap::crypto_source_',
+ '<(crypto_dart)',
+ ],
+ 'message': 'Generating ''<(crypto_cc_file)'' file.'
+ },
+ ]
+ },
+ {
'target_name': 'generate_math_cc_file',
'type': 'none',
'variables': {
@@ -736,6 +798,56 @@
]
},
{
+ 'target_name': 'generate_json_cc_file',
+ 'type': 'none',
+ 'variables': {
+ 'json_dart': '<(SHARED_INTERMEDIATE_DIR)/json_gen.dart',
+ },
+ 'includes': [
+ # Load the shared json sources.
+ '../../sdk/lib/json/json_sources.gypi',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'generate_json_dart',
+ 'inputs': [
+ '../tools/concat_library.py',
+ '<@(_sources)',
+ ],
+ 'outputs': [
+ '<(json_dart)',
+ ],
+ 'action': [
+ 'python',
+ '<@(_inputs)',
+ '--output', '<(json_dart)',
+ ],
+ 'message': 'Generating ''<(json_dart)'' file.',
+ },
+ {
+ 'action_name': 'generate_json_cc',
+ 'inputs': [
+ '../tools/create_string_literal.py',
+ '<(builtin_in_cc_file)',
+ '<(json_dart)',
+ ],
+ 'outputs': [
+ '<(json_cc_file)',
+ ],
+ 'action': [
+ 'python',
+ 'tools/create_string_literal.py',
+ '--output', '<(json_cc_file)',
+ '--input_cc', '<(builtin_in_cc_file)',
+ '--include', 'vm/bootstrap.h',
+ '--var_name', 'dart::Bootstrap::json_source_',
+ '<(json_dart)',
+ ],
+ 'message': 'Generating ''<(json_cc_file)'' file.'
+ },
+ ]
+ },
+ {
'target_name': 'generate_scalarlist_cc_file',
'type': 'none',
'variables': {
@@ -831,6 +943,106 @@
]
},
{
+ 'target_name': 'generate_uri_cc_file',
+ 'type': 'none',
+ 'variables': {
+ 'uri_dart': '<(SHARED_INTERMEDIATE_DIR)/uri_gen.dart',
+ },
+ 'includes': [
+ # Load the shared uri sources.
+ '../../sdk/lib/uri/uri_sources.gypi',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'generate_uri_dart',
+ 'inputs': [
+ '../tools/concat_library.py',
+ '<@(_sources)',
+ ],
+ 'outputs': [
+ '<(uri_dart)',
+ ],
+ 'action': [
+ 'python',
+ '<@(_inputs)',
+ '--output', '<(uri_dart)',
+ ],
+ 'message': 'Generating ''<(uri_dart)'' file.'
+ },
+ {
+ 'action_name': 'generate_uri_cc',
+ 'inputs': [
+ '../tools/create_string_literal.py',
+ '<(builtin_in_cc_file)',
+ '<(uri_dart)',
+ ],
+ 'outputs': [
+ '<(uri_cc_file)',
+ ],
+ 'action': [
+ 'python',
+ 'tools/create_string_literal.py',
+ '--output', '<(uri_cc_file)',
+ '--input_cc', '<(builtin_in_cc_file)',
+ '--include', 'vm/bootstrap.h',
+ '--var_name', 'dart::Bootstrap::uri_source_',
+ '<(uri_dart)',
+ ],
+ 'message': 'Generating ''<(uri_cc_file)'' file.'
+ },
+ ]
+ },
+ {
+ 'target_name': 'generate_utf_cc_file',
+ 'type': 'none',
+ 'variables': {
+ 'utf_dart': '<(SHARED_INTERMEDIATE_DIR)/utf_gen.dart',
+ },
+ 'includes': [
+ # Load the shared utf sources.
+ '../../sdk/lib/utf/utf_sources.gypi',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'generate_utf_dart',
+ 'inputs': [
+ '../tools/concat_library.py',
+ '<@(_sources)',
+ ],
+ 'outputs': [
+ '<(utf_dart)',
+ ],
+ 'action': [
+ 'python',
+ '<@(_inputs)',
+ '--output', '<(utf_dart)',
+ ],
+ 'message': 'Generating ''<(utf_dart)'' file.',
+ },
+ {
+ 'action_name': 'generate_utf_cc',
+ 'inputs': [
+ '../tools/create_string_literal.py',
+ '<(builtin_in_cc_file)',
+ '<(utf_dart)',
+ ],
+ 'outputs': [
+ '<(utf_cc_file)',
+ ],
+ 'action': [
+ 'python',
+ 'tools/create_string_literal.py',
+ '--output', '<(utf_cc_file)',
+ '--input_cc', '<(builtin_in_cc_file)',
+ '--include', 'vm/bootstrap.h',
+ '--var_name', 'dart::Bootstrap::utf_source_',
+ '<(utf_dart)',
+ ],
+ 'message': 'Generating ''<(utf_cc_file)'' file.'
+ },
+ ]
+ },
+ {
'target_name': 'generate_snapshot_test_dat_file',
'type': 'none',
'actions': [
diff --git a/sdk/lib/_internal/compiler/implementation/code_buffer.dart b/sdk/lib/_internal/compiler/implementation/code_buffer.dart
index 795742d..fafc409 100644
--- a/sdk/lib/_internal/compiler/implementation/code_buffer.dart
+++ b/sdk/lib/_internal/compiler/implementation/code_buffer.dart
@@ -33,7 +33,7 @@
return addBuffer(object);
}
if (mappedRangeCounter == 0) setSourceLocation(null);
- buffer.add(object.toString());
+ buffer.write(object);
return this;
}
@@ -60,7 +60,7 @@
}
lastBufferOffset = buffer.length + other.lastBufferOffset;
}
- buffer.add(other.getText());
+ buffer.write(other.getText());
return this;
}
@@ -73,7 +73,7 @@
}
CodeBuffer clear() {
- buffer.clear();
+ buffer = new StringBuffer();
markers.clear();
lastBufferOffset = 0;
return this;
diff --git a/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart b/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart
index 691cbac..e4758a2 100644
--- a/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart
+++ b/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart
@@ -175,7 +175,7 @@
if (isConst) {
compiler.reportError(node, new CompileTimeConstantError(
MessageKind.NOT_ASSIGNABLE,
- {'fromType': elementType, 'toType': constantType}));
+ {'fromType': constantType, 'toType': elementType}));
} else {
// If the field can be lazily initialized, we will throw
// the exception at runtime.
@@ -337,7 +337,8 @@
arguments.add(evaluateConstant(link.head));
}
// TODO(floitsch): get type parameters.
- DartType type = new InterfaceType(compiler.listClass);
+ compiler.listClass.computeType(compiler);
+ DartType type = compiler.listClass.rawType;
Constant constant = new ListConstant(type, arguments);
handler.registerCompileTimeConstant(constant);
return constant;
@@ -373,7 +374,8 @@
}
bool hasProtoKey = (protoValue != null);
// TODO(floitsch): this should be a List<String> type.
- DartType keysType = new InterfaceType(compiler.listClass);
+ compiler.listClass.computeType(compiler);
+ DartType keysType = compiler.listClass.rawType;
ListConstant keysList = new ListConstant(keysType, keys);
handler.registerCompileTimeConstant(keysList);
SourceString className = hasProtoKey
@@ -382,7 +384,7 @@
ClassElement classElement = compiler.jsHelperLibrary.find(className);
classElement.ensureResolved(compiler);
// TODO(floitsch): copy over the generic type.
- DartType type = new InterfaceType(classElement);
+ DartType type = classElement.rawType;
handler.registerInstantiatedClass(classElement);
Constant constant = new MapConstant(type, keysList, values, protoValue);
handler.registerCompileTimeConstant(constant);
@@ -660,7 +662,8 @@
handler.registerInstantiatedClass(classElement);
// TODO(floitsch): take generic types into account.
- DartType type = classElement.computeType(compiler);
+ classElement.computeType(compiler);
+ DartType type = classElement.rawType;
Constant constant = new ConstructedConstant(type, jsNewArguments);
handler.registerCompileTimeConstant(constant);
return constant;
diff --git a/sdk/lib/_internal/compiler/implementation/compiler.dart b/sdk/lib/_internal/compiler/implementation/compiler.dart
index adf2a77..ec15da18 100644
--- a/sdk/lib/_internal/compiler/implementation/compiler.dart
+++ b/sdk/lib/_internal/compiler/implementation/compiler.dart
@@ -126,8 +126,24 @@
return new ItemCompilationContext();
}
- SourceString getCheckedModeHelper(DartType type) => null;
+ // The following methods are hooks for the backend to register its
+ // helper methods.
void registerInstantiatedClass(ClassElement cls, Enqueuer enqueuer) {}
+ void registerStringInterpolation() {}
+ void registerCatchStatement() {}
+ void registerThrow() {}
+ void registerLazyField() {}
+ void registerTypeLiteral() {}
+ void registerStackTraceInCatch() {}
+ void registerIsCheck(DartType type, Enqueuer enqueuer) {}
+ void registerAsCheck(DartType type) {}
+ void registerThrowNoSuchMethod() {}
+ void registerThrowRuntimeError() {}
+ void registerAbstractClassInstantiation() {}
+ void registerFallThroughError() {}
+ void registerSuperNoSuchMethod() {}
+ void registerConstantMap() {}
+ void registerRuntimeType() {}
}
/**
@@ -291,6 +307,7 @@
ConstantHandler metadataHandler;
EnqueueTask enqueuer;
CompilerTask fileReadingTask;
+ DeferredLoadTask deferredLoadTask;
static const SourceString MAIN = const SourceString('main');
static const SourceString CALL_OPERATOR_NAME = const SourceString('call');
@@ -369,6 +386,7 @@
checker = new TypeCheckerTask(this),
typesTask = new ti.TypesTask(this),
constantHandler = new ConstantHandler(this, backend.constantSystem),
+ deferredLoadTask = new DeferredLoadTask(this),
enqueuer = new EnqueueTask(this)];
tasks.addAll(backend.tasks);
@@ -637,6 +655,8 @@
}
}
+ deferredLoadTask.registerMainApp(mainApp);
+
log('Resolving...');
phase = PHASE_RESOLVING;
if (analyzeAll) {
@@ -845,8 +865,9 @@
// is more complete.
if (identical(message.message.kind, MessageKind.NOT_ASSIGNABLE)) return;
if (identical(message.message.kind, MessageKind.MISSING_RETURN)) return;
- if (identical(message.message.kind, MessageKind.MAYBE_MISSING_RETURN)) return;
- if (identical(message.message.kind, MessageKind.METHOD_NOT_FOUND)) return;
+ if (identical(message.message.kind, MessageKind.MAYBE_MISSING_RETURN)) {
+ return;
+ }
}
SourceSpan span = spanFromNode(node);
@@ -1026,7 +1047,7 @@
String get name => 'Unknown task';
int get timing => watch.elapsedMilliseconds;
- measure(Function action) {
+ measure(action()) {
CompilerTask previous = compiler.measuredTask;
if (identical(this, previous)) return action();
compiler.measuredTask = this;
@@ -1040,6 +1061,10 @@
compiler.measuredTask = previous;
}
}
+
+ measureElement(Element element, action()) {
+ compiler.withCurrentElement(element, () => measure(action));
+ }
}
class CompilerCancelledException implements Exception {
diff --git a/sdk/lib/_internal/compiler/implementation/dart2js.dart b/sdk/lib/_internal/compiler/implementation/dart2js.dart
index 5fdcd2c7..0f0a029 100644
--- a/sdk/lib/_internal/compiler/implementation/dart2js.dart
+++ b/sdk/lib/_internal/compiler/implementation/dart2js.dart
@@ -151,6 +151,12 @@
return passThrough('--categories=${categories.join(",")}');
}
+ // TODO(8522): Remove this method once option is restored.
+ complainAboutDisallowUnsafeEval(String argument) {
+ fail('Error: $argument is currently not supported, '
+ 'see http://dartbug.com/8522');
+ }
+
handleShortOptions(String argument) {
var shortOptions = argument.substring(1).split("");
for (var shortOption in shortOptions) {
@@ -196,7 +202,8 @@
(_) => passThrough('--enable-concrete-type-inference')),
new OptionHandler(r'--help|/\?|/h', (_) => wantHelp = true),
new OptionHandler('--package-root=.+|-p.+', setPackageRoot),
- new OptionHandler('--disallow-unsafe-eval', passThrough),
+ new OptionHandler('--disallow-unsafe-eval',
+ complainAboutDisallowUnsafeEval),
new OptionHandler('--analyze-all', passThrough),
new OptionHandler('--analyze-only', setAnalyzeOnly),
new OptionHandler('--disable-native-live-type-analysis', passThrough),
@@ -283,7 +290,8 @@
if (uri.scheme != 'file') {
fail('Error: Unhandled scheme ${uri.scheme} in $uri.');
}
- var outputStream = new File(uriPathToNative(uri.path)).openOutputStream();
+ IOSink output =
+ new File(uriPathToNative(uri.path)).openWrite();
CountingSink sink;
@@ -291,16 +299,16 @@
if (sourceMapFileName != null) {
String sourceMapTag = '//@ sourceMappingURL=$sourceMapFileName\n';
sink.count += sourceMapTag.length;
- outputStream.writeString(sourceMapTag);
+ output.addString(sourceMapTag);
}
- outputStream.close();
+ output.close();
if (isPrimaryOutput) {
charactersWritten += sink.count;
}
}
var controller = new StreamController<String>();
- controller.stream.listen(outputStream.writeString, onDone: onDone);
+ controller.stream.listen(output.addString, onDone: onDone);
sink = new CountingSink(controller);
return sink;
}
@@ -434,12 +442,18 @@
Disable the optimization that removes unused native types from dart:html
and related libraries.
+'''
+/* TODO(8522): Restore this comment once option is restored.
+'''
--disallow-unsafe-eval
Disable dynamic generation of code in the generated output. This is
necessary to satisfy CSP restrictions (see http://www.w3.org/TR/CSP/).
This flag is not continuously tested. Please report breakages and we
will fix them as soon as possible.
+'''
+*/
+'''
--reject-deprecated-language-features
Reject deprecated language features. Without this option, the
compiler will accept language features that are no longer valid
diff --git a/sdk/lib/_internal/compiler/implementation/dart2jslib.dart b/sdk/lib/_internal/compiler/implementation/dart2jslib.dart
index 9b683f2..fd859af 100644
--- a/sdk/lib/_internal/compiler/implementation/dart2jslib.dart
+++ b/sdk/lib/_internal/compiler/implementation/dart2jslib.dart
@@ -34,6 +34,7 @@
import 'types/types.dart' as ti;
import 'resolution/resolution.dart';
import 'js/js.dart' as js;
+import 'deferred_load.dart' show DeferredLoadTask;
export 'resolution/resolution.dart' show TreeElements, TreeElementMapping;
export 'scanner/scannerlib.dart' show SourceString,
diff --git a/sdk/lib/_internal/compiler/implementation/dart_backend/backend.dart b/sdk/lib/_internal/compiler/implementation/dart_backend/backend.dart
index 45a6523..d400708 100644
--- a/sdk/lib/_internal/compiler/implementation/dart_backend/backend.dart
+++ b/sdk/lib/_internal/compiler/implementation/dart_backend/backend.dart
@@ -440,7 +440,7 @@
// TODO(antonm): Ideally XML should be a separate backend.
// TODO(antonm): obey renames and minification, at least as an option.
StringBuffer sb = new StringBuffer();
- outputElement(element) { sb.add(parse(element).toDebugString()); }
+ outputElement(element) { sb.write(parse(element).toDebugString()); }
// Emit XML for AST instead of the program.
for (final topLevel in sortedTopLevels) {
@@ -502,7 +502,7 @@
visit(Node node) {
if (node != null && renames.containsKey(node)) {
- sb.add(renames[node]);
+ sb.write(renames[node]);
} else {
super.visit(node);
}
@@ -516,7 +516,7 @@
unparseFunctionName(Node name) {
if (name != null && renames.containsKey(name)) {
- sb.add(renames[name]);
+ sb.write(renames[name]);
} else {
super.unparseFunctionName(name);
}
diff --git a/sdk/lib/_internal/compiler/implementation/dart_backend/renamer.dart b/sdk/lib/_internal/compiler/implementation/dart_backend/renamer.dart
index 886d39e..9acc6ec 100644
--- a/sdk/lib/_internal/compiler/implementation/dart_backend/renamer.dart
+++ b/sdk/lib/_internal/compiler/implementation/dart_backend/renamer.dart
@@ -89,15 +89,15 @@
StringBuffer result = new StringBuffer(renameElement(type.element));
if (type is InterfaceType) {
if (!type.isRaw) {
- result.add('<');
+ result.write('<');
Link<DartType> argumentsLink = type.typeArguments;
- result.add(renameType(argumentsLink.head, renameElement));
+ result.write(renameType(argumentsLink.head, renameElement));
for (Link<DartType> link = argumentsLink.tail; !link.isEmpty;
link = link.tail) {
- result.add(',');
- result.add(renameType(link.head, renameElement));
+ result.write(',');
+ result.write(renameType(link.head, renameElement));
}
- result.add('>');
+ result.write('>');
}
}
return result.toString();
@@ -112,8 +112,8 @@
// Named constructor or factory. Is there a more reliable way to check
// this case?
if (!placeholder.isRedirectingCall) {
- result.add(renameType(placeholder.type, renameElement));
- result.add('.');
+ result.write(renameType(placeholder.type, renameElement));
+ result.write('.');
}
String prefix = '${element.getEnclosingClass().name.slowToString()}\$';
if (!name.startsWith(prefix)) {
@@ -125,10 +125,10 @@
if (!element.getLibrary().isPlatformLibrary) {
name = renameString(element.getLibrary(), name);
}
- result.add(name);
+ result.write(name);
} else {
assert(!placeholder.isRedirectingCall);
- result.add(renameType(placeholder.type, renameElement));
+ result.write(renameType(placeholder.type, renameElement));
}
return result.toString();
}
@@ -346,14 +346,14 @@
int index = nextIdIndex++;
StringBuffer resultBuilder = new StringBuffer();
if (index < firstCharAlphabet.length) return firstCharAlphabet[index];
- resultBuilder.add(firstCharAlphabet[index % firstCharAlphabet.length]);
+ resultBuilder.write(firstCharAlphabet[index % firstCharAlphabet.length]);
index ~/= firstCharAlphabet.length;
int length = otherCharsAlphabet.length;
while (index >= length) {
- resultBuilder.add(otherCharsAlphabet[index % length]);
+ resultBuilder.write(otherCharsAlphabet[index % length]);
index ~/= length;
}
- resultBuilder.add(otherCharsAlphabet[index]);
+ resultBuilder.write(otherCharsAlphabet[index]);
return resultBuilder.toString();
}
}
diff --git a/sdk/lib/_internal/compiler/implementation/dart_types.dart b/sdk/lib/_internal/compiler/implementation/dart_types.dart
index 1d9f217..ec79917 100644
--- a/sdk/lib/_internal/compiler/implementation/dart_types.dart
+++ b/sdk/lib/_internal/compiler/implementation/dart_types.dart
@@ -70,6 +70,14 @@
DartType unalias(Compiler compiler);
/**
+ * Finds the method, field or property named [name] declared or inherited
+ * on this type.
+ */
+ // TODO(johnniwinther): Implement this for [TypeVariableType], [FunctionType],
+ // and [TypedefType].
+ Member lookupMember(SourceString name) => null;
+
+ /**
* A type is malformed if it is itself a malformed type or contains a
* malformed type.
*/
@@ -93,6 +101,8 @@
bool get isRaw => true;
DartType asRaw() => this;
+
+ accept(DartTypeVisitor visitor, var argument);
}
/**
@@ -151,6 +161,10 @@
DartType unalias(Compiler compiler) => this;
+ accept(DartTypeVisitor visitor, var argument) {
+ return visitor.visitTypeVariableType(this, argument);
+ }
+
int get hashCode => 17 * element.hashCode;
bool operator ==(other) {
@@ -191,6 +205,10 @@
DartType unalias(Compiler compiler) => this;
+ accept(DartTypeVisitor visitor, var argument) {
+ return visitor.visitStatementType(this, argument);
+ }
+
int get hashCode => 17 * stringName.hashCode;
bool operator ==(other) {
@@ -217,6 +235,10 @@
DartType unalias(Compiler compiler) => this;
+ accept(DartTypeVisitor visitor, var argument) {
+ return visitor.visitVoidType(this, argument);
+ }
+
int get hashCode => 1729;
bool operator ==(other) => other is VoidType;
@@ -264,21 +286,25 @@
DartType unalias(Compiler compiler) => this;
+ accept(DartTypeVisitor visitor, var argument) {
+ return visitor.visitMalformedType(this, argument);
+ }
+
String toString() {
var sb = new StringBuffer();
if (typeArguments != null) {
if (userProvidedBadType != null) {
- sb.add(userProvidedBadType.name.slowToString());
+ sb.write(userProvidedBadType.name.slowToString());
} else {
- sb.add(element.name.slowToString());
+ sb.write(element.name.slowToString());
}
if (!typeArguments.isEmpty) {
- sb.add('<');
+ sb.write('<');
typeArguments.printOn(sb, ', ');
- sb.add('>');
+ sb.write('>');
}
} else {
- sb.add(userProvidedBadType.toString());
+ sb.write(userProvidedBadType.toString());
}
return sb.toString();
}
@@ -334,11 +360,11 @@
String toString() {
StringBuffer sb = new StringBuffer();
- sb.add(name.slowToString());
+ sb.write(name.slowToString());
if (!isRaw) {
- sb.add('<');
+ sb.write('<');
typeArguments.printOn(sb, ', ');
- sb.add('>');
+ sb.write('>');
}
return sb.toString();
}
@@ -372,8 +398,17 @@
[Link<DartType> typeArguments = const Link<DartType>()])
: super(typeArguments, hasMalformed(typeArguments)) {
assert(invariant(element, element.isDeclaration));
+ assert(invariant(element, element.thisType == null ||
+ typeArguments.slowLength() == element.typeVariables.slowLength(),
+ message: 'Invalid type argument count on ${element.thisType}. '
+ 'Provided type arguments: $typeArguments.'));
}
+ InterfaceType.userProvidedBadType(this.element,
+ [Link<DartType> typeArguments =
+ const Link<DartType>()])
+ : super(typeArguments, true);
+
TypeKind get kind => TypeKind.INTERFACE;
SourceString get name => element.name;
@@ -402,12 +437,50 @@
DartType unalias(Compiler compiler) => this;
+ /**
+ * Finds the method, field or property named [name] declared or inherited
+ * on this interface type.
+ */
+ Member lookupMember(SourceString name) {
+
+ Member createMember(InterfaceType receiver, InterfaceType declarer,
+ Element member) {
+ if (member.kind == ElementKind.FUNCTION ||
+ member.kind == ElementKind.ABSTRACT_FIELD ||
+ member.kind == ElementKind.FIELD) {
+ return new Member(receiver, declarer, member);
+ }
+ return null;
+ }
+
+ ClassElement classElement = element;
+ InterfaceType receiver = this;
+ InterfaceType declarer = receiver;
+ Element member = classElement.lookupLocalMember(name);
+ if (member != null) {
+ return createMember(receiver, declarer, member);
+ }
+ for (DartType supertype in classElement.allSupertypes) {
+ declarer = supertype;
+ ClassElement lookupTarget = declarer.element;
+ member = lookupTarget.lookupLocalMember(name);
+ if (member != null) {
+ return createMember(receiver, declarer, member);
+ }
+ }
+ return null;
+ }
+
bool operator ==(other) {
if (other is !InterfaceType) return false;
return super == other;
}
InterfaceType asRaw() => super.asRaw();
+
+ accept(DartTypeVisitor visitor, var argument) {
+ return visitor.visitInterfaceType(this, argument);
+ }
}
class FunctionType extends DartType {
@@ -535,42 +608,46 @@
DartType unalias(Compiler compiler) => this;
+ accept(DartTypeVisitor visitor, var argument) {
+ return visitor.visitFunctionType(this, argument);
+ }
+
String toString() {
StringBuffer sb = new StringBuffer();
- sb.add('(');
+ sb.write('(');
parameterTypes.printOn(sb, ', ');
bool first = parameterTypes.isEmpty;
if (!optionalParameterTypes.isEmpty) {
if (!first) {
- sb.add(', ');
+ sb.write(', ');
}
- sb.add('[');
+ sb.write('[');
optionalParameterTypes.printOn(sb, ', ');
- sb.add(']');
+ sb.write(']');
first = false;
}
if (!namedParameterTypes.isEmpty) {
if (!first) {
- sb.add(', ');
+ sb.write(', ');
}
- sb.add('{');
+ sb.write('{');
Link<SourceString> namedParameter = namedParameters;
Link<DartType> namedParameterType = namedParameterTypes;
first = true;
while (!namedParameter.isEmpty && !namedParameterType.isEmpty) {
if (!first) {
- sb.add(', ');
+ sb.write(', ');
}
- sb.add(namedParameterType.head);
- sb.add(' ');
- sb.add(namedParameter.head.slowToString());
+ sb.write(namedParameterType.head);
+ sb.write(' ');
+ sb.write(namedParameter.head.slowToString());
namedParameter = namedParameter.tail;
namedParameterType = namedParameterType.tail;
first = false;
}
- sb.add('}');
+ sb.write('}');
}
- sb.add(') -> ${returnType}');
+ sb.write(') -> ${returnType}');
return sb.toString();
}
@@ -612,6 +689,8 @@
class TypedefType extends GenericType {
final TypedefElement element;
+ // TODO(johnniwinther): Assert that the number of arguments and parameters
+ // match, like for [InterfaceType].
TypedefType(this.element,
[Link<DartType> typeArguments = const Link<DartType>()])
: super(typeArguments, hasMalformed(typeArguments));
@@ -620,6 +699,11 @@
return new TypedefType(element, newTypeArguments);
}
+ TypedefType.userProvidedBadType(this.element,
+ [Link<DartType> typeArguments =
+ const Link<DartType>()])
+ : super(typeArguments, true);
+
TypeKind get kind => TypeKind.TYPEDEF;
SourceString get name => element.name;
@@ -638,6 +722,10 @@
}
TypedefType asRaw() => super.asRaw();
+
+ accept(DartTypeVisitor visitor, var argument) {
+ return visitor.visitTypedefType(this, argument);
+ }
}
/**
@@ -648,25 +736,117 @@
DynamicType(ClassElement element) : super(element);
SourceString get name => const SourceString('dynamic');
+
+ accept(DartTypeVisitor visitor, var argument) {
+ return visitor.visitDynamicType(this, argument);
+ }
}
-class Types {
- final Compiler compiler;
- // TODO(karlklose): should we have a class Void?
- final VoidType voidType;
- final DynamicType dynamicType;
+/**
+ * Member encapsulates a member (method, field, property) with the types of the
+ * declarer and receiver in order to do substitution on the member type.
+ *
+ * Consider for instance these classes and the variable [: B<String> b :]:
+ *
+ * class A<E> {
+ * E field;
+ * }
+ * class B<F> extends A<F> {}
+ *
+ * In a [Member] for [: b.field :] the [receiver] is the type [: B<String> :]
+ * and the declarer is the type [: A<F> :], which is the supertype of [: B<F> :]
+ * from which [: field :] has been inherited. To compute the type of
+ * [: b.field :] we must first substitute [: E :] by [: F :] using the relation
+ * between [: A<E> :] and [: A<F> :], and then [: F :] by [: String :] using the
+ * relation between [: B<F> :] and [: B<String> :].
+ */
+class Member {
+ final InterfaceType receiver;
+ final InterfaceType declarer;
+ final Element element;
+ DartType cachedType;
- factory Types(Compiler compiler, ClassElement dynamicElement) {
- LibraryElement library = new LibraryElementX(new Script(null, null));
- VoidType voidType = new VoidType(new VoidElementX(library));
- DynamicType dynamicType = new DynamicType(dynamicElement);
- dynamicElement.rawType = dynamicElement.thisType = dynamicType;
- return new Types.internal(compiler, voidType, dynamicType);
+ Member(this.receiver, this.declarer, this.element) {
+ assert(invariant(element, element.isAbstractField() ||
+ element.isField() ||
+ element.isFunction(),
+ message: "Unsupported Member element: $element"));
}
- Types.internal(this.compiler, this.voidType, this.dynamicType);
+ DartType computeType(Compiler compiler) {
+ if (cachedType == null) {
+ DartType type;
+ if (element.isAbstractField()) {
+ AbstractFieldElement abstractFieldElement = element;
+ if (abstractFieldElement.getter != null) {
+ FunctionType functionType =
+ abstractFieldElement.getter.computeType(compiler);
+ type = functionType.returnType;
+ } else {
+ FunctionType functionType =
+ abstractFieldElement.setter.computeType(
+ compiler);
+ type = functionType.parameterTypes.head;
+ if (type == null) {
+ type = compiler.types.dynamicType;
+ }
+ }
+ } else {
+ type = element.computeType(compiler);
+ }
+ if (!declarer.element.typeVariables.isEmpty) {
+ type = type.subst(declarer.typeArguments,
+ declarer.element.typeVariables);
+ type = type.subst(receiver.typeArguments,
+ receiver.element.typeVariables);
+ }
+ cachedType = type;
+ }
+ return cachedType;
+ }
- /** Returns true if t is a subtype of s */
+ String toString() {
+ return '$receiver.${element.name.slowToString()}';
+ }
+}
+
+abstract class DartTypeVisitor<R, A> {
+ R visitType(DartType type, A argument);
+
+ R visitVoidType(VoidType type, A argument) =>
+ visitType(type, argument);
+
+ R visitTypeVariableType(TypeVariableType type, A argument) =>
+ visitType(type, argument);
+
+ R visitFunctionType(FunctionType type, A argument) =>
+ visitType(type, argument);
+
+ R visitMalformedType(MalformedType type, A argument) =>
+ visitType(type, argument);
+
+ R visitStatementType(StatementType type, A argument) =>
+ visitType(type, argument);
+
+ R visitGenericType(GenericType type, A argument) =>
+ visitType(type, argument);
+
+ R visitInterfaceType(InterfaceType type, A argument) =>
+ visitGenericType(type, argument);
+
+ R visitTypedefType(TypedefType type, A argument) =>
+ visitGenericType(type, argument);
+
+ R visitDynamicType(DynamicType type, A argument) =>
+ visitInterfaceType(type, argument);
+}
+
+class SubtypeVisitor extends DartTypeVisitor<bool, DartType> {
+ final Compiler compiler;
+ final DynamicType dynamicType;
+
+ SubtypeVisitor(Compiler this.compiler, DynamicType this.dynamicType);
+
bool isSubtype(DartType t, DartType s) {
if (identical(t, s) ||
identical(t, dynamicType) ||
@@ -680,84 +860,138 @@
t = t.unalias(compiler);
s = s.unalias(compiler);
- if (t is VoidType) {
- return false;
- } else if (t is InterfaceType) {
- if (s is !InterfaceType) return false;
- ClassElement tc = t.element;
- if (identical(tc, s.element)) return true;
- for (Link<DartType> supertypes = tc.allSupertypes;
- supertypes != null && !supertypes.isEmpty;
- supertypes = supertypes.tail) {
- DartType supertype = supertypes.head;
- if (identical(supertype.element, s.element)) return true;
- }
- return false;
- } else if (t is FunctionType) {
- if (identical(s.element, compiler.functionClass)) return true;
- if (s is !FunctionType) return false;
- FunctionType tf = t;
- FunctionType sf = s;
- Link<DartType> tps = tf.parameterTypes;
- Link<DartType> sps = sf.parameterTypes;
- while (!tps.isEmpty && !sps.isEmpty) {
- if (!isAssignable(tps.head, sps.head)) return false;
- tps = tps.tail;
- sps = sps.tail;
- }
- if (!tps.isEmpty || !sps.isEmpty) return false;
- if (!isAssignable(sf.returnType, tf.returnType)) return false;
- if (!sf.namedParameters.isEmpty) {
- // Since named parameters are globally ordered we can determine the
- // subset relation with a linear search for [:sf.NamedParameters:]
- // within [:tf.NamedParameters:].
- Link<SourceString> tNames = tf.namedParameters;
- Link<DartType> tTypes = tf.namedParameterTypes;
- Link<SourceString> sNames = sf.namedParameters;
- Link<DartType> sTypes = sf.namedParameterTypes;
- while (!tNames.isEmpty && !sNames.isEmpty) {
- if (sNames.head == tNames.head) {
- if (!isAssignable(tTypes.head, sTypes.head)) return false;
+ return t.accept(this, s);
+ }
- sNames = sNames.tail;
- sTypes = sTypes.tail;
- }
- tNames = tNames.tail;
- tTypes = tTypes.tail;
- }
- if (!sNames.isEmpty) {
- // We didn't find all names.
+ bool isAssignable(DartType t, DartType s) {
+ return isSubtype(t, s) || isSubtype(s, t);
+ }
+
+ bool visitType(DartType t, DartType s) {
+ throw 'internal error: unknown type kind ${t.kind}';
+ }
+
+ bool visitVoidType(VoidType t, DartType s) {
+ assert(s is! VoidType);
+ return false;
+ }
+
+ bool visitInterfaceType(InterfaceType t, DartType s) {
+ if (s is !InterfaceType) return false;
+
+ bool checkTypeArguments(InterfaceType instance) {
+ Link<DartType> tTypeArgs = instance.typeArguments;
+ Link<DartType> sTypeArgs = s.typeArguments;
+ while (!tTypeArgs.isEmpty) {
+ assert(!sTypeArgs.isEmpty);
+ if (!isSubtype(tTypeArgs.head, sTypeArgs.head)) {
return false;
}
+ tTypeArgs = tTypeArgs.tail;
+ sTypeArgs = sTypeArgs.tail;
}
- if (!sf.optionalParameterTypes.isEmpty) {
- Link<DartType> tOptionalParameterType = tf.optionalParameterTypes;
- Link<DartType> sOptionalParameterType = sf.optionalParameterTypes;
- while (!tOptionalParameterType.isEmpty &&
- !sOptionalParameterType.isEmpty) {
- if (!isAssignable(tOptionalParameterType.head,
- sOptionalParameterType.head)) {
- return false;
- }
- sOptionalParameterType = sOptionalParameterType.tail;
- tOptionalParameterType = tOptionalParameterType.tail;
- }
- if (!sOptionalParameterType.isEmpty) {
- // We didn't find enough optional parameters.
- return false;
- }
- }
+ assert(sTypeArgs.isEmpty);
return true;
- } else if (t is TypeVariableType) {
- if (s is !TypeVariableType) return false;
- return (identical(t.element, s.element));
- } else {
- throw 'internal error: unknown type kind';
}
+
+ // TODO(johnniwinther): Currently needed since literal types like int,
+ // double, bool etc. might not have been resolved yet.
+ t.element.ensureResolved(compiler);
+
+ InterfaceType instance = t.asInstanceOf(s.element);
+ return instance != null && checkTypeArguments(instance);
+ }
+
+ bool visitFunctionType(FunctionType t, DartType s) {
+ if (identical(s.element, compiler.functionClass)) return true;
+ if (s is !FunctionType) return false;
+ FunctionType tf = t;
+ FunctionType sf = s;
+ Link<DartType> tps = tf.parameterTypes;
+ Link<DartType> sps = sf.parameterTypes;
+ while (!tps.isEmpty && !sps.isEmpty) {
+ if (!isAssignable(tps.head, sps.head)) return false;
+ tps = tps.tail;
+ sps = sps.tail;
+ }
+ if (!tps.isEmpty || !sps.isEmpty) return false;
+ // TODO(johnniwinther): Handle the void type correctly.
+ if (!isAssignable(tf.returnType, sf.returnType)) return false;
+ if (!sf.namedParameters.isEmpty) {
+ // Since named parameters are globally ordered we can determine the
+ // subset relation with a linear search for [:sf.NamedParameters:]
+ // within [:tf.NamedParameters:].
+ Link<SourceString> tNames = tf.namedParameters;
+ Link<DartType> tTypes = tf.namedParameterTypes;
+ Link<SourceString> sNames = sf.namedParameters;
+ Link<DartType> sTypes = sf.namedParameterTypes;
+ while (!tNames.isEmpty && !sNames.isEmpty) {
+ if (sNames.head == tNames.head) {
+ if (!isAssignable(tTypes.head, sTypes.head)) return false;
+
+ sNames = sNames.tail;
+ sTypes = sTypes.tail;
+ }
+ tNames = tNames.tail;
+ tTypes = tTypes.tail;
+ }
+ if (!sNames.isEmpty) {
+ // We didn't find all names.
+ return false;
+ }
+ }
+ if (!sf.optionalParameterTypes.isEmpty) {
+ Link<DartType> tOptionalParameterType = tf.optionalParameterTypes;
+ Link<DartType> sOptionalParameterType = sf.optionalParameterTypes;
+ while (!tOptionalParameterType.isEmpty &&
+ !sOptionalParameterType.isEmpty) {
+ if (!isAssignable(tOptionalParameterType.head,
+ sOptionalParameterType.head)) {
+ return false;
+ }
+ sOptionalParameterType = sOptionalParameterType.tail;
+ tOptionalParameterType = tOptionalParameterType.tail;
+ }
+ if (!sOptionalParameterType.isEmpty) {
+ // We didn't find enough optional parameters.
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool visitTypeVariableType(TypeVariableType t, DartType s) {
+ if (s is !TypeVariableType) return false;
+ return (identical(t.element, s.element));
+ }
+}
+
+class Types {
+ final Compiler compiler;
+ // TODO(karlklose): should we have a class Void?
+ final VoidType voidType;
+ final DynamicType dynamicType;
+ final SubtypeVisitor subtypeVisitor;
+
+ factory Types(Compiler compiler, ClassElement dynamicElement) {
+ LibraryElement library = new LibraryElementX(new Script(null, null));
+ VoidType voidType = new VoidType(new VoidElementX(library));
+ DynamicType dynamicType = new DynamicType(dynamicElement);
+ dynamicElement.rawType = dynamicElement.thisType = dynamicType;
+ SubtypeVisitor subtypeVisitor = new SubtypeVisitor(compiler, dynamicType);
+ return new Types.internal(compiler, voidType, dynamicType, subtypeVisitor);
+ }
+
+ Types.internal(this.compiler, this.voidType, this.dynamicType,
+ this.subtypeVisitor);
+
+ /** Returns true if t is a subtype of s */
+ bool isSubtype(DartType t, DartType s) {
+ return subtypeVisitor.isSubtype(t, s);
}
bool isAssignable(DartType r, DartType s) {
- return isSubtype(r, s) || isSubtype(s, r);
+ return subtypeVisitor.isAssignable(r, s);
}
diff --git a/sdk/lib/_internal/compiler/implementation/deferred_load.dart b/sdk/lib/_internal/compiler/implementation/deferred_load.dart
new file mode 100644
index 0000000..c623bd8
--- /dev/null
+++ b/sdk/lib/_internal/compiler/implementation/deferred_load.dart
@@ -0,0 +1,106 @@
+// 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 deferred_load;
+
+import 'dart:uri';
+
+import 'dart2jslib.dart'
+ show Compiler,
+ CompilerTask,
+ ConstructedConstant,
+ MessageKind,
+ SourceString,
+ StringConstant;
+
+import 'elements/elements.dart'
+ show ClassElement,
+ Element,
+ LibraryElement,
+ MetadataAnnotation;
+
+import 'util/util.dart'
+ show Link;
+
+import 'tree/tree.dart'
+ show LibraryTag;
+
+class DeferredLoadTask extends CompilerTask {
+ final Set<LibraryElement> deferredLibraries = new Set<LibraryElement>();
+
+ ClassElement cachedDeferredLibraryClass;
+
+ DeferredLoadTask(Compiler compiler) : super(compiler);
+
+ String get name => 'Lazy';
+
+ /// DeferredLibrary from dart:async
+ ClassElement get deferredLibraryClass {
+ if (cachedDeferredLibraryClass == null) {
+ cachedDeferredLibraryClass = findDeferredLibraryClass();
+ }
+ return cachedDeferredLibraryClass;
+ }
+
+ ClassElement findDeferredLibraryClass() {
+ var uri = new Uri.fromComponents(scheme: 'dart', path: 'async');
+ LibraryElement asyncLibrary =
+ compiler.libraryLoader.loadLibrary(uri, null, uri);
+ var element = asyncLibrary.find(const SourceString('DeferredLibrary'));
+ if (element == null) {
+ compiler.internalErrorOnElement(
+ asyncLibrary,
+ 'dart:async library does not contain required class: '
+ 'DeferredLibrary');
+ }
+ return element;
+ }
+
+ bool isDeferred(Element element) {
+ // TODO(ahe): This is really a graph coloring problem. We should
+ // make sure that libraries and elements only used by a deferred
+ // library are also deferred.
+ // Also, if something is deferred depends on your
+ // perspective. Inside a deferred library, other elements of the
+ // same library are not deferred. We should add an extra parameter
+ // to this method to indicate "from where".
+ return deferredLibraries.contains(element.getLibrary());
+ }
+
+ void registerMainApp(LibraryElement mainApp) {
+ if (mainApp == null) return;
+ measureElement(mainApp, () {
+ deferredLibraries.addAll(findDeferredLibraries(mainApp));
+ });
+ }
+
+ Link<LibraryElement> findDeferredLibraries(LibraryElement library) {
+ Link<LibraryElement> link = const Link<LibraryElement>();
+ for (LibraryTag tag in library.tags) {
+ Link<MetadataAnnotation> metadata = tag.metadata;
+ if (metadata == null) continue;
+ for (MetadataAnnotation metadata in tag.metadata) {
+ metadata.ensureResolved(compiler);
+ Element element = metadata.value.computeType(compiler).element;
+ if (element == deferredLibraryClass) {
+ ConstructedConstant value = metadata.value;
+ StringConstant nameField = value.fields[0];
+ SourceString expectedName = nameField.toDartString().source;
+ LibraryElement deferredLibrary = library.getLibraryFromTag(tag);
+ link = link.prepend(deferredLibrary);
+ SourceString actualName =
+ new SourceString(deferredLibrary.getLibraryOrScriptName());
+ if (expectedName != actualName) {
+ compiler.reportErrorCode(
+ metadata,
+ MessageKind.DEFERRED_LIBRARY_NAME_MISMATCH,
+ { 'expectedName': expectedName.slowToString(),
+ 'actualName': actualName.slowToString()});
+ }
+ }
+ }
+ }
+ return link;
+ }
+}
diff --git a/sdk/lib/_internal/compiler/implementation/elements/elements.dart b/sdk/lib/_internal/compiler/implementation/elements/elements.dart
index 312b35b..6432679 100644
--- a/sdk/lib/_internal/compiler/implementation/elements/elements.dart
+++ b/sdk/lib/_internal/compiler/implementation/elements/elements.dart
@@ -553,6 +553,13 @@
void addTag(LibraryTag tag, DiagnosticListener listener);
void addImport(Element element, DiagnosticListener listener);
+ /// Record which element an import or export tag resolved to.
+ /// (Belongs on builder object).
+ void recordResolvedTag(LibraryDependency tag, LibraryElement library);
+
+ /// Return the library element corresponding to an import or export.
+ LibraryElement getLibraryFromTag(LibraryDependency tag);
+
void addMember(Element element, DiagnosticListener listener);
void addToScope(Element element, DiagnosticListener listener);
diff --git a/sdk/lib/_internal/compiler/implementation/elements/modelx.dart b/sdk/lib/_internal/compiler/implementation/elements/modelx.dart
index ce9984d..00bcbd2 100644
--- a/sdk/lib/_internal/compiler/implementation/elements/modelx.dart
+++ b/sdk/lib/_internal/compiler/implementation/elements/modelx.dart
@@ -5,6 +5,7 @@
library elements.modelx;
import 'dart:uri';
+import 'dart:collection' show LinkedHashMap;
import 'elements.dart';
import '../../compiler.dart' as api;
@@ -564,6 +565,9 @@
*/
Link<Element> slotForExports;
+ final Map<LibraryDependency, LibraryElement> tagMapping =
+ new LinkedHashMap<LibraryDependency, LibraryElement>();
+
LibraryElementX(Script script, [Uri canonicalUri, LibraryElement this.origin])
: this.canonicalUri = ((canonicalUri == null) ? script.uri : canonicalUri),
importScope = new Map<SourceString, Element>(),
@@ -590,6 +594,13 @@
tags = tags.prepend(tag);
}
+ void recordResolvedTag(LibraryDependency tag, LibraryElement library) {
+ assert(tagMapping[tag] == null);
+ tagMapping[tag] = library;
+ }
+
+ LibraryElement getLibraryFromTag(LibraryDependency tag) => tagMapping[tag];
+
/**
* Adds [element] to the import scope of this library.
*
diff --git a/sdk/lib/_internal/compiler/implementation/enqueue.dart b/sdk/lib/_internal/compiler/implementation/enqueue.dart
index 5ea86e3..e22fb11 100644
--- a/sdk/lib/_internal/compiler/implementation/enqueue.dart
+++ b/sdk/lib/_internal/compiler/implementation/enqueue.dart
@@ -67,6 +67,7 @@
// runtime type.
if (element.isGetter() && element.name == Compiler.RUNTIME_TYPE) {
compiler.enabledRuntimeType = true;
+ compiler.backend.registerRuntimeType();
} else if (element == compiler.functionApplyMethod) {
compiler.enabledFunctionApply = true;
} else if (element == compiler.invokeOnMethod) {
@@ -89,8 +90,8 @@
if (universe.instantiatedClasses.contains(cls)) return;
if (!cls.isAbstract(compiler)) {
universe.instantiatedClasses.add(cls);
- onRegisterInstantiatedClass(cls);
}
+ onRegisterInstantiatedClass(cls);
compiler.backend.registerInstantiatedClass(cls, this);
}
@@ -120,9 +121,12 @@
if (isProcessed(member)) return;
if (!member.isInstanceMember()) return;
if (member.isField()) {
- // Native fields need to go into instanceMembersByName as they are virtual
- // instantiation points and escape points.
- // Test the enclosing class, since the metadata has not been parsed yet.
+ // Fields are implicitly used by the constructor of the
+ // instantiated class they are part of.
+ compiler.world.registerUsedElement(member);
+ // Native fields need to go into instanceMembersByName as they
+ // are virtual instantiation points and escape points. Test the
+ // enclosing class, since the metadata has not been parsed yet.
if (!member.enclosingElement.isNative()) return;
}
@@ -182,7 +186,7 @@
}
// The element is not yet used. Add it to the list of instance
- // members to still be processed.
+ // members to still be processed.
Link<Element> members = instanceMembersByName.putIfAbsent(
memberName, () => const Link<Element>());
instanceMembersByName[memberName] = members.prepend(member);
@@ -205,22 +209,6 @@
if (isResolutionQueue) {
compiler.resolver.checkClass(cls);
}
-
- if (compiler.enableTypeAssertions) {
- // We need to register is checks and helpers for checking
- // assignments to fields.
- // TODO(ngeoffray): This should really move to the backend.
- cls.forEachLocalMember((Element member) {
- if (!member.isInstanceMember() || !member.isField()) return;
- DartType type = member.computeType(compiler);
- registerIsCheck(type);
- SourceString helper = compiler.backend.getCheckedModeHelper(type);
- if (helper != null) {
- Element helperElement = compiler.findHelper(helper);
- registerStaticUse(helperElement);
- }
- });
- }
}
processClass(cls);
for (Link<DartType> supertypes = cls.allSupertypes;
@@ -364,6 +352,12 @@
void registerIsCheck(DartType type) {
universe.isChecks.add(type);
+ compiler.backend.registerIsCheck(type, this);
+ }
+
+ void registerAsCheck(DartType type) {
+ registerIsCheck(type);
+ compiler.backend.registerAsCheck(type);
}
void forEach(f(WorkItem work));
@@ -473,11 +467,8 @@
void enableNoSuchMethod(Element element) {
if (compiler.enabledNoSuchMethod) return;
+ if (element.getEnclosingClass() == compiler.objectClass) return;
Selector selector = new Selector.noSuchMethod();
- if (identical(element.getEnclosingClass(), compiler.objectClass)) {
- registerDynamicInvocationOf(element, selector);
- return;
- }
compiler.enabledNoSuchMethod = true;
registerInvocation(Compiler.NO_SUCH_METHOD, selector);
@@ -513,7 +504,8 @@
: super('codegen enqueuer', compiler, itemCompilationContextCreator),
queue = new Queue<CodegenWorkItem>();
- bool isProcessed(Element member) => generatedCode.containsKey(member);
+ bool isProcessed(Element member) =>
+ member.isAbstract(compiler) || generatedCode.containsKey(member);
bool addElementToWorkList(Element element, [TreeElements elements]) {
if (queueIsClosed) {
diff --git a/sdk/lib/_internal/compiler/implementation/js/nodes.dart b/sdk/lib/_internal/compiler/implementation/js/nodes.dart
index da8cd44..ccf91b9 100644
--- a/sdk/lib/_internal/compiler/implementation/js/nodes.dart
+++ b/sdk/lib/_internal/compiler/implementation/js/nodes.dart
@@ -59,6 +59,8 @@
T visitObjectInitializer(ObjectInitializer node);
T visitProperty(Property node);
T visitRegExpLiteral(RegExpLiteral node);
+
+ T visitComment(Comment node);
}
class BaseVisitor<T> implements NodeVisitor<T> {
@@ -141,6 +143,9 @@
T visitObjectInitializer(ObjectInitializer node) => visitExpression(node);
T visitProperty(Property node) => visitNode(node);
T visitRegExpLiteral(RegExpLiteral node) => visitExpression(node);
+
+ // Ignore comments by default.
+ T visitComment(Comment node) {}
}
abstract class Node {
@@ -151,6 +156,10 @@
void visitChildren(NodeVisitor visitor);
VariableUse asVariableUse() => null;
+
+ Statement toStatement() {
+ throw new UnsupportedError('toStatement');
+ }
}
class Program extends Node {
@@ -164,6 +173,7 @@
}
abstract class Statement extends Node {
+ Statement toStatement() => this;
}
class Block extends Statement {
@@ -432,8 +442,71 @@
abstract class Expression extends Node {
int get precedenceLevel;
- PropertyAccess dot(String name) => new PropertyAccess.field(this, name);
Call callWith(List<Expression> arguments) => new Call(this, arguments);
+
+ New newWith(List<Expression> arguments) => new New(this, arguments);
+
+ PropertyAccess operator [](expression) {
+ if (expression is Expression) {
+ return new PropertyAccess(this, expression);
+ } else if (expression is int) {
+ return new PropertyAccess.indexed(this, expression);
+ } else if (expression is String) {
+ return new PropertyAccess.field(this, expression);
+ } else {
+ throw new ArgumentError('Expected an int, String, or Expression');
+ }
+ }
+
+ Statement toStatement() => new ExpressionStatement(this);
+
+ Call call([expression]) {
+ List<Expression> arguments;
+ if (expression == null) {
+ arguments = <Expression>[];
+ } else if (expression is List) {
+ arguments = expression.map(js.toExpression).toList();
+ } else {
+ arguments = <Expression>[js.toExpression(expression)];
+ }
+ return callWith(arguments);
+ }
+
+ Expression equals(expression) => binary('==', expression);
+
+ Expression strictEquals(expression) => binary('===', expression);
+
+ Expression notEquals(expression) => binary('!=', expression);
+
+ Expression operator +(expression) => binary('+', expression);
+
+ Expression operator -(expression) => binary('-', expression);
+
+ Expression operator &(expression) => binary('&', expression);
+
+ Expression operator <(expression) => binary('<', expression);
+
+ Expression operator >(expression) => binary('>', expression);
+
+ Expression operator >=(expression) => binary('>=', expression);
+
+ Expression binary(String operator, expression) {
+ return new Binary(operator, this, js.toExpression(expression));
+ }
+
+ Expression assign(expression) {
+ return new Assignment(this, js.toExpression(expression));
+ }
+
+ Expression update(String operator, expression) {
+ return new Assignment.compound(this, operator, js.toExpression(expression));
+ }
+
+ Expression get plusPlus => new Postfix('++', this);
+
+ Prefix get typeof => new Prefix('typeof', this);
+
+ Prefix get not => new Prefix('!', this);
}
class LiteralExpression extends Expression {
@@ -798,8 +871,8 @@
}
/**
- * An expression inside an [ArrayInitialization]. An [ArrayElement] knows
- * its position in the containing [ArrayInitialization].
+ * An expression inside an [ArrayInitializer]. An [ArrayElement] knows
+ * its position in the containing [ArrayInitializer].
*/
class ArrayElement extends Node {
int index;
@@ -859,48 +932,562 @@
int get precedenceLevel => PRIMARY;
}
-Prefix typeOf(Expression argument) => new Prefix('typeof', argument);
+/**
+ * A comment.
+ *
+ * Extends [Statement] so we can add comments before statements in
+ * [Block] and [Program].
+ */
+class Comment extends Statement {
+ final String comment;
-Binary equals(Expression left, Expression right) {
- return new Binary('==', left, right);
+ Comment(this.comment);
+
+ accept(NodeVisitor visitor) => visitor.visitComment(this);
+
+ void visitChildren(NodeVisitor visitor) {}
}
-Binary strictEquals(Expression left, Expression right) {
- return new Binary('===', left, right);
+class JsBuilder {
+ const JsBuilder();
+
+ Expression operator [](String source) {
+ return new MiniJsParser(source).expression();
+ }
+
+ // TODO(ahe): Remove this method.
+ Binary equals(Expression left, Expression right) {
+ return new Binary('==', left, right);
+ }
+
+ // TODO(ahe): Remove this method.
+ Binary strictEquals(Expression left, Expression right) {
+ return new Binary('===', left, right);
+ }
+
+ LiteralString string(String value) => new LiteralString('"$value"');
+
+ If if_(condition, thenPart, [elsePart]) {
+ condition = toExpression(condition);
+ return (elsePart == null)
+ ? new If.noElse(condition, toStatement(thenPart))
+ : new If(condition, toStatement(thenPart), toStatement(elsePart));
+ }
+
+ Return return_([value]) {
+ return new Return(value == null ? null : toExpression(value));
+ }
+
+ Block block(statement) {
+ if (statement is Block) {
+ return statement;
+ } else if (statement is List) {
+ return new Block(statement.map(toStatement).toList());
+ } else {
+ return new Block(<Statement>[toStatement(statement)]);
+ }
+ }
+
+ Fun fun(parameters, body) {
+ Parameter toParameter(parameter) {
+ if (parameter is String) {
+ return new Parameter(parameter);
+ } else if (parameter is Parameter) {
+ return parameter;
+ } else {
+ throw new ArgumentError('parameter should be a String or a Parameter');
+ }
+ }
+ if (parameters is! List) {
+ parameters = [parameters];
+ }
+ return new Fun(parameters.map(toParameter).toList(), block(body));
+ }
+
+ Assignment assign(Expression leftHandSide, Expression value) {
+ return new Assignment(leftHandSide, value);
+ }
+
+ Expression undefined() => new Prefix('void', new LiteralNumber('0'));
+
+ VariableDeclarationList defineVar(String name, [initializer]) {
+ if (initializer != null) {
+ initializer = toExpression(initializer);
+ }
+ var declaration = new VariableDeclaration(name);
+ var initialization = [new VariableInitialization(declaration, initializer)];
+ return new VariableDeclarationList(initialization);
+ }
+
+ Statement toStatement(statement) {
+ if (statement is List) {
+ return new Block(statement.map(toStatement).toList());
+ } else if (statement is Node) {
+ return statement.toStatement();
+ } else {
+ throw new ArgumentError('statement');
+ }
+ }
+
+ Expression toExpression(expression) {
+ if (expression is Expression) {
+ return expression;
+ } else if (expression is String) {
+ return this[expression];
+ } else if (expression is num) {
+ return new LiteralNumber('$expression');
+ } else if (expression is bool) {
+ return new LiteralBool(expression);
+ } else if (expression is Map) {
+ if (!expression.isEmpty) {
+ throw new ArgumentError('expression should be an empty Map');
+ }
+ return new ObjectInitializer([]);
+ } else {
+ throw new ArgumentError('expression should be an Expression, '
+ 'a String, a num, a bool, or a Map');
+ }
+ }
+
+ ForIn forIn(String name, object, statement) {
+ return new ForIn(defineVar(name),
+ toExpression(object),
+ toStatement(statement));
+ }
+
+ For for_(init, condition, update, statement) {
+ return new For(
+ toExpression(init), toExpression(condition), toExpression(update),
+ toStatement(statement));
+ }
+
+ Try try_(body, {catchPart, finallyPart}) {
+ if (catchPart != null) catchPart = toStatement(catchPart);
+ if (finallyPart != null) finallyPart = toStatement(finallyPart);
+ return new Try(toStatement(body), catchPart, finallyPart);
+ }
+
+ Comment comment(String text) => new Comment(text);
}
-LiteralString string(String value) => new LiteralString('"$value"');
+const JsBuilder js = const JsBuilder();
-If if_(Expression condition, Node then, [Node otherwise]) {
- return (otherwise == null)
- ? new If.noElse(condition, then)
- : new If(condition, then, otherwise);
+LiteralString string(String value) => js.string(value);
+
+class MiniJsParserError {
+ MiniJsParserError(this.parser, this.message) { }
+
+ MiniJsParser parser;
+ String message;
+
+ String toString() {
+ var codes =
+ new List.fixedLength(parser.lastPosition, fill: charCodes.$SPACE);
+ var spaces = new String.fromCharCodes(codes);
+ return "Error in MiniJsParser:\n${parser.src}\n$spaces^\n$spaces$message\n";
+ }
}
-Return return_([Expression value]) => new Return(value);
+/// Mini JavaScript parser for tiny snippets of code that we want to make into
+/// AST nodes. Handles:
+/// * identifiers.
+/// * dot access.
+/// * method calls.
+/// * [] access.
+/// * array, string, boolean, null and numeric literals (no hex).
+/// * most operators.
+/// * brackets.
+/// * var declarations.
+/// Notable things it can't do yet include:
+/// * operator precedence.
+/// * non-empty object literals.
+/// * throw, return.
+/// * statements, including any flow control (if, while, for, etc.)
+/// * the 'in' keyword.
+///
+/// It's a fairly standard recursive descent parser.
+///
+/// Literal strings are passed through to the final JS source code unchanged,
+/// including the choice of surrounding quotes, so if you parse
+/// r'var x = "foo\n\"bar\""' you will end up with
+/// var x = "foo\n\"bar\"" in the final program. String literals are
+/// restricted to a small subset of the full set of allowed JS escapes in order
+/// to get early errors for unintentional escape sequences without complicating
+/// this parser unneccessarily.
+class MiniJsParser {
+ MiniJsParser(this.src)
+ : lastCategory = NONE,
+ lastToken = null,
+ lastPosition = 0,
+ position = 0 {
+ getSymbol();
+ }
-VariableUse use(String name) => new VariableUse(name);
+ int lastCategory;
+ String lastToken;
+ int lastPosition;
+ int position;
+ String src;
-PropertyAccess fieldAccess(Expression receiver, String fieldName) {
- return new PropertyAccess.field(receiver, fieldName);
+ static const NONE = -1;
+ static const ALPHA = 0;
+ static const NUMERIC = 1;
+ static const STRING = 2;
+ static const SYMBOL = 3;
+ static const RELATION = 4;
+ static const DOT = 5;
+ static const LPAREN = 6;
+ static const RPAREN = 7;
+ static const LBRACE = 8;
+ static const RBRACE = 9;
+ static const LSQUARE = 10;
+ static const RSQUARE = 11;
+ static const COMMA = 12;
+ static const QUERY = 13;
+ static const COLON = 14;
+ static const OTHER = 15;
+
+ // Make sure that ]] is two symbols.
+ bool singleCharCategory(int category) => category >= DOT;
+
+ static String categoryToString(int cat) {
+ switch (cat) {
+ case NONE: return "NONE";
+ case ALPHA: return "ALPHA";
+ case NUMERIC: return "NUMERIC";
+ case SYMBOL: return "SYMBOL";
+ case RELATION: return "RELATION";
+ case DOT: return "DOT";
+ case LPAREN: return "LPAREN";
+ case RPAREN: return "RPAREN";
+ case LBRACE: return "LBRACE";
+ case RBRACE: return "RBRACE";
+ case RSQUARE: return "RSQUARE";
+ case STRING: return "STRING";
+ case COMMA: return "COMMA";
+ case QUERY: return "QUERY";
+ case COLON: return "COLON";
+ case OTHER: return "OTHER";
+ }
+ return "Unknown: $cat";
+ }
+
+ static const CATEGORIES = const <int>[
+ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 0-7
+ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 8-15
+ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 16-23
+ OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 24-31
+ OTHER, RELATION, OTHER, OTHER, ALPHA, SYMBOL, SYMBOL, OTHER, // !"#$%&´
+ LPAREN, RPAREN, SYMBOL, SYMBOL, COMMA, SYMBOL, DOT, SYMBOL, // ()*+,-./
+ NUMERIC, NUMERIC, NUMERIC, NUMERIC, NUMERIC, // 01234
+ NUMERIC, NUMERIC, NUMERIC, NUMERIC, NUMERIC, // 56789
+ COLON, OTHER, RELATION, RELATION, RELATION, 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
+ ALPHA, ALPHA, LSQUARE, OTHER, RSQUARE, SYMBOL, ALPHA, OTHER, // YZ[\]^_'
+ 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
+ ALPHA, ALPHA, LBRACE, SYMBOL, RBRACE, SYMBOL]; // yz{|}~
+
+ static final BINARY_OPERATORS = [
+ '+', '-', '*', '/', '%', '^', '|', '&', '||', '&&',
+ '<<', '>>', '+=', '-=', '*=', '/=', '^=', '|=', '&=', '<<=', '>>=',
+ '=', '!=', '==', '!==', '===', '<', '<=', '>=', '>'].toSet();
+ static final UNARY_OPERATORS = ['++', '--', '+', '-', '~', '!'].toSet();
+
+ // For sanity we only allow \\, \', \" and \n in string literals.
+ static final STRING_LITERAL_PATTERN =
+ new RegExp('^[\'"](?:[^\\\\]|\\\\[\\\\n\'"])*[\'"]\$');
+
+ static int category(int code) {
+ if (code >= CATEGORIES.length) return OTHER;
+ return CATEGORIES[code];
+ }
+
+ void getSymbol() {
+ while (position < src.length &&
+ src.codeUnitAt(position) == charCodes.$SPACE) {
+ position++;
+ }
+ if (position == src.length) {
+ lastCategory = NONE;
+ lastToken = null;
+ lastPosition = position;
+ return;
+ }
+ int code = src.codeUnitAt(position);
+ lastPosition = position;
+ if (code == charCodes.$SQ || code == charCodes.$DQ) {
+ int currentCode;
+ do {
+ position++;
+ if (position >= src.length) {
+ throw new MiniJsParserError(this, "Unterminated string");
+ }
+ currentCode = src.codeUnitAt(position);
+ if (currentCode == charCodes.$BACKSLASH) {
+ if (++position >= src.length) {
+ throw new MiniJsParserError(this, "Unterminated string");
+ }
+ }
+ } while (currentCode != code);
+ lastCategory = STRING;
+ position++;
+ lastToken = src.substring(lastPosition, position);
+ if (!STRING_LITERAL_PATTERN.hasMatch(lastToken)) {
+ throw new MiniJsParserError(
+ this,
+ "Only escapes allowed in string literals are \\, \', \" and \n");
+ }
+ } else {
+ int cat = category(src.codeUnitAt(position));
+ int newCat;
+ do {
+ position++;
+ if (position == src.length) break;
+ newCat = category(src.codeUnitAt(position));
+ } while (!singleCharCategory(cat) &&
+ (cat == newCat ||
+ (cat == ALPHA && newCat == NUMERIC) || // eg. level42.
+ (cat == NUMERIC && newCat == DOT) || // eg. 3.1415
+ (cat == SYMBOL && newCat == RELATION))); // eg. +=.
+ lastCategory = cat;
+ lastToken = src.substring(lastPosition, position);
+ if (cat == NUMERIC) {
+ double.parse(lastToken, (_) {
+ throw new MiniJsParserError(this, "Unparseable number");
+ });
+ } else if (cat == SYMBOL || cat == RELATION) {
+ if (!BINARY_OPERATORS.contains(lastToken) &&
+ !UNARY_OPERATORS.contains(lastToken)) {
+ throw new MiniJsParserError(this, "Unknown operator");
+ }
+ }
+ }
+ }
+
+ void expectCategory(int cat) {
+ if (cat != lastCategory) {
+ throw new MiniJsParserError(this, "Expected ${categoryToString(cat)}");
+ }
+ getSymbol();
+ }
+
+ bool acceptCategory(int cat) {
+ if (cat == lastCategory) {
+ getSymbol();
+ return true;
+ }
+ return false;
+ }
+
+ bool acceptString(String string) {
+ if (lastToken == string) {
+ getSymbol();
+ return true;
+ }
+ return false;
+ }
+
+ Expression parsePrimary() {
+ String last = lastToken;
+ if (acceptCategory(ALPHA)) {
+ if (last == "true") {
+ return new LiteralBool(true);
+ } else if (last == "false") {
+ return new LiteralBool(false);
+ } else if (last == "null") {
+ return new LiteralNull();
+ } else {
+ return new VariableUse(last);
+ }
+ } else if (acceptCategory(LPAREN)) {
+ Expression expression = parseExpression();
+ expectCategory(RPAREN);
+ return expression;
+ } else if (acceptCategory(STRING)) {
+ return new LiteralString(last);
+ } else if (acceptCategory(NUMERIC)) {
+ return new LiteralNumber(last);
+ } else if (acceptCategory(LBRACE)) {
+ expectCategory(RBRACE);
+ return new ObjectInitializer([]);
+ } else if (acceptCategory(LSQUARE)) {
+ var values = <ArrayElement>[];
+ if (!acceptCategory(RSQUARE)) {
+ do {
+ values.add(new ArrayElement(values.length, parseExpression()));
+ } while (acceptCategory(COMMA));
+ expectCategory(RSQUARE);
+ }
+ return new ArrayInitializer(values.length, values);
+ } else {
+ throw new MiniJsParserError(this, "Expected primary expression");
+ }
+ }
+
+ Expression parseMember() {
+ Expression receiver = parsePrimary();
+ while (true) {
+ if (acceptCategory(DOT)) {
+ String identifier = lastToken;
+ expectCategory(ALPHA);
+ receiver = new PropertyAccess.field(receiver, identifier);
+ } else if (acceptCategory(LSQUARE)) {
+ Expression inBraces = parseExpression();
+ expectCategory(RSQUARE);
+ receiver = new PropertyAccess(receiver, inBraces);
+ } else {
+ return receiver;
+ }
+ }
+ }
+
+ Expression parseCall() {
+ bool constructor = acceptString("new");
+ Expression receiver = parseMember();
+ if (acceptCategory(LPAREN)) {
+ final arguments = <Expression>[];
+ if (!acceptCategory(RPAREN)) {
+ while (true) {
+ Expression argument = parseExpression();
+ arguments.add(argument);
+ if (acceptCategory(RPAREN)) break;
+ expectCategory(COMMA);
+ }
+ }
+ return constructor ?
+ new New(receiver, arguments) :
+ new Call(receiver, arguments);
+ } else {
+ if (constructor) {
+ // JS allows new without (), but we don't.
+ throw new MiniJsParserError(this, "Parentheses are required for new");
+ }
+ return receiver;
+ }
+ }
+
+ Expression parsePostfix() {
+ Expression expression = parseCall();
+ String operator = lastToken;
+ if (lastCategory == SYMBOL && (acceptString("++") || acceptString("--"))) {
+ return new Postfix(operator, expression);
+ }
+ return expression;
+ }
+
+ Expression parseUnary() {
+ String operator = lastToken;
+ if (lastCategory == ALPHA) {
+ if (acceptString("typeof") || acceptString("void") ||
+ acceptString("delete")) {
+ return new Prefix(operator, parsePostfix());
+ }
+ } else if (lastCategory == SYMBOL) {
+ if (acceptString("~") || acceptString("-") || acceptString("++") ||
+ acceptString("--") || acceptString("+")) {
+ return new Prefix(operator, parsePostfix());
+ }
+ } else if (acceptString("!")) {
+ return new Prefix(operator, parsePostfix());
+ }
+ return parsePostfix();
+ }
+
+ Expression parseBinary() {
+ // Since we don't handle precedence we don't allow two different symbols
+ // without parentheses.
+ Expression lhs = parseUnary();
+ String firstSymbol = lastToken;
+ while (true) {
+ String symbol = lastToken;
+ if (!acceptCategory(SYMBOL)) return lhs;
+ if (!BINARY_OPERATORS.contains(symbol)) {
+ throw new MiniJsParserError(this, "Unknown binary operator");
+ }
+ if (symbol != firstSymbol) {
+ throw new MiniJsParserError(
+ this, "Mixed $firstSymbol and $symbol operators without ()");
+ }
+ Expression rhs = parseUnary();
+ if (symbol.endsWith("=")) {
+ // +=, -=, *= etc.
+ lhs = new Assignment.compound(lhs,
+ symbol.substring(0, symbol.length - 1),
+ rhs);
+ } else {
+ lhs = new Binary(symbol, lhs, rhs);
+ }
+ }
+ }
+
+ Expression parseRelation() {
+ Expression lhs = parseBinary();
+ String relation = lastToken;
+ // The lexer returns "=" as a relational operator because it looks a bit
+ // like ==, <=, etc. But we don't want to handle it here (that would give
+ // it the wrong prescedence), so we just return if we see it.
+ if (relation == "=" || !acceptCategory(RELATION)) return lhs;
+ Expression rhs = parseBinary();
+ if (relation == "<<=" || relation == ">>=") {
+ return new Assignment.compound(lhs,
+ relation.substring(0, relation.length - 1),
+ rhs);
+ } else {
+ // Regular binary operation.
+ return new Binary(relation, lhs, rhs);
+ }
+ }
+
+ Expression parseConditional() {
+ Expression lhs = parseRelation();
+ if (!acceptCategory(QUERY)) return lhs;
+ Expression ifTrue = parseAssignment();
+ expectCategory(COLON);
+ Expression ifFalse = parseAssignment();
+ return new Conditional(lhs, ifTrue, ifFalse);
+ }
+
+
+ Expression parseAssignment() {
+ Expression lhs = parseConditional();
+ if (acceptString("=")) {
+ return new Assignment(lhs, parseAssignment());
+ }
+ return lhs;
+ }
+
+ Expression parseExpression() => parseAssignment();
+
+ 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);
+ } else {
+ return parseExpression();
+ }
+ }
+
+ Expression expression() {
+ Expression expression = parseVarDeclarationOrExpression();
+ if (lastCategory != NONE || position != src.length) {
+ throw new MiniJsParserError(
+ this, "Unparsed junk: ${categoryToString(lastCategory)}");
+ }
+ return expression;
+ }
}
-
-Block emptyBlock() => new Block.empty();
-
-Block block1(Statement statement) => new Block(<Statement>[statement]);
-
-Block block2(Statement s1, Statement s2) => new Block(<Statement>[s1, s2]);
-
-Call call(Expression target, List<Expression> arguments) {
- return new Call(target, arguments);
-}
-
-Fun fun(List<String> parameterNames, Block body) {
- return new Fun(parameterNames.map((n) => new Parameter(n)).toList(), body);
-}
-
-Assignment assign(Expression leftHandSide, Expression value) {
- return new Assignment(leftHandSide, value);
-}
-
-Expression undefined() => new Prefix('void', new LiteralNumber('0'));
diff --git a/sdk/lib/_internal/compiler/implementation/js/printer.dart b/sdk/lib/_internal/compiler/implementation/js/printer.dart
index 8595174..5af3a42 100644
--- a/sdk/lib/_internal/compiler/implementation/js/printer.dart
+++ b/sdk/lib/_internal/compiler/implementation/js/printer.dart
@@ -851,6 +851,19 @@
visitLiteralStatement(LiteralStatement node) {
outLn(node.code);
}
+
+ void visitComment(Comment node) {
+ if (shouldCompressOutput) return;
+ String comment = node.comment.trim();
+ if (comment.isEmpty) return;
+ for (var line in comment.split('\n')) {
+ if (comment.startsWith('//')) {
+ outIndentLn(line.trim());
+ } else {
+ outIndentLn('// ${line.trim()}');
+ }
+ }
+ }
}
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart b/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
index 9f8155d..a66ab80 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
@@ -77,10 +77,10 @@
: types = null,
namedArguments = null;
- factory HTypeList.fromStaticInvocation(HInvokeStatic node, HTypeMap types) {
+ factory HTypeList.fromStaticInvocation(HInvokeStatic node) {
bool allUnknown = true;
for (int i = 1; i < node.inputs.length; i++) {
- if (types[node.inputs[i]] != HType.UNKNOWN) {
+ if (node.inputs[i].instructionType != HType.UNKNOWN) {
allUnknown = false;
break;
}
@@ -89,14 +89,13 @@
HTypeList result = new HTypeList(node.inputs.length - 1);
for (int i = 0; i < result.types.length; i++) {
- result.types[i] = types[node.inputs[i + 1]];
+ result.types[i] = node.inputs[i + 1].instructionType;
}
return result;
}
factory HTypeList.fromDynamicInvocation(HInvokeDynamic node,
- Selector selector,
- HTypeMap types) {
+ Selector selector) {
HTypeList result;
int argumentsCount = node.inputs.length - 1;
int startInvokeIndex = HInvoke.ARGUMENTS_OFFSET;
@@ -115,7 +114,7 @@
}
for (int i = 0; i < result.types.length; i++) {
- result.types[i] = types[node.inputs[i + startInvokeIndex]];
+ result.types[i] = node.inputs[i + startInvokeIndex].instructionType;
}
return result;
}
@@ -330,7 +329,7 @@
&& initializerType == null
&& constructorType == null
&& setterType == null) {
- // Don't register UNKONWN if there is currently no type information
+ // Don't register UNKNOWN if there is currently no type information
// present for the field. Instead register the function holding the
// setter for recompilation if better type information for the field
// becomes available.
@@ -456,11 +455,11 @@
return true;
}
- void registerStaticInvocation(HInvokeStatic node, HTypeMap types) {
+ void registerStaticInvocation(HInvokeStatic node) {
Element element = node.element;
assert(invariant(node, element.isDeclaration));
HTypeList oldTypes = staticTypeMap[element];
- HTypeList newTypes = new HTypeList.fromStaticInvocation(node, types);
+ HTypeList newTypes = new HTypeList.fromStaticInvocation(node);
if (oldTypes == null) {
staticTypeMap[element] = newTypes;
} else if (updateTypes(oldTypes, newTypes, element, staticTypeMap)) {
@@ -501,7 +500,7 @@
// Run through all optimized functions and figure out if they need
// to be recompiled because of this new invocation.
- optimizedFunctions.filterBySelector(selector).forEach((Element element) {
+ for (Element element in optimizedFunctions.filter(selector)) {
// TODO(kasperl): Maybe check if the element is already marked for
// recompilation? Could be pretty cheap compared to computing
// union types.
@@ -521,7 +520,7 @@
}
}
if (recompile) backend.scheduleForRecompilation(element);
- });
+ }
}
HTypeList parameterTypes(FunctionElement element,
@@ -585,7 +584,7 @@
}
// TODO(kasperl): What kind of non-members do we get here?
- if (!element.isMember()) return;
+ if (!element.isInstanceMember()) return;
if (parameterTypes.allUnknown) {
optimizedFunctions.remove(element);
@@ -607,12 +606,10 @@
}
class JavaScriptItemCompilationContext extends ItemCompilationContext {
- final HTypeMap types;
final Set<HInstruction> boundsChecked;
JavaScriptItemCompilationContext()
- : types = new HTypeMap(),
- boundsChecked = new Set<HInstruction>();
+ : boundsChecked = new Set<HInstruction>();
}
class JavaScriptBackend extends Backend {
@@ -624,15 +621,15 @@
/**
* The generated code as a js AST for compiled methods.
*/
- Map<Element, js.Expression> get generatedCode {
+ Map<Element, jsAst.Expression> get generatedCode {
return compiler.enqueuer.codegen.generatedCode;
}
/**
* The generated code as a js AST for compiled bailout methods.
*/
- final Map<Element, js.Expression> generatedBailoutCode =
- new Map<Element, js.Expression>();
+ final Map<Element, jsAst.Expression> generatedBailoutCode =
+ new Map<Element, jsAst.Expression>();
ClassElement jsStringClass;
ClassElement jsArrayClass;
@@ -685,7 +682,7 @@
* A collection of selectors that must have a one shot interceptor
* generated.
*/
- final Set<Selector> oneShotInterceptors;
+ final Map<String, Selector> oneShotInterceptors;
/**
* The members of instantiated interceptor classes: maps a member
@@ -711,6 +708,14 @@
*/
final Map<ClassElement, ClassElement> interceptedClasses;
+ /**
+ * Set of selectors that are used from within loops. Used by the
+ * builder to allow speculative optimizations for functions without
+ * loops themselves.
+ */
+ final Map<SourceString, Set<Selector>> selectorsCalledInLoop =
+ new Map<SourceString, Set<Selector>>();
+
List<CompilerTask> get tasks {
return <CompilerTask>[builder, optimizer, generator, emitter];
}
@@ -722,7 +727,7 @@
returnInfo = new Map<Element, ReturnInfo>(),
invalidateAfterCodegen = new List<Element>(),
usedInterceptors = new Set<Selector>(),
- oneShotInterceptors = new Set<Selector>(),
+ oneShotInterceptors = new Map<String, Selector>(),
interceptedElements = new Map<SourceString, Set<Element>>(),
rti = new RuntimeTypeInformation(compiler),
specializedGetInterceptors =
@@ -730,7 +735,8 @@
interceptedClasses = new LinkedHashMap<ClassElement, ClassElement>(),
super(compiler, JAVA_SCRIPT_CONSTANT_SYSTEM) {
emitter = disableEval
- ? new CodeEmitterNoEvalTask(compiler, namer, generateSourceMap)
+ // TODO(8522): Restore --disallow-unsafe-eval.
+ ? null // new CodeEmitterNoEvalTask(compiler, namer, generateSourceMap)
: new CodeEmitterTask(compiler, namer, generateSourceMap);
builder = new SsaBuilderTask(this);
optimizer = new SsaOptimizerTask(this);
@@ -754,34 +760,54 @@
usedInterceptors.add(selector);
}
- void addOneShotInterceptor(Selector selector) {
- oneShotInterceptors.add(selector);
+ String registerOneShotInterceptor(Selector selector) {
+ Set<ClassElement> classes = getInterceptedClassesOn(selector);
+ String name = namer.getOneShotInterceptorName(selector, classes);
+ if (!oneShotInterceptors.containsKey(name)) {
+ registerSpecializedGetInterceptor(classes);
+ oneShotInterceptors[name] = selector;
+ }
+ return name;
}
+ final Map<Selector, Set<ClassElement>> interceptedClassesCache =
+ new Map<Selector, Set<ClassElement>>();
+ final Map<Selector, Set<ClassElement>> interceptedClassesNonNullCache =
+ new Map<Selector, Set<ClassElement>>();
+
/**
* Returns a set of interceptor classes that contain a member whose
* signature matches the given [selector]. Returns [:null:] if there
* is no class.
*/
- Set<ClassElement> getInterceptedClassesOn(Selector selector) {
+ Set<ClassElement> getInterceptedClassesOn(Selector selector,
+ {bool canBeNull: true}) {
Set<Element> intercepted = interceptedElements[selector.name];
if (intercepted == null) return null;
+ // Pick the right cache and query it.
+ Map<Selector, Set<ClassElement>> cache = canBeNull
+ ? interceptedClassesCache
+ : interceptedClassesNonNullCache;
+ if (cache.containsKey(selector)) return cache[selector];
+ // Populate the cache by running through all the elements and
+ // determine if the given selector applies to them.
Set<ClassElement> result = new Set<ClassElement>();
for (Element element in intercepted) {
- if (selector.applies(element, compiler)) {
- result.add(element.getEnclosingClass());
- }
+ ClassElement enclosing = element.getEnclosingClass();
+ // We have to treat null as a bottom type, so we use the untyped
+ // applies method for those elements that are implemented on the
+ // null class.
+ bool applies = (enclosing == jsNullClass)
+ ? canBeNull && selector.appliesUntyped(element, compiler)
+ : selector.applies(element, compiler);
+ if (applies) result.add(enclosing);
}
- if (result.isEmpty) return null;
+ if (result.isEmpty) result = null;
+ cache[selector] = result;
+ assert(cache.containsKey(selector));
return result;
}
- List<ClassElement> getListOfInterceptedClasses() {
- return <ClassElement>[jsStringClass, jsArrayClass, jsIntClass,
- jsDoubleClass, jsNumberClass, jsNullClass,
- jsFunctionClass, jsBoolClass];
- }
-
void initializeInterceptorElements() {
objectInterceptorClass =
compiler.findInterceptor(const SourceString('ObjectInterceptor'));
@@ -875,19 +901,39 @@
initializeNoSuchMethod();
seenAnyClass = true;
}
+
+ // Register any helper that will be needed by the backend.
+ if (enqueuer.isResolutionQueue) {
+ if (cls == compiler.intClass
+ || cls == compiler.doubleClass
+ || cls == compiler.numClass) {
+ // The backend will try to optimize number operations and use the
+ // `iae` helper directly.
+ enqueuer.registerStaticUse(
+ compiler.findHelper(const SourceString('iae')));
+ } else if (cls == compiler.listClass
+ || cls == compiler.stringClass) {
+ // The backend will try to optimize array and string access and use the
+ // `ioore` and `iae` helpers directly.
+ enqueuer.registerStaticUse(
+ compiler.findHelper(const SourceString('ioore')));
+ enqueuer.registerStaticUse(
+ compiler.findHelper(const SourceString('iae')));
+ } else if (cls == compiler.functionClass) {
+ enqueuer.registerInstantiatedClass(compiler.closureClass);
+ } else if (cls == compiler.mapClass) {
+ // The backend will use a literal list to initialize the entries
+ // of the map.
+ enqueuer.registerInstantiatedClass(compiler.listClass);
+ enqueuer.registerInstantiatedClass(compiler.mapLiteralClass);
+ enqueueInResolution(getMapMaker());
+ }
+ }
ClassElement result = null;
if (cls == compiler.stringClass) {
addInterceptors(jsStringClass, enqueuer);
} else if (cls == compiler.listClass) {
addInterceptors(jsArrayClass, enqueuer);
- // The backend will try to optimize array access and use the
- // `ioore` and `iae` helpers directly.
- if (enqueuer.isResolutionQueue) {
- enqueuer.registerStaticUse(
- compiler.findHelper(const SourceString('ioore')));
- enqueuer.registerStaticUse(
- compiler.findHelper(const SourceString('iae')));
- }
} else if (cls == compiler.intClass) {
addInterceptors(jsIntClass, enqueuer);
addInterceptors(jsNumberClass, enqueuer);
@@ -905,17 +951,16 @@
addInterceptors(jsDoubleClass, enqueuer);
addInterceptors(jsNumberClass, enqueuer);
} else if (cls == compiler.mapClass) {
- // The backend will use a literal list to initialize the entries
- // of the map.
- if (enqueuer.isResolutionQueue) {
- enqueuer.registerInstantiatedClass(compiler.listClass);
- enqueuer.registerInstantiatedClass(compiler.mapLiteralClass);
- }
}
- }
- Element get cyclicThrowHelper {
- return compiler.findHelper(const SourceString("throwCyclicInit"));
+ if (compiler.enableTypeAssertions) {
+ // We need to register is checks for assignments to fields.
+ cls.forEachLocalMember((Element member) {
+ if (!member.isInstanceMember() || !member.isField()) return;
+ DartType type = member.computeType(compiler);
+ enqueuer.registerIsCheck(type);
+ });
+ }
}
JavaScriptItemCompilationContext createItemCompilationContext() {
@@ -923,22 +968,114 @@
}
void enqueueHelpers(ResolutionEnqueuer world) {
- enqueueAllTopLevelFunctions(compiler.jsHelperLibrary, world);
-
jsIndexingBehaviorInterface =
compiler.findHelper(const SourceString('JavaScriptIndexingBehavior'));
if (jsIndexingBehaviorInterface != null) {
world.registerIsCheck(jsIndexingBehaviorInterface.computeType(compiler));
}
- for (var helper in [const SourceString('Closure'),
- const SourceString('ConstantMap'),
- const SourceString('ConstantProtoMap')]) {
- var e = compiler.findHelper(helper);
- if (e != null) world.registerInstantiatedClass(e);
+ if (compiler.enableTypeAssertions) {
+ // Unconditionally register the helper that checks if the
+ // expression in an if/while/for is a boolean.
+ // TODO(ngeoffray): Should we have the resolver register those instead?
+ Element e =
+ compiler.findHelper(const SourceString('boolConversionCheck'));
+ if (e != null) world.addToWorkList(e);
}
}
+ void registerStringInterpolation() {
+ enqueueInResolution(getStringInterpolationHelper());
+ }
+
+ void registerCatchStatement() {
+ enqueueInResolution(getExceptionUnwrapper());
+ }
+
+ void registerThrow() {
+ enqueueInResolution(getThrowHelper());
+ }
+
+ void registerLazyField() {
+ enqueueInResolution(getCyclicThrowHelper());
+ }
+
+ void registerTypeLiteral() {
+ enqueueInResolution(getCreateRuntimeType());
+ }
+
+ void registerStackTraceInCatch() {
+ enqueueInResolution(getTraceFromException());
+ }
+
+ void registerRuntimeType() {
+ enqueueInResolution(getSetRuntimeTypeInfo());
+ enqueueInResolution(getGetRuntimeTypeInfo());
+ enqueueInResolution(getGetRuntimeTypeArgument());
+ compiler.enqueuer.resolution.registerInstantiatedClass(compiler.listClass);
+ }
+
+ void registerIsCheck(DartType type, Enqueuer world) {
+ if (!type.isRaw) {
+ enqueueInResolution(getSetRuntimeTypeInfo());
+ enqueueInResolution(getGetRuntimeTypeInfo());
+ enqueueInResolution(getGetRuntimeTypeArgument());
+ enqueueInResolution(getCheckArguments());
+ world.registerInstantiatedClass(compiler.listClass);
+ }
+ // [registerIsCheck] is also called for checked mode checks, so we
+ // need to register checked mode helpers.
+ if (compiler.enableTypeAssertions) {
+ Element e = getCheckedModeHelper(type, typeCast: false);
+ if (e != null) world.addToWorkList(e);
+ // We also need the native variant of the check (for DOM types).
+ e = getNativeCheckedModeHelper(type, typeCast: false);
+ if (e != null) world.addToWorkList(e);
+ }
+ }
+
+ void registerAsCheck(DartType type) {
+ Element e = getCheckedModeHelper(type, typeCast: true);
+ enqueueInResolution(e);
+ // We also need the native variant of the check (for DOM types).
+ e = getNativeCheckedModeHelper(type, typeCast: true);
+ enqueueInResolution(e);
+ }
+
+ void registerThrowNoSuchMethod() {
+ enqueueInResolution(getThrowNoSuchMethod());
+ }
+
+ void registerThrowRuntimeError() {
+ enqueueInResolution(getThrowRuntimeError());
+ }
+
+ void registerAbstractClassInstantiation() {
+ enqueueInResolution(getThrowAbstractClassInstantiationError());
+ }
+
+ void registerFallThroughError() {
+ enqueueInResolution(getFallThroughError());
+ }
+
+ void registerSuperNoSuchMethod() {
+ enqueueInResolution(getCreateInvocationMirror());
+ enqueueInResolution(
+ compiler.objectClass.lookupLocalMember(Compiler.NO_SUCH_METHOD));
+ compiler.enqueuer.resolution.registerInstantiatedClass(compiler.listClass);
+ }
+
+ void enqueueInResolution(Element e) {
+ if (e != null) compiler.enqueuer.resolution.addToWorkList(e);
+ }
+
+ void registerConstantMap() {
+ Element e = compiler.findHelper(const SourceString('ConstantMap'));
+ if (e != null) compiler.enqueuer.resolution.registerInstantiatedClass(e);
+ e = compiler.findHelper(const SourceString('ConstantProtoMap'));
+ if (e != null) compiler.enqueuer.resolution.registerInstantiatedClass(e);
+ }
+
void codegen(CodegenWorkItem work) {
Element element = work.element;
if (element.kind.category == ElementCategory.VARIABLE) {
@@ -950,7 +1087,7 @@
// go through the builder (below) to generate the lazy initializer for
// the static variable.
// We also need to register the use of the cyclic-error helper.
- compiler.enqueuer.codegen.registerStaticUse(cyclicThrowHelper);
+ compiler.enqueuer.codegen.registerStaticUse(getCyclicThrowHelper());
}
}
@@ -958,12 +1095,12 @@
optimizer.optimize(work, graph, false);
if (work.allowSpeculativeOptimization
&& optimizer.trySpeculativeOptimizations(work, graph)) {
- js.Expression code = generator.generateBailoutMethod(work, graph);
+ jsAst.Expression code = generator.generateBailoutMethod(work, graph);
generatedBailoutCode[element] = code;
optimizer.prepareForSpeculativeOptimizations(work, graph);
optimizer.optimize(work, graph, true);
}
- js.Expression code = generator.generateCode(work, graph);
+ jsAst.Expression code = generator.generateCode(work, graph);
generatedCode[element] = code;
invalidateAfterCodegen.forEach(eagerRecompile);
invalidateAfterCodegen.clear();
@@ -984,7 +1121,7 @@
*/
String assembleCode(Element element) {
assert(invariant(element, element.isDeclaration));
- return js.prettyPrint(generatedCode[element], compiler).getText();
+ return jsAst.prettyPrint(generatedCode[element], compiler).getText();
}
void assembleProgram() {
@@ -1007,11 +1144,9 @@
* Register a dynamic invocation and collect the provided types for the
* named selector.
*/
- void registerDynamicInvocation(HInvokeDynamic node,
- Selector selector,
- HTypeMap types) {
+ void registerDynamicInvocation(HInvokeDynamic node, Selector selector) {
HTypeList providedTypes =
- new HTypeList.fromDynamicInvocation(node, selector, types);
+ new HTypeList.fromDynamicInvocation(node, selector);
argumentTypes.registerDynamicInvocation(providedTypes, selector);
}
@@ -1019,8 +1154,8 @@
* Register a static invocation and collect the provided types for the
* named selector.
*/
- void registerStaticInvocation(HInvokeStatic node, HTypeMap types) {
- argumentTypes.registerStaticInvocation(node, types);
+ void registerStaticInvocation(HInvokeStatic node) {
+ argumentTypes.registerStaticInvocation(node);
}
/**
@@ -1141,54 +1276,126 @@
}
/**
- * Return the checked mode helper name that will be needed to do a
- * type check on [type] at runtime. Note that this method is being
- * called both by the resolver with interface types (int, String,
- * ...), and by the SSA backend with implementation types (JSInt,
- * JSString, ...).
+ * Returns the checked mode helper that will be needed to do a type check/type
+ * cast on [type] at runtime. Note that this method is being called both by
+ * the resolver with interface types (int, String, ...), and by the SSA
+ * backend with implementation types (JSInt, JSString, ...).
*/
- SourceString getCheckedModeHelper(DartType type) {
+ Element getCheckedModeHelper(DartType type, {bool typeCast}) {
+ return compiler.findHelper(getCheckedModeHelperName(
+ type, typeCast: typeCast, nativeCheckOnly: false));
+ }
+
+ /**
+ * Returns the native checked mode helper that will be needed to do a type
+ * check/type cast on [type] at runtime. If no native helper exists for
+ * [type], [:null:] is returned.
+ */
+ Element getNativeCheckedModeHelper(DartType type, {bool typeCast}) {
+ SourceString sourceName = getCheckedModeHelperName(
+ type, typeCast: typeCast, nativeCheckOnly: true);
+ if (sourceName == null) return null;
+ return compiler.findHelper(sourceName);
+ }
+
+ /**
+ * Returns the name of the type check/type cast helper method for [type]. If
+ * [nativeCheckOnly] is [:true:], only names for native helpers are returned.
+ */
+ SourceString getCheckedModeHelperName(DartType type,
+ {bool typeCast,
+ bool nativeCheckOnly}) {
Element element = type.element;
- bool nativeCheck =
- emitter.nativeEmitter.requiresNativeIsCheck(element);
+ bool nativeCheck = nativeCheckOnly ||
+ emitter.nativeEmitter.requiresNativeIsCheck(element);
if (type.isMalformed) {
// Check for malformed types first, because the type may be a list type
// with a malformed argument type.
- return const SourceString('malformedTypeCheck');
+ if (nativeCheckOnly) return null;
+ return typeCast
+ ? const SourceString('malformedTypeCast')
+ : const SourceString('malformedTypeCheck');
} else if (type == compiler.types.voidType) {
+ assert(!typeCast); // Cannot cast to void.
+ if (nativeCheckOnly) return null;
return const SourceString('voidTypeCheck');
} else if (element == jsStringClass || element == compiler.stringClass) {
- return const SourceString('stringTypeCheck');
+ if (nativeCheckOnly) return null;
+ return typeCast
+ ? const SourceString("stringTypeCast")
+ : const SourceString('stringTypeCheck');
} else if (element == jsDoubleClass || element == compiler.doubleClass) {
- return const SourceString('doubleTypeCheck');
+ if (nativeCheckOnly) return null;
+ return typeCast
+ ? const SourceString("doubleTypeCast")
+ : const SourceString('doubleTypeCheck');
} else if (element == jsNumberClass || element == compiler.numClass) {
- return const SourceString('numTypeCheck');
+ if (nativeCheckOnly) return null;
+ return typeCast
+ ? const SourceString("numTypeCast")
+ : const SourceString('numTypeCheck');
} else if (element == jsBoolClass || element == compiler.boolClass) {
- return const SourceString('boolTypeCheck');
- } else if (element == jsFunctionClass
- || element == compiler.functionClass) {
- return const SourceString('functionTypeCheck');
+ if (nativeCheckOnly) return null;
+ return typeCast
+ ? const SourceString("boolTypeCast")
+ : const SourceString('boolTypeCheck');
+ } else if (element == jsFunctionClass ||
+ element == compiler.functionClass) {
+ if (nativeCheckOnly) return null;
+ return typeCast
+ ? const SourceString("functionTypeCast")
+ : const SourceString('functionTypeCheck');
} else if (element == jsIntClass || element == compiler.intClass) {
- return const SourceString('intTypeCheck');
+ if (nativeCheckOnly) return null;
+ return typeCast ?
+ const SourceString("intTypeCast") :
+ const SourceString('intTypeCheck');
} else if (Elements.isNumberOrStringSupertype(element, compiler)) {
- return nativeCheck
- ? const SourceString('numberOrStringSuperNativeTypeCheck')
+ if (nativeCheck) {
+ return typeCast
+ ? const SourceString("numberOrStringSuperNativeTypeCast")
+ : const SourceString('numberOrStringSuperNativeTypeCheck');
+ } else {
+ return typeCast
+ ? const SourceString("numberOrStringSuperTypeCast")
: const SourceString('numberOrStringSuperTypeCheck');
+ }
} else if (Elements.isStringOnlySupertype(element, compiler)) {
- return nativeCheck
- ? const SourceString('stringSuperNativeTypeCheck')
- : const SourceString('stringSuperTypeCheck');
+ if (nativeCheck) {
+ return typeCast
+ ? const SourceString("stringSuperNativeTypeCast")
+ : const SourceString('stringSuperNativeTypeCheck');
+ } else {
+ return typeCast
+ ? const SourceString("stringSuperTypeCast")
+ : const SourceString('stringSuperTypeCheck');
+ }
} else if (element == compiler.listClass || element == jsArrayClass) {
- return const SourceString('listTypeCheck');
+ if (nativeCheckOnly) return null;
+ return typeCast
+ ? const SourceString("listTypeCast")
+ : const SourceString('listTypeCheck');
} else {
if (Elements.isListSupertype(element, compiler)) {
- return nativeCheck
- ? const SourceString('listSuperNativeTypeCheck')
- : const SourceString('listSuperTypeCheck');
+ if (nativeCheck) {
+ return typeCast
+ ? const SourceString("listSuperNativeTypeCast")
+ : const SourceString('listSuperNativeTypeCheck');
+ } else {
+ return typeCast
+ ? const SourceString("listSuperTypeCast")
+ : const SourceString('listSuperTypeCheck');
+ }
} else {
- return nativeCheck
- ? const SourceString('callTypeCheck')
- : const SourceString('propertyTypeCheck');
+ if (nativeCheck) {
+ return typeCast
+ ? const SourceString("callTypeCast")
+ : const SourceString('callTypeCheck');
+ } else {
+ return typeCast
+ ? const SourceString("propertyTypeCast")
+ : const SourceString('propertyTypeCheck');
+ }
}
}
}
@@ -1226,6 +1433,14 @@
const SourceString('throwAbstractClassInstantiationError'));
}
+ Element getStringInterpolationHelper() {
+ return compiler.findHelper(const SourceString('S'));
+ }
+
+ Element getThrowHelper() {
+ return compiler.findHelper(const SourceString(r'$throw'));
+ }
+
Element getClosureConverter() {
return compiler.findHelper(const SourceString('convertDartClosureToJS'));
}
@@ -1250,6 +1465,30 @@
return compiler.findHelper(const SourceString('getRuntimeTypeArgument'));
}
+ Element getCheckArguments() {
+ return compiler.findHelper(const SourceString('checkArguments'));
+ }
+
+ Element getThrowNoSuchMethod() {
+ return compiler.findHelper(const SourceString('throwNoSuchMethod'));
+ }
+
+ Element getCreateRuntimeType() {
+ return compiler.findHelper(const SourceString('createRuntimeType'));
+ }
+
+ Element getFallThroughError() {
+ return compiler.findHelper(const SourceString("getFallThroughError"));
+ }
+
+ Element getCreateInvocationMirror() {
+ return compiler.findHelper(Compiler.CREATE_INVOCATION_MIRROR);
+ }
+
+ Element getCyclicThrowHelper() {
+ return compiler.findHelper(const SourceString("throwCyclicInit"));
+ }
+
/**
* Remove [element] from the set of generated code, and put it back
* into the worklist.
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/constant_emitter.dart b/sdk/lib/_internal/compiler/implementation/js_backend/constant_emitter.dart
index 86717ae..9f3ea2e 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/constant_emitter.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/constant_emitter.dart
@@ -19,7 +19,7 @@
* canonical name unless the constant can be emitted multiple times (as for
* numbers and strings).
*/
- js.Expression reference(Constant constant) {
+ jsAst.Expression reference(Constant constant) {
return _referenceEmitter.generate(constant);
}
@@ -27,14 +27,14 @@
* Constructs an expression like [reference], but the expression is valid
* during isolate initialization.
*/
- js.Expression referenceInInitializationContext(Constant constant) {
+ jsAst.Expression referenceInInitializationContext(Constant constant) {
return _referenceEmitter.generateInInitializationContext(constant);
}
/**
* Constructs an expression used to initialize a canonicalized constant.
*/
- js.Expression initializationExpression(Constant constant) {
+ jsAst.Expression initializationExpression(Constant constant) {
return _initializerEmitter.generate(constant);
}
}
@@ -43,74 +43,68 @@
* Visitor for generating JavaScript expressions to refer to [Constant]s.
* Do not use directly, use methods from [ConstantEmitter].
*/
-class ConstantReferenceEmitter implements ConstantVisitor<js.Expression> {
+class ConstantReferenceEmitter implements ConstantVisitor<jsAst.Expression> {
final Compiler compiler;
final Namer namer;
- bool inIsolateInitializationContext = false;
ConstantReferenceEmitter(this.compiler, this.namer);
- js.Expression generate(Constant constant) {
- inIsolateInitializationContext = false;
+ jsAst.Expression generate(Constant constant) {
return _visit(constant);
}
- js.Expression generateInInitializationContext(Constant constant) {
- inIsolateInitializationContext = true;
+ jsAst.Expression generateInInitializationContext(Constant constant) {
return _visit(constant);
}
- js.Expression _visit(Constant constant) {
+ jsAst.Expression _visit(Constant constant) {
return constant.accept(this);
}
- js.Expression visitSentinel(SentinelConstant constant) {
- return new js.VariableUse(namer.CURRENT_ISOLATE);
+ jsAst.Expression visitSentinel(SentinelConstant constant) {
+ return new jsAst.VariableUse(namer.CURRENT_ISOLATE);
}
- js.Expression visitFunction(FunctionConstant constant) {
- return inIsolateInitializationContext
- ? new js.VariableUse(namer.isolatePropertiesAccess(constant.element))
- : new js.VariableUse(namer.isolateAccess(constant.element));
+ jsAst.Expression visitFunction(FunctionConstant constant) {
+ return new jsAst.VariableUse(namer.isolateAccess(constant.element));
}
- js.Expression visitNull(NullConstant constant) {
- return new js.LiteralNull();
+ jsAst.Expression visitNull(NullConstant constant) {
+ return new jsAst.LiteralNull();
}
- js.Expression visitInt(IntConstant constant) {
- return new js.LiteralNumber('${constant.value}');
+ jsAst.Expression visitInt(IntConstant constant) {
+ return new jsAst.LiteralNumber('${constant.value}');
}
- js.Expression visitDouble(DoubleConstant constant) {
+ jsAst.Expression visitDouble(DoubleConstant constant) {
double value = constant.value;
if (value.isNaN) {
- return new js.LiteralNumber("(0/0)");
+ return new jsAst.LiteralNumber("(0/0)");
} else if (value == double.INFINITY) {
- return new js.LiteralNumber("(1/0)");
+ return new jsAst.LiteralNumber("(1/0)");
} else if (value == -double.INFINITY) {
- return new js.LiteralNumber("(-1/0)");
+ return new jsAst.LiteralNumber("(-1/0)");
} else {
- return new js.LiteralNumber("$value");
+ return new jsAst.LiteralNumber("$value");
}
}
- js.Expression visitTrue(TrueConstant constant) {
+ jsAst.Expression visitTrue(TrueConstant constant) {
if (compiler.enableMinification) {
// Use !0 for true.
- return new js.Prefix("!", new js.LiteralNumber("0"));
+ return js["!0"];
} else {
- return new js.LiteralBool(true);
+ return new jsAst.LiteralBool(true);
}
-
}
- js.Expression visitFalse(FalseConstant constant) {
+ jsAst.Expression visitFalse(FalseConstant constant) {
if (compiler.enableMinification) {
// Use !1 for false.
- return new js.Prefix("!", new js.LiteralNumber("1"));
+ return js["!1"];
} else {
- return new js.LiteralBool(false);
+ return new jsAst.LiteralBool(false);
}
}
@@ -119,44 +113,34 @@
* a form that is valid as JavaScript string literal content.
* The string is assumed quoted by double quote characters.
*/
- js.Expression visitString(StringConstant constant) {
+ jsAst.Expression visitString(StringConstant constant) {
// TODO(sra): If the string is long *and repeated* (and not on a hot path)
// then it should be assigned to a name. We don't have reference counts (or
// profile information) here, so this is the wrong place.
StringBuffer sb = new StringBuffer();
writeJsonEscapedCharsOn(constant.value.slowToString(), sb);
- return new js.LiteralString('"$sb"');
+ return new jsAst.LiteralString('"$sb"');
}
- js.Expression emitCanonicalVersion(Constant constant) {
+ jsAst.Expression emitCanonicalVersion(Constant constant) {
String name = namer.constantName(constant);
- if (inIsolateInitializationContext) {
- // $isolateName.$isolatePropertiesName.$name
- return new js.PropertyAccess.field(
- new js.PropertyAccess.field(
- new js.VariableUse(namer.isolateName),
- namer.isolatePropertiesName),
- name);
- } else {
- return new js.PropertyAccess.field(
- new js.VariableUse(namer.CURRENT_ISOLATE),
- name);
- }
+ return new jsAst.PropertyAccess.field(
+ new jsAst.VariableUse(namer.CURRENT_ISOLATE), name);
}
- js.Expression visitList(ListConstant constant) {
+ jsAst.Expression visitList(ListConstant constant) {
return emitCanonicalVersion(constant);
}
- js.Expression visitMap(MapConstant constant) {
+ jsAst.Expression visitMap(MapConstant constant) {
return emitCanonicalVersion(constant);
}
- js.Expression visitType(TypeConstant constant) {
+ jsAst.Expression visitType(TypeConstant constant) {
return emitCanonicalVersion(constant);
}
- js.Expression visitConstructed(ConstructedConstant constant) {
+ jsAst.Expression visitConstructed(ConstructedConstant constant) {
return emitCanonicalVersion(constant);
}
}
@@ -165,90 +149,90 @@
* Visitor for generating JavaScript expressions to initialize [Constant]s.
* Do not use directly; use methods from [ConstantEmitter].
*/
-class ConstantInitializerEmitter implements ConstantVisitor<js.Expression> {
+class ConstantInitializerEmitter implements ConstantVisitor<jsAst.Expression> {
final Compiler compiler;
final Namer namer;
final ConstantReferenceEmitter referenceEmitter;
ConstantInitializerEmitter(this.compiler, this.namer, this.referenceEmitter);
- js.Expression generate(Constant constant) {
+ jsAst.Expression generate(Constant constant) {
return _visit(constant);
}
- js.Expression _visit(Constant constant) {
+ jsAst.Expression _visit(Constant constant) {
return constant.accept(this);
}
- js.Expression _reference(Constant constant) {
+ jsAst.Expression _reference(Constant constant) {
return referenceEmitter.generateInInitializationContext(constant);
}
- js.Expression visitSentinel(SentinelConstant constant) {
+ jsAst.Expression visitSentinel(SentinelConstant constant) {
compiler.internalError(
"The parameter sentinel constant does not need specific JS code");
}
- js.Expression visitFunction(FunctionConstant constant) {
+ jsAst.Expression visitFunction(FunctionConstant constant) {
compiler.internalError(
"The function constant does not need specific JS code");
}
- js.Expression visitNull(NullConstant constant) {
+ jsAst.Expression visitNull(NullConstant constant) {
return _reference(constant);
}
- js.Expression visitInt(IntConstant constant) {
+ jsAst.Expression visitInt(IntConstant constant) {
return _reference(constant);
}
- js.Expression visitDouble(DoubleConstant constant) {
+ jsAst.Expression visitDouble(DoubleConstant constant) {
return _reference(constant);
}
- js.Expression visitTrue(TrueConstant constant) {
+ jsAst.Expression visitTrue(TrueConstant constant) {
return _reference(constant);
}
- js.Expression visitFalse(FalseConstant constant) {
+ jsAst.Expression visitFalse(FalseConstant constant) {
return _reference(constant);
}
- js.Expression visitString(StringConstant constant) {
+ jsAst.Expression visitString(StringConstant constant) {
// TODO(sra): Some larger strings are worth sharing.
return _reference(constant);
}
- js.Expression visitList(ListConstant constant) {
- return new js.Call(
- new js.PropertyAccess.field(
- new js.VariableUse(namer.isolateName),
+ jsAst.Expression visitList(ListConstant constant) {
+ return new jsAst.Call(
+ new jsAst.PropertyAccess.field(
+ new jsAst.VariableUse(namer.isolateName),
'makeConstantList'),
- [new js.ArrayInitializer.from(_array(constant.entries))]);
+ [new jsAst.ArrayInitializer.from(_array(constant.entries))]);
}
String getJsConstructor(ClassElement element) {
- return namer.isolatePropertiesAccess(element);
+ return namer.isolateAccess(element);
}
- js.Expression visitMap(MapConstant constant) {
- js.Expression jsMap() {
- List<js.Property> properties = <js.Property>[];
+ jsAst.Expression visitMap(MapConstant constant) {
+ jsAst.Expression jsMap() {
+ List<jsAst.Property> properties = <jsAst.Property>[];
int valueIndex = 0;
for (int i = 0; i < constant.keys.entries.length; i++) {
StringConstant key = constant.keys.entries[i];
if (key.value == MapConstant.PROTO_PROPERTY) continue;
// Keys in literal maps must be emitted in place.
- js.Literal keyExpression = _visit(key);
- js.Expression valueExpression =
+ jsAst.Literal keyExpression = _visit(key);
+ jsAst.Expression valueExpression =
_reference(constant.values[valueIndex++]);
- properties.add(new js.Property(keyExpression, valueExpression));
+ properties.add(new jsAst.Property(keyExpression, valueExpression));
}
if (valueIndex != constant.values.length) {
compiler.internalError("Bad value count.");
}
- return new js.ObjectInitializer(properties);
+ return new jsAst.ObjectInitializer(properties);
}
void badFieldCountError() {
@@ -258,7 +242,7 @@
ClassElement classElement = constant.type.element;
- List<js.Expression> arguments = <js.Expression>[];
+ List<jsAst.Expression> arguments = <jsAst.Expression>[];
// The arguments of the JavaScript constructor for any given Dart class
// are in the same order as the members of the class element.
@@ -267,7 +251,7 @@
(ClassElement enclosing, Element field) {
if (field.name == MapConstant.LENGTH_NAME) {
arguments.add(
- new js.LiteralNumber('${constant.keys.entries.length}'));
+ new jsAst.LiteralNumber('${constant.keys.entries.length}'));
} else if (field.name == MapConstant.JS_OBJECT_NAME) {
arguments.add(jsMap());
} else if (field.name == MapConstant.KEYS_NAME) {
@@ -288,35 +272,34 @@
badFieldCountError();
}
- return new js.New(
- new js.VariableUse(getJsConstructor(classElement)),
+ return new jsAst.New(
+ new jsAst.VariableUse(getJsConstructor(classElement)),
arguments);
}
- js.Expression visitType(TypeConstant constant) {
- SourceString helperSourceName = const SourceString('createRuntimeType');
- Element helper = compiler.findHelper(helperSourceName);
+ jsAst.Expression visitType(TypeConstant constant) {
JavaScriptBackend backend = compiler.backend;
+ Element helper = backend.getCreateRuntimeType();
String helperName = backend.namer.getName(helper);
DartType type = constant.representedType;
Element element = type.element;
String name = backend.rti.getRawTypeRepresentation(type);
- js.Expression typeName = new js.LiteralString("'$name'");
- return new js.Call(
- new js.PropertyAccess.field(
- new js.VariableUse(namer.CURRENT_ISOLATE),
+ jsAst.Expression typeName = new jsAst.LiteralString("'$name'");
+ return new jsAst.Call(
+ new jsAst.PropertyAccess.field(
+ new jsAst.VariableUse(namer.CURRENT_ISOLATE),
helperName),
[typeName]);
}
- js.Expression visitConstructed(ConstructedConstant constant) {
- return new js.New(
- new js.VariableUse(getJsConstructor(constant.type.element)),
+ jsAst.Expression visitConstructed(ConstructedConstant constant) {
+ return new jsAst.New(
+ new jsAst.VariableUse(getJsConstructor(constant.type.element)),
_array(constant.fields));
}
- List<js.Expression> _array(List<Constant> values) {
- List<js.Expression> valueList = <js.Expression>[];
+ List<jsAst.Expression> _array(List<Constant> values) {
+ List<jsAst.Expression> valueList = <jsAst.Expression>[];
for (int i = 0; i < values.length; i++) {
valueList.add(_reference(values[i]));
}
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/constant_system_javascript.dart b/sdk/lib/_internal/compiler/implementation/js_backend/constant_system_javascript.dart
index 30c8bc5..fa5384f 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/constant_system_javascript.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/constant_system_javascript.dart
@@ -144,9 +144,6 @@
class JavaScriptConstantSystem extends ConstantSystem {
const int BITS31 = 0x8FFFFFFF;
const int BITS32 = 0xFFFFFFFF;
- // The maximum integer value a double can represent without losing
- // precision.
- const int BITS53 = 0x1FFFFFFFFFFFFF;
final add = const JavaScriptBinaryArithmeticOperation(const AddOperation());
final bitAnd = const JavaScriptBinaryBitOperation(const BitAndOperation());
@@ -185,7 +182,9 @@
*/
bool integerFitsIntoDouble(int value) {
int absValue = value.abs();
- return (absValue & BITS53) == absValue;
+ double doubleValue = absValue.toDouble();
+ if (doubleValue.isNaN || doubleValue.isInfinite) return false;
+ return value.toDouble().floor().toInt() == value;
}
NumConstant convertToJavaScriptConstant(NumConstant constant) {
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
index b238df6..64a6aa6 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
@@ -27,20 +27,22 @@
/**
* A convenient type alias for some functions that emit keyed values.
*/
-typedef void DefineStubFunction(String invocationName, js.Expression value);
+typedef void DefineStubFunction(String invocationName, jsAst.Expression value);
/**
* A data structure for collecting fragments of a class definition.
*/
class ClassBuilder {
- final List<js.Property> properties = <js.Property>[];
+ final List<jsAst.Property> properties = <jsAst.Property>[];
// Has the same signature as [DefineStubFunction].
- void addProperty(String name, js.Expression value) {
- properties.add(new js.Property(js.string(name), value));
+ void addProperty(String name, jsAst.Expression value) {
+ properties.add(new jsAst.Property(js.string(name), value));
}
- js.Expression toObjectInitializer() => new js.ObjectInitializer(properties);
+ jsAst.Expression toObjectInitializer() {
+ return new jsAst.ObjectInitializer(properties);
+ }
}
/**
@@ -59,6 +61,7 @@
NativeEmitter nativeEmitter;
CodeBuffer boundClosureBuffer;
CodeBuffer mainBuffer;
+ final CodeBuffer deferredBuffer = new CodeBuffer();
/** Shorter access to [isolatePropertiesName]. Both here in the code, as
well as in the generated code. */
String isolateProperties;
@@ -67,6 +70,8 @@
// TODO(ngeoffray): remove this field.
Set<ClassElement> instantiatedClasses;
+ JavaScriptBackend get backend => compiler.backend;
+
String get _ => compiler.enableMinification ? "" : " ";
String get n => compiler.enableMinification ? "" : "\n";
String get N => compiler.enableMinification ? "\n" : ";\n";
@@ -113,6 +118,10 @@
nativeEmitter = new NativeEmitter(this);
}
+ void addComment(String comment, CodeBuffer buffer) {
+ buffer.add(jsAst.prettyPrint(js.comment(comment), compiler));
+ }
+
void computeRequiredTypeChecks() {
assert(checkedClasses == null);
checkedClasses = new Set<ClassElement>();
@@ -126,28 +135,26 @@
});
}
- js.Expression constantReference(Constant value) {
+ jsAst.Expression constantReference(Constant value) {
return constantEmitter.reference(value);
}
- js.Expression constantInitializerExpression(Constant value) {
+ jsAst.Expression constantInitializerExpression(Constant value) {
return constantEmitter.initializationExpression(value);
}
String get name => 'CodeEmitter';
- String get defineClassName
- => '${namer.isolateName}.\$defineClass';
String get currentGenerateAccessorName
=> '${namer.CURRENT_ISOLATE}.\$generateAccessor';
String get generateAccessorHolder
=> '$isolatePropertiesName.\$generateAccessor';
+ String get finishClassesProperty
+ => r'$finishClasses';
String get finishClassesName
- => '${namer.isolateName}.\$finishClasses';
+ => '${namer.isolateName}.$finishClassesProperty';
String get finishIsolateConstructorName
=> '${namer.isolateName}.\$finishIsolateConstructor';
- String get pendingClassesName
- => '${namer.isolateName}.\$pendingClasses';
String get isolatePropertiesName
=> '${namer.isolateName}.${namer.isolatePropertiesName}';
String get supportsProtoName
@@ -176,38 +183,55 @@
String needsSetterCode(String variable) => '($variable & 2) == 0';
String isRenaming(String variable) => '($variable & $RENAMING_FLAG) != 0';
- String get generateAccessorFunction {
- return """
-function generateAccessor(field, prototype) {
- var len = field.length;
- var lastCharCode = field.charCodeAt(len - 1);
- var needsAccessor = (lastCharCode & $SUFFIX_MASK) >= $FIRST_SUFFIX_CODE;
- if (needsAccessor) {
- var needsGetter = ${needsGetterCode('lastCharCode')};
- var needsSetter = ${needsSetterCode('lastCharCode')};
- var renaming = ${isRenaming('lastCharCode')};
- var accessorName = field = field.substring(0, len - 1);
- if (renaming) {
- var divider = field.indexOf(":");
- accessorName = field.substring(0, divider);
- field = field.substring(divider + 1);
- }
- if (needsGetter) {
- var getterString = "return this." + field + ";";
- prototype["${namer.getterPrefix}" + accessorName] =
- new Function(getterString);
- }
- if (needsSetter) {
- var setterString = "this." + field + " = v;";
- prototype["${namer.setterPrefix}" + accessorName] =
- new Function("v", setterString);
- }
- }
- return field;
-}""";
+ jsAst.FunctionDeclaration get generateAccessorFunction {
+ // function generateAccessor(field, prototype) {
+ jsAst.Fun fun = js.fun(['field', 'prototype'], [
+ js['var len = field.length'],
+ js['var lastCharCode = field.charCodeAt(len - 1)'],
+ js['var needsAccessor = '
+ '(lastCharCode & $SUFFIX_MASK) >= $FIRST_SUFFIX_CODE'],
+
+ // if (needsAccessor) {
+ js.if_('needsAccessor', [
+ js['var needsGetter = ${needsGetterCode("lastCharCode")}'],
+ js['var needsSetter = ${needsSetterCode("lastCharCode")}'],
+ js['var renaming = ${isRenaming("lastCharCode")}'],
+ js['var accessorName = field = field.substring(0, len - 1)'],
+
+ // if (renaming) {
+ js.if_('renaming', [
+ js['var divider = field.indexOf(":")'],
+ js['accessorName = field.substring(0, divider)'],
+ js['field = field.substring(divider + 1)']
+ ]),
+
+ // if (needsGetter) {
+ js.if_('needsGetter', [
+ js['var getterString = "return this." + field'],
+ js['prototype["${namer.getterPrefix}" + accessorName] = '
+ 'new Function(getterString)']
+ ]),
+
+ // if (needsSetter) {
+ js.if_('needsSetter', [
+ // var setterString = "this." + field + " = v;";
+ js['var setterString = "this." + field + "$_=${_}v"'],
+ js['prototype["${namer.setterPrefix}" + accessorName] = '
+ 'new Function("v", setterString)']
+ ]),
+
+ ]),
+
+ // return field;
+ js.return_('field')
+ ]);
+
+ return new jsAst.FunctionDeclaration(
+ new jsAst.VariableDeclaration('generateAccessor'),
+ fun);
}
- String get defineClassFunction {
+ jsAst.Fun get defineClassFunction {
// First the class name, then the field names in an array and the members
// (inside an Object literal).
// The caller can also pass in the constructor as a function if needed.
@@ -221,33 +245,44 @@
// this.x = t - v;
// },
// });
- return """
-function(cls, fields, prototype) {
- var constructor;
- if (typeof fields == 'function') {
- constructor = fields;
- } else {
- var str = "function " + cls + "(";
- var body = "";
- for (var i = 0; i < fields.length; i++) {
- if (i != 0) str += ", ";
- var field = fields[i];
- field = generateAccessor(field, prototype);
- str += field;
- body += "this." + field + " = " + field + ";\\n";
- }
- str += ") {" + body + "}\\n";
- str += "return " + cls + ";";
- constructor = new Function(str)();
- }
- constructor.prototype = prototype;
- constructor.builtin\$cls = cls;
- return constructor;
-}""";
+
+ // function(cls, fields, prototype) {
+ return js.fun(['cls', 'fields', 'prototype'], [
+ js['var constructor'],
+
+ // if (typeof fields == 'function') {
+ js.if_(js["typeof fields == 'function'"], [
+ js['constructor = fields']
+ ], /* else */ [
+ js['var str = "function " + cls + "("'],
+ js['var body = ""'],
+
+ // for (var i = 0; i < fields.length; i++) {
+ js.for_(js['var i = 0'], js['i < fields.length'], js['i++'], [
+ // if (i != 0) str += ", ";
+ js.if_(js['i != 0'], js['str += ", "']),
+
+ js['var field = fields[i]'],
+ js['field = generateAccessor(field, prototype)'],
+ js['str += field'],
+ js['body += ("this." + field + " = " + field + ";\\n")']
+ ]),
+
+ js['str += (") {" + body + "}\\nreturn " + cls)'],
+
+ js['constructor = (new Function(str))()']
+ ]),
+
+ js['constructor.prototype = prototype'],
+ js['constructor.builtin\$cls = cls'],
+
+ // return constructor;
+ js.return_('constructor')
+ ]);
}
/** Needs defineClass to be defined. */
- String get protoSupportCheck {
+ List buildProtoSupportCheck() {
// On Firefox and Webkit browsers we can manipulate the __proto__
// directly. Opera claims to have __proto__ support, but it is buggy.
// So we have to do more checks.
@@ -256,21 +291,25 @@
// If the browser does not support __proto__ we need to instantiate an
// object with the correct (internal) prototype set up correctly, and then
// copy the members.
+ // TODO(8541): Remove this work around.
- return '''
-var $supportsProtoName = false;
-var tmp = $defineClassName('c', ['f?'], {}).prototype;
-if (tmp.__proto__) {
- tmp.__proto__ = {};
- if (typeof tmp.get\$f !== 'undefined') $supportsProtoName = true;
-}
-''';
+ return [
+ js['var $supportsProtoName = false'],
+ js["var tmp = (defineClass('c', ['f?'], {})).prototype"],
+
+ js.if_(js['tmp.__proto__'], [
+ js['tmp.__proto__ = {}'],
+ js.if_(js[r"typeof tmp.get$f != 'undefined'"],
+ js['$supportsProtoName = true'])
+
+ ])
+ ];
}
- String get finishClassesFunction {
- // 'defineClass' does not require the classes to be constructed in order.
- // Classes are initially just stored in the 'pendingClasses' field.
- // 'finishClasses' takes all pending classes and sets up the prototype.
+ jsAst.Fun get finishClassesFunction {
+ // Class descriptions are collected in a JS object.
+ // 'finishClasses' takes all collected descriptions and sets up
+ // the prototype.
// Once set up, the constructors prototype field satisfy:
// - it contains all (local) members.
// - its internal prototype (__proto__) points to the superclass'
@@ -280,67 +319,124 @@
// For engines where we have access to the '__proto__' we can manipulate
// the object literal directly. For other engines we have to create a new
// object and copy over the members.
- return '''
-function(collectedClasses) {
- var hasOwnProperty = Object.prototype.hasOwnProperty;
- for (var cls in collectedClasses) {
- if (hasOwnProperty.call(collectedClasses, cls)) {
- var desc = collectedClasses[cls];
-'''/* The 'fields' are either a constructor function or a string encoding
- fields, constructor and superclass. Get the superclass and the fields
- in the format Super;field1,field2 from the null-string property on the
- descriptor. */'''
- var fields = desc[''], supr;
- if (typeof fields == 'string') {
- var s = fields.split(';'); supr = s[0];
- fields = s[1] == '' ? [] : s[1].split(',');
- } else {
- supr = desc['super'];
- }
- $isolatePropertiesName[cls] = $defineClassName(cls, fields, desc);
- if (supr) $pendingClassesName[cls] = supr;
- }
- }
- var pendingClasses = $pendingClassesName;
-'''/* FinishClasses can be called multiple times. This means that we need to
- clear the pendingClasses property. */'''
- $pendingClassesName = {};
- var finishedClasses = {};
- function finishClass(cls) {
-'''/* Opera does not support 'getOwnPropertyNames'. Therefore we use
- hasOwnProperty instead. */'''
- var hasOwnProperty = Object.prototype.hasOwnProperty;
- if (hasOwnProperty.call(finishedClasses, cls)) return;
- finishedClasses[cls] = true;
- var superclass = pendingClasses[cls];
-'''/* The superclass is only false (empty string) for Dart's Object class. */'''
- if (!superclass) return;
- finishClass(superclass);
- var constructor = $isolatePropertiesName[cls];
- var superConstructor = $isolatePropertiesName[superclass];
- var prototype = constructor.prototype;
- if ($supportsProtoName) {
- prototype.__proto__ = superConstructor.prototype;
- prototype.constructor = constructor;
- } else {
- function tmp() {};
- tmp.prototype = superConstructor.prototype;
- var newPrototype = new tmp();
- constructor.prototype = newPrototype;
- newPrototype.constructor = constructor;
- for (var member in prototype) {
- if (!member) continue; '''/* Short version of: if (member == '') */'''
- if (hasOwnProperty.call(prototype, member)) {
- newPrototype[member] = prototype[member];
- }
- }
- }
- }
- for (var cls in pendingClasses) finishClass(cls);
-}''';
+
+ // function(collectedClasses,
+ // isolateProperties,
+ // existingIsolateProperties) {
+ return js.fun(['collectedClasses', 'isolateProperties',
+ 'existingIsolateProperties'], [
+ js['var pendingClasses = {}'],
+
+ js['var hasOwnProperty = Object.prototype.hasOwnProperty'],
+
+ // for (var cls in collectedClasses) {
+ js.forIn('cls', 'collectedClasses', [
+ // if (hasOwnProperty.call(collectedClasses, cls)) {
+ js.if_(js['hasOwnProperty.call(collectedClasses, cls)'], [
+ js['var desc = collectedClasses[cls]'],
+
+ /* The 'fields' are either a constructor function or a
+ * string encoding fields, constructor and superclass. Get
+ * the superclass and the fields in the format
+ * Super;field1,field2 from the null-string property on the
+ * descriptor.
+ */
+ // var fields = desc[''], supr;
+ js["var fields = desc[''], supr"],
+
+ js.if_(js["typeof fields == 'string'"], [
+ js['var s = fields.split(";")'],
+ js['supr = s[0]'],
+ js["fields = s[1] == '' ? [] : s[1].split(',')"],
+ ], /* else */ [
+ js['supr = desc.super']
+ ]),
+
+ js['isolateProperties[cls] = defineClass(cls, fields, desc)'],
+
+ // if (supr) pendingClasses[cls] = supr;
+ js.if_(js['supr'], js['pendingClasses[cls] = supr'])
+ ])
+ ]),
+
+ js['var finishedClasses = {}'],
+
+ // function finishClass(cls) { ... }
+ buildFinishClass(),
+
+ // for (var cls in pendingClasses) finishClass(cls);
+ js.forIn('cls', 'pendingClasses', js['finishClass']('cls'))
+ ]);
}
- String get finishIsolateConstructorFunction {
+ jsAst.FunctionDeclaration buildFinishClass() {
+ // function finishClass(cls) {
+ jsAst.Fun fun = js.fun(['cls'], [
+
+ // TODO(8540): Remove this work around.
+ /* Opera does not support 'getOwnPropertyNames'. Therefore we use
+ hasOwnProperty instead. */
+ js['var hasOwnProperty = Object.prototype.hasOwnProperty'],
+
+ // if (hasOwnProperty.call(finishedClasses, cls)) return;
+ js.if_(js['hasOwnProperty.call(finishedClasses, cls)'],
+ js.return_()),
+
+ js['finishedClasses[cls] = true'],
+ js['var superclass = pendingClasses[cls]'],
+
+ /* The superclass is only false (empty string) for Dart's Object class. */
+ js.if_(js['!superclass'], js.return_()),
+ js['finishClass(superclass)'],
+ js['var constructor = isolateProperties[cls]'],
+ js['var superConstructor = isolateProperties[superclass]'],
+
+ // if (!superConstructor)
+ // superConstructor = existingIsolateProperties[superclass];
+ js.if_(js['superConstructor'].not,
+ js['superConstructor'].assign(
+ js['existingIsolateProperties'][js['superclass']])),
+
+ js['var prototype = constructor.prototype'],
+
+ // if ($supportsProtoName) {
+ js.if_(supportsProtoName, [
+ js['prototype.__proto__ = superConstructor.prototype'],
+ js['prototype.constructor = constructor'],
+
+ ], /* else */ [
+ // function tmp() {};
+ new jsAst.FunctionDeclaration(
+ new jsAst.VariableDeclaration('tmp'),
+ js.fun([], [])),
+
+ js['tmp.prototype = superConstructor.prototype'],
+ js['var newPrototype = new tmp()'],
+
+ js['constructor.prototype = newPrototype'],
+ js['newPrototype.constructor = constructor'],
+
+ // for (var member in prototype) {
+ js.forIn('member', 'prototype', [
+ /* Short version of: if (member == '') */
+ // if (!member) continue;
+ js.if_(js['!member'], new jsAst.Continue(null)),
+
+ // if (hasOwnProperty.call(prototype, member)) {
+ js.if_(js['hasOwnProperty.call(prototype, member)'], [
+ js['newPrototype[member] = prototype[member]']
+ ])
+ ])
+
+ ])
+ ]);
+
+ return new jsAst.FunctionDeclaration(
+ new jsAst.VariableDeclaration('finishClass'),
+ fun);
+ }
+
+ jsAst.Fun get finishIsolateConstructorFunction {
String isolate = namer.isolateName;
// We replace the old Isolate function with a new one that initializes
// all its field with the initial (and often final) value of all globals.
@@ -362,91 +458,146 @@
//
// We also copy over old values like the prototype, and the
// isolateProperties themselves.
- return """function(oldIsolate) {
- var isolateProperties = oldIsolate.${namer.isolatePropertiesName};
- var isolatePrototype = oldIsolate.prototype;
- var str = "{\\n";
- str += "var properties = $isolate.${namer.isolatePropertiesName};\\n";
- for (var staticName in isolateProperties) {
- if (Object.prototype.hasOwnProperty.call(isolateProperties, staticName)) {
- str += "this." + staticName + "= properties." + staticName + ";\\n";
- }
- }
- str += "}\\n";
- var newIsolate = new Function(str);
- newIsolate.prototype = isolatePrototype;
- isolatePrototype.constructor = newIsolate;
- newIsolate.${namer.isolatePropertiesName} = isolateProperties;
- return newIsolate;
-}""";
- }
- String get lazyInitializerFunction {
- String isolate = namer.CURRENT_ISOLATE;
- return """
-function(prototype, staticName, fieldName, getterName, lazyValue) {
- var getter = new Function("{ return $isolate." + fieldName + ";}");
-$lazyInitializerLogic
-}""";
- }
-
- String get lazyInitializerLogic {
- String isolate = namer.CURRENT_ISOLATE;
- JavaScriptBackend backend = compiler.backend;
- String cyclicThrow = namer.isolateAccess(backend.cyclicThrowHelper);
- return """
- var sentinelUndefined = {};
- var sentinelInProgress = {};
- prototype[fieldName] = sentinelUndefined;
- prototype[getterName] = function() {
- var result = $isolate[fieldName];
- try {
- if (result === sentinelUndefined) {
- $isolate[fieldName] = sentinelInProgress;
- try {
- result = $isolate[fieldName] = lazyValue();
- } finally {
-""" // Use try-finally, not try-catch/throw as it destroys the stack trace.
-"""
- if (result === sentinelUndefined) {
- if ($isolate[fieldName] === sentinelInProgress) {
- $isolate[fieldName] = null;
- }
- }
- }
- } else if (result === sentinelInProgress) {
- $cyclicThrow(staticName);
- }
- return result;
- } finally {
- $isolate[getterName] = getter;
- }
- };""";
- }
-
- void addDefineClassAndFinishClassFunctionsIfNecessary(CodeBuffer buffer) {
+ List copyFinishClasses = [];
if (needsDefineClass) {
- // Declare function called generateAccessor. This is used in
+ copyFinishClasses.add(
+ // newIsolate.$finishClasses = oldIsolate.\$finishClasses;
+ js['newIsolate'][finishClassesProperty].assign(
+ js['oldIsolate'][finishClassesProperty]));
+ }
+
+ // function(oldIsolate) {
+ return js.fun('oldIsolate', [
+ js['var isolateProperties = oldIsolate.${namer.isolatePropertiesName}'],
+
+ js[r'isolateProperties.$currentScript ='
+ 'typeof document == "object" ?'
+ '(document.currentScript ||'
+ 'document.scripts[document.scripts.length - 1]) :'
+ 'null'],
+
+ js['var isolatePrototype = oldIsolate.prototype'],
+ js['var str = "{\\n"'],
+ js['str += '
+ '"var properties = $isolate.${namer.isolatePropertiesName};\\n"'],
+ js['var hasOwnProperty = Object.prototype.hasOwnProperty'],
+
+ // for (var staticName in isolateProperties) {
+ js.forIn('staticName', 'isolateProperties', [
+ js.if_(js['hasOwnProperty.call(isolateProperties, staticName)'], [
+ js['str += ("this." + staticName + "= properties." + staticName + '
+ '";\\n")']
+ ])
+ ]),
+
+ js['str += "}\\n"'],
+
+ js['var newIsolate = new Function(str)'],
+ js['newIsolate.prototype = isolatePrototype'],
+ js['isolatePrototype.constructor = newIsolate'],
+ js['newIsolate.${namer.isolatePropertiesName} = isolateProperties'],
+ ]..addAll(copyFinishClasses)
+ ..addAll([
+
+ // return newIsolate;
+ js.return_('newIsolate')
+ ]));
+ }
+
+ jsAst.Fun get lazyInitializerFunction {
+ String isolate = namer.CURRENT_ISOLATE;
+
+ // function(prototype, staticName, fieldName, getterName, lazyValue) {
+ var parameters = <String>['prototype', 'staticName', 'fieldName',
+ 'getterName', 'lazyValue'];
+ return js.fun(parameters, [
+ js['var getter = new Function("{ return $isolate." + fieldName + ";}")'],
+ ]..addAll(addLazyInitializerLogic())
+ );
+ }
+
+ List addLazyInitializerLogic() {
+ String isolate = namer.CURRENT_ISOLATE;
+ String cyclicThrow = namer.isolateAccess(backend.getCyclicThrowHelper());
+
+ return [
+ js['var sentinelUndefined = {}'],
+ js['var sentinelInProgress = {}'],
+ js['prototype[fieldName] = sentinelUndefined'],
+
+ // prototype[getterName] = function() {
+ js['prototype'][js['getterName']].assign(js.fun([], [
+ js['var result = $isolate[fieldName]'],
+
+ // try {
+ js.try_([
+ js.if_(js['result === sentinelUndefined'], [
+ js['$isolate[fieldName] = sentinelInProgress'],
+
+ // try {
+ js.try_([
+ js['result = $isolate[fieldName] = lazyValue()'],
+
+ ], finallyPart: [
+ // Use try-finally, not try-catch/throw as it destroys the
+ // stack trace.
+
+ // if (result === sentinelUndefined) {
+ js.if_(js['result === sentinelUndefined'], [
+ // if ($isolate[fieldName] === sentinelInProgress) {
+ js.if_(js['$isolate[fieldName] === sentinelInProgress'], [
+ js['$isolate[fieldName] = null'],
+ ])
+ ])
+ ])
+ ], /* else */ [
+ js.if_(js['result === sentinelInProgress'],
+ js['$cyclicThrow(staticName)']
+ )
+ ]),
+
+ // return result;
+ js.return_('result')
+
+ ], finallyPart: [
+ js['$isolate[getterName] = getter']
+ ])
+ ]))
+ ];
+ }
+
+ List buildDefineClassAndFinishClassFunctionsIfNecessary() {
+ if (!needsDefineClass) return [];
+ return [
+ // Declare a function called "generateAccessor". This is used in
// defineClassFunction (it's a local declaration in init()).
- buffer.add("$generateAccessorFunction$N");
- buffer.add("$generateAccessorHolder = generateAccessor$N");
- buffer.add("$defineClassName = $defineClassFunction$N");
- buffer.add(protoSupportCheck);
- buffer.add("$pendingClassesName = {}$N");
- buffer.add("$finishClassesName = $finishClassesFunction$N");
- }
+ generateAccessorFunction,
+
+ js['$generateAccessorHolder = generateAccessor'],
+
+ // function defineClass ...
+ new jsAst.FunctionDeclaration(
+ new jsAst.VariableDeclaration('defineClass'), defineClassFunction)
+ ]
+ ..addAll(buildProtoSupportCheck())
+ ..addAll([
+ js[finishClassesName].assign(finishClassesFunction)
+ ]);
}
- void addLazyInitializerFunctionIfNecessary(CodeBuffer buffer) {
- if (needsLazyInitializer) {
- buffer.add("$lazyInitializerName = $lazyInitializerFunction$N");
- }
+ List buildLazyInitializerFunctionIfNecessary() {
+ if (!needsLazyInitializer) return [];
+
+ // $lazyInitializerName = $lazyInitializerFunction
+ return [js[lazyInitializerName].assign(lazyInitializerFunction)];
}
- void emitFinishIsolateConstructor(CodeBuffer buffer) {
- String name = finishIsolateConstructorName;
- String value = finishIsolateConstructorFunction;
- buffer.add("$name = $value$N");
+ List buildFinishIsolateConstructor() {
+ return [
+ // $finishIsolateConstructorName = $finishIsolateConstructorFunction
+ js[finishIsolateConstructorName].assign(finishIsolateConstructorFunction)
+ ];
}
void emitFinishIsolateConstructorInvocation(CodeBuffer buffer) {
@@ -486,7 +637,6 @@
if (alreadyGenerated.contains(invocationName)) return;
alreadyGenerated.add(invocationName);
- JavaScriptBackend backend = compiler.backend;
bool isInterceptorClass =
backend.isInterceptorClass(member.getEnclosingClass());
@@ -499,19 +649,19 @@
String receiverArgumentName = r'$receiver';
// The parameters that this stub takes.
- List<js.Parameter> parametersBuffer =
- new List<js.Parameter>.fixedLength(
+ List<jsAst.Parameter> parametersBuffer =
+ new List<jsAst.Parameter>.fixedLength(
selector.argumentCount + extraArgumentCount);
// The arguments that will be passed to the real method.
- List<js.Expression> argumentsBuffer =
- new List<js.Expression>.fixedLength(
+ List<jsAst.Expression> argumentsBuffer =
+ new List<jsAst.Expression>.fixedLength(
parameters.parameterCount + extraArgumentCount);
int count = 0;
if (isInterceptorClass) {
count++;
- parametersBuffer[0] = new js.Parameter(receiverArgumentName);
- argumentsBuffer[0] = new js.VariableUse(receiverArgumentName);
+ parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName);
+ argumentsBuffer[0] = js[receiverArgumentName];
}
int indexOfLastOptionalArgumentInParameters = positionalArgumentCount - 1;
@@ -523,17 +673,17 @@
assert(jsName != receiverArgumentName);
int optionalParameterStart = positionalArgumentCount + extraArgumentCount;
if (count < optionalParameterStart) {
- parametersBuffer[count] = new js.Parameter(jsName);
- argumentsBuffer[count] = new js.VariableUse(jsName);
+ parametersBuffer[count] = new jsAst.Parameter(jsName);
+ argumentsBuffer[count] = js[jsName];
} else {
int index = names.indexOf(element.name);
if (index != -1) {
indexOfLastOptionalArgumentInParameters = count;
// The order of the named arguments is not the same as the
// one in the real method (which is in Dart source order).
- argumentsBuffer[count] = new js.VariableUse(jsName);
+ argumentsBuffer[count] = js[jsName];
parametersBuffer[optionalParameterStart + index] =
- new js.Parameter(jsName);
+ new jsAst.Parameter(jsName);
// Note that [elements] may be null for a synthesized [member].
} else if (elements != null && elements.isParameterChecked(element)) {
argumentsBuffer[count] = constantReference(SentinelConstant.SENTINEL);
@@ -554,20 +704,16 @@
count++;
});
- List<js.Statement> body;
+ List body;
if (member.hasFixedBackendName()) {
body = nativeEmitter.generateParameterStubStatements(
member, invocationName, parametersBuffer, argumentsBuffer,
indexOfLastOptionalArgumentInParameters);
} else {
- body = <js.Statement>[
- new js.Return(
- new js.VariableUse('this')
- .dot(namer.getName(member))
- .callWith(argumentsBuffer))];
+ body = [js.return_(js['this'][namer.getName(member)](argumentsBuffer))];
}
- js.Fun function = new js.Fun(parametersBuffer, new js.Block(body));
+ jsAst.Fun function = js.fun(parametersBuffer, body);
defineStub(invocationName, function);
}
@@ -725,8 +871,7 @@
|| member.isGenerativeConstructorBody()
|| member.isAccessor()) {
if (member.isAbstract(compiler)) return;
- JavaScriptBackend backend = compiler.backend;
- js.Expression code = backend.generatedCode[member];
+ jsAst.Expression code = backend.generatedCode[member];
if (code == null) return;
builder.addProperty(namer.getName(member), code);
code = backend.generatedBailoutCode[member];
@@ -753,7 +898,6 @@
void emitInstanceMembers(ClassElement classElement,
ClassBuilder builder) {
assert(invariant(classElement, classElement.isDeclaration));
- JavaScriptBackend backend = compiler.backend;
if (classElement == backend.objectInterceptorClass) {
emitInterceptorMethods(builder);
// The ObjectInterceptor does not have any instance methods.
@@ -788,12 +932,12 @@
includeSuperMembers: false);
void generateIsTest(Element other) {
- js.Expression code;
+ jsAst.Expression code;
if (compiler.objectClass == other) return;
if (nativeEmitter.requiresNativeIsCheck(other)) {
- code = js.fun([], js.block1(js.return_(new js.LiteralBool(true))));
+ code = js.fun([], [js.return_(true)]);
} else {
- code = new js.LiteralBool(true);
+ code = new jsAst.LiteralBool(true);
}
builder.addProperty(namer.operatorIs(other), code);
}
@@ -801,21 +945,20 @@
void generateSubstitution(Element other, {bool emitNull: false}) {
RuntimeTypeInformation rti = backend.rti;
// TODO(karlklose): support typedefs with variables.
- js.Expression expression;
+ jsAst.Expression expression;
bool needsNativeCheck = nativeEmitter.requiresNativeIsCheck(other);
if (other.kind == ElementKind.CLASS) {
String substitution = rti.getSupertypeSubstitution(classElement, other,
alwaysGenerateFunction: true);
if (substitution != null) {
- expression = new js.LiteralExpression(substitution);
+ expression = new jsAst.LiteralExpression(substitution);
} else if (emitNull || needsNativeCheck) {
- expression = new js.LiteralNull();
+ expression = new jsAst.LiteralNull();
}
}
if (expression != null) {
if (needsNativeCheck) {
- expression =
- new js.Fun([], new js.Block([new js.Return(expression)]));
+ expression = js.fun([], js.return_(expression));
}
builder.addProperty(namer.substitutionName(other), expression);
}
@@ -847,12 +990,11 @@
? js.equals
: js.strictEquals;
builder.addProperty(name, js.fun(['receiver', 'a'],
- js.block1(js.return_(kind(js.use('receiver'), js.use('a'))))));
+ js.block(js.return_(kind(js['receiver'], js['a'])))));
}
}
void emitRuntimeClassesAndTests(CodeBuffer buffer) {
- JavaScriptBackend backend = compiler.backend;
RuntimeTypeInformation rti = backend.rti;
TypeChecks typeChecks = rti.getRequiredChecks();
@@ -1009,17 +1151,14 @@
ClassBuilder builder) {
String getterName = namer.getterNameFromAccessorName(accessorName);
builder.addProperty(getterName,
- js.fun([], js.block1(js.return_(js.use('this').dot(fieldName)))));
+ js.fun([], js.return_(js['this'][fieldName])));
}
void generateSetter(Element member, String fieldName, String accessorName,
ClassBuilder builder) {
String setterName = namer.setterNameFromAccessorName(accessorName);
builder.addProperty(setterName,
- js.fun(['v'],
- js.block1(
- new js.ExpressionStatement(
- js.assign(js.use('this').dot(fieldName), js.use('v'))))));
+ js.fun(['v'], js['this'][fieldName].assign('v')));
}
bool canGenerateCheckedSetter(Element member) {
@@ -1041,22 +1180,17 @@
DartType type = member.computeType(compiler);
// TODO(ahe): Generate a dynamic type error here.
if (type.element.isErroneous()) return;
- SourceString helper = compiler.backend.getCheckedModeHelper(type);
- FunctionElement helperElement = compiler.findHelper(helper);
+ FunctionElement helperElement
+ = backend.getCheckedModeHelper(type, typeCast: false);
String helperName = namer.isolateAccess(helperElement);
- List<js.Expression> arguments = <js.Expression>[js.use('v')];
+ List<jsAst.Expression> arguments = <jsAst.Expression>[js['v']];
if (helperElement.computeSignature(compiler).parameterCount != 1) {
arguments.add(js.string(namer.operatorIs(type.element)));
}
String setterName = namer.setterNameFromAccessorName(accessorName);
builder.addProperty(setterName,
- js.fun(['v'],
- js.block1(
- new js.ExpressionStatement(
- js.assign(
- js.use('this').dot(fieldName),
- js.call(js.use(helperName), arguments))))));
+ js.fun(['v'], js['this'][fieldName].assign(js[helperName](arguments))));
}
void emitClassConstructor(ClassElement classElement, ClassBuilder builder) {
@@ -1074,7 +1208,7 @@
bool isFirstField = true;
StringBuffer buffer = new StringBuffer();
if (!classIsNative) {
- buffer.add('$superClass;');
+ buffer.write('$superClass;');
}
visitClassFields(classElement, (Element member,
String name,
@@ -1093,30 +1227,30 @@
if (isFirstField) {
isFirstField = false;
} else {
- buffer.add(',');
+ buffer.write(',');
}
int flag = 0;
if (!needsAccessor) {
// Emit field for constructor generation.
assert(!classIsNative);
- buffer.add(name);
+ buffer.write(name);
} else {
// Emit (possibly renaming) field name so we can add accessors at
// runtime.
- buffer.add(accessorName);
+ buffer.write(accessorName);
if (name != accessorName) {
- buffer.add(':$name');
+ buffer.write(':$name');
// Only the native classes can have renaming accessors.
assert(classIsNative);
flag = RENAMING_FLAG;
}
}
if (needsGetter && needsSetter) {
- buffer.addCharCode(GETTER_SETTER_CODE + flag);
+ buffer.writeCharCode(GETTER_SETTER_CODE + flag);
} else if (needsGetter) {
- buffer.addCharCode(GETTER_CODE + flag);
+ buffer.writeCharCode(GETTER_CODE + flag);
} else if (needsSetter) {
- buffer.addCharCode(SETTER_CODE + flag);
+ buffer.writeCharCode(SETTER_CODE + flag);
}
}
});
@@ -1188,11 +1322,9 @@
emitClassGettersSetters(classElement, builder);
emitInstanceMembers(classElement, builder);
- js.Expression init =
- js.assign(
- js.use(classesCollector).dot(className),
- builder.toObjectInitializer());
- buffer.add(js.prettyPrint(init, compiler));
+ jsAst.Expression init =
+ js[classesCollector][className].assign(builder.toObjectInitializer());
+ buffer.add(jsAst.prettyPrint(init, compiler));
buffer.add('$N$n');
}
@@ -1213,34 +1345,33 @@
}
void emitInterceptorMethods(ClassBuilder builder) {
- JavaScriptBackend backend = compiler.backend;
// Emit forwarders for the ObjectInterceptor class. We need to
- // emit all possible sends on intercepted methods.
+ // emit all possible sends on intercepted methods. Because of
+ // typed selectors we have to avoid generating the same forwarder
+ // multiple times.
+ Set<String> alreadyGenerated = new Set<String>();
for (Selector selector in
backend.usedInterceptors.toList()..sort(_compareSelectorNames)) {
- List<js.Parameter> parameters = <js.Parameter>[];
- List<js.Expression> arguments = <js.Expression>[];
- parameters.add(new js.Parameter('receiver'));
-
String name = backend.namer.invocationName(selector);
+ if (alreadyGenerated.contains(name)) continue;
+ alreadyGenerated.add(name);
+
+ List<jsAst.Parameter> parameters = <jsAst.Parameter>[];
+ List<jsAst.Expression> arguments = <jsAst.Expression>[];
+ parameters.add(new jsAst.Parameter('receiver'));
+
if (selector.isSetter()) {
- parameters.add(new js.Parameter('value'));
- arguments.add(new js.VariableUse('value'));
+ parameters.add(new jsAst.Parameter('value'));
+ arguments.add(js['value']);
} else {
for (int i = 0; i < selector.argumentCount; i++) {
String argName = 'a$i';
- parameters.add(new js.Parameter(argName));
- arguments.add(new js.VariableUse(argName));
+ parameters.add(new jsAst.Parameter(argName));
+ arguments.add(js[argName]);
}
}
- js.Fun function =
- new js.Fun(parameters,
- new js.Block(
- <js.Statement>[
- new js.Return(
- new js.VariableUse('receiver')
- .dot(name)
- .callWith(arguments))]));
+ jsAst.Fun function =
+ js.fun(parameters, js.return_(js['receiver'][name](arguments)));
builder.addProperty(name, function);
}
}
@@ -1270,8 +1401,7 @@
emitSubstitution(cls);
}
- JavaScriptBackend jsBackend = compiler.backend;
- RuntimeTypeInformation rti = jsBackend.rti;
+ RuntimeTypeInformation rti = backend.rti;
ClassElement superclass = cls.superclass;
bool haveSameTypeVariables(ClassElement a, ClassElement b) {
@@ -1386,8 +1516,6 @@
// constructor that always throws. We never need to emit it.
unneededClasses.add(compiler.boolClass);
- JavaScriptBackend backend = compiler.backend;
-
// Go over specialized interceptors and then constants to know which
// interceptors are needed.
Set<ClassElement> needed = new Set<ClassElement>();
@@ -1449,20 +1577,23 @@
}
for (ClassElement element in sortedClasses) {
- generateClass(element, buffer);
+ generateClass(element, bufferForElement(element, buffer));
}
// The closure class could have become necessary because of the generation
// of stubs.
ClassElement closureClass = compiler.closureClass;
if (needsClosureClass && !instantiatedClasses.contains(closureClass)) {
- generateClass(closureClass, buffer);
+ generateClass(closureClass, bufferForElement(closureClass, buffer));
}
}
void emitFinishClassesInvocationIfNecessary(CodeBuffer buffer) {
if (needsDefineClass) {
- buffer.add("$finishClassesName($classesCollector)$N");
+ buffer.add('$finishClassesName($classesCollector,'
+ '$_$isolateProperties,'
+ '${_}null)$N');
+
// Reset the map.
buffer.add("$classesCollector$_=$_{}$N");
}
@@ -1470,15 +1601,14 @@
void emitStaticFunction(CodeBuffer buffer,
String name,
- js.Expression functionExpression) {
- js.Expression assignment =
- js.assign(js.use(isolateProperties).dot(name), functionExpression);
- buffer.add(js.prettyPrint(assignment, compiler));
+ jsAst.Expression functionExpression) {
+ jsAst.Expression assignment =
+ js[isolateProperties][name].assign(functionExpression);
+ buffer.add(jsAst.prettyPrint(assignment, compiler));
buffer.add('$N$n');
}
- void emitStaticFunctions(CodeBuffer buffer) {
- JavaScriptBackend backend = compiler.backend;
+ void emitStaticFunctions(CodeBuffer eagerBuffer) {
bool isStaticFunction(Element element) =>
!element.isInstanceMember() && !element.isField();
@@ -1490,9 +1620,10 @@
.toSet();
for (Element element in Elements.sortedByPosition(elements)) {
- js.Expression code = backend.generatedCode[element];
+ CodeBuffer buffer = bufferForElement(element, eagerBuffer);
+ jsAst.Expression code = backend.generatedCode[element];
emitStaticFunction(buffer, namer.getName(element), code);
- js.Expression bailoutCode = backend.generatedBailoutCode[element];
+ jsAst.Expression bailoutCode = backend.generatedBailoutCode[element];
if (bailoutCode != null) {
pendingElementsWithBailouts.remove(element);
emitStaticFunction(buffer, namer.getBailoutName(element), bailoutCode);
@@ -1502,7 +1633,8 @@
// Is it possible the primary function was inlined but the bailout was not?
for (Element element in
Elements.sortedByPosition(pendingElementsWithBailouts)) {
- js.Expression bailoutCode = backend.generatedBailoutCode[element];
+ CodeBuffer buffer = bufferForElement(element, eagerBuffer);
+ jsAst.Expression bailoutCode = backend.generatedBailoutCode[element];
emitStaticFunction(buffer, namer.getBailoutName(element), bailoutCode);
}
}
@@ -1512,6 +1644,8 @@
compiler.codegenWorld.staticFunctionsNeedingGetter;
for (FunctionElement element in
Elements.sortedByPosition(functionsNeedingGetter)) {
+ // TODO(ahe): Defer loading of these getters.
+
// The static function does not have the correct name. Since
// [addParameterStubs] use the name to create its stubs we simply
// create a fake element with the correct name.
@@ -1524,13 +1658,11 @@
String fieldAccess = '$isolateProperties.$staticName';
buffer.add("$fieldAccess.$invocationName$_=$_$fieldAccess$N");
- addParameterStubs(callElement, (String name, js.Expression value) {
- js.Expression assignment =
- js.assign(
- js.use(isolateProperties).dot(staticName).dot(name),
- value);
+ addParameterStubs(callElement, (String name, jsAst.Expression value) {
+ jsAst.Expression assignment =
+ js[isolateProperties][staticName][name].assign(value);
buffer.add(
- js.prettyPrint(new js.ExpressionStatement(assignment), compiler));
+ jsAst.prettyPrint(assignment.toStatement(), compiler));
buffer.add('$N');
});
@@ -1591,7 +1723,6 @@
String extraArg = null;
// Methods on interceptor classes take an extra parameter, which is the
// actual receiver of the call.
- JavaScriptBackend backend = compiler.backend;
bool inInterceptor = backend.isInterceptorClass(member.getEnclosingClass());
if (inInterceptor) {
cache = interceptorClosureCache;
@@ -1649,37 +1780,32 @@
String invocationName = namer.instanceMethodName(callElement);
List<String> parameters = <String>[];
- List<js.Expression> arguments = <js.Expression>[];
+ List<jsAst.Expression> arguments = <jsAst.Expression>[];
if (inInterceptor) {
- arguments.add(js.use('this').dot(fieldNames[2]));
+ arguments.add(js['this'][fieldNames[2]]);
}
for (int i = 0; i < parameterCount; i++) {
String name = 'p$i';
parameters.add(name);
- arguments.add(js.use(name));
+ arguments.add(js[name]);
}
- js.Expression fun =
- js.fun(parameters,
- js.block1(
- js.return_(
- new js.PropertyAccess(
- js.use('this').dot(fieldNames[0]),
- js.use('this').dot(fieldNames[1]))
- .callWith(arguments))));
+ jsAst.Expression fun = js.fun(
+ parameters,
+ js.return_(
+ js['this'][fieldNames[0]][js['this'][fieldNames[1]]](arguments)));
boundClosureBuilder.addProperty(invocationName, fun);
addParameterStubs(callElement, boundClosureBuilder.addProperty);
typedefChecks.forEach((Element typedef) {
String operator = namer.operatorIs(typedef);
- boundClosureBuilder.addProperty(operator, new js.LiteralBool(true));
+ boundClosureBuilder.addProperty(operator, new jsAst.LiteralBool(true));
});
- js.Expression init =
- js.assign(
- js.use(classesCollector).dot(mangledName),
+ jsAst.Expression init =
+ js[classesCollector][mangledName].assign(
boundClosureBuilder.toObjectInitializer());
- boundClosureBuffer.add(js.prettyPrint(init, compiler));
+ boundClosureBuffer.add(jsAst.prettyPrint(init, compiler));
boundClosureBuffer.add("$N");
closureClass = namer.isolateAccess(closureClassElement);
@@ -1695,19 +1821,17 @@
String targetName = namer.instanceMethodName(member);
List<String> parameters = <String>[];
- List<js.Expression> arguments = <js.Expression>[];
- arguments.add(js.use('this'));
+ List<jsAst.Expression> arguments = <jsAst.Expression>[];
+ arguments.add(js['this']);
arguments.add(js.string(targetName));
if (inInterceptor) {
parameters.add(extraArg);
- arguments.add(js.use(extraArg));
+ arguments.add(js[extraArg]);
}
- js.Expression getterFunction =
- js.fun(parameters,
- js.block1(
- js.return_(
- new js.New(js.use(closureClass), arguments))));
+ jsAst.Expression getterFunction = js.fun(
+ parameters,
+ js.return_(js[closureClass].newWith(arguments)));
defineStub(getterName, getterFunction);
}
@@ -1722,7 +1846,6 @@
DefineStubFunction defineStub) {
assert(invariant(member, member.isDeclaration));
LibraryElement memberLibrary = member.getLibrary();
- JavaScriptBackend backend = compiler.backend;
// If the class is an interceptor class, the stub gets the
// receiver explicitely and we need to pass it to the getter call.
bool isInterceptorClass =
@@ -1730,18 +1853,18 @@
const String receiverArgumentName = r'$receiver';
- js.Expression buildGetter() {
+ jsAst.Expression buildGetter() {
if (member.isGetter()) {
String getterName = namer.getterName(member);
- return new js.VariableUse('this').dot(getterName).callWith(
+ return js['this'][getterName](
isInterceptorClass
- ? <js.Expression>[new js.VariableUse(receiverArgumentName)]
- : <js.Expression>[]);
+ ? <jsAst.Expression>[js[receiverArgumentName]]
+ : <jsAst.Expression>[]);
} else {
String fieldName = member.hasFixedBackendName()
? member.fixedBackendName()
: namer.instanceFieldName(member);
- return new js.VariableUse('this').dot(fieldName);
+ return js['this'][fieldName];
}
}
@@ -1760,25 +1883,21 @@
Selector callSelector = new Selector.callClosureFrom(selector);
String closureCallName = namer.invocationName(callSelector);
- List<js.Parameter> parameters = <js.Parameter>[];
- List<js.Expression> arguments = <js.Expression>[];
+ List<jsAst.Parameter> parameters = <jsAst.Parameter>[];
+ List<jsAst.Expression> arguments = <jsAst.Expression>[];
if (isInterceptorClass) {
- parameters.add(new js.Parameter(receiverArgumentName));
+ parameters.add(new jsAst.Parameter(receiverArgumentName));
}
for (int i = 0; i < selector.argumentCount; i++) {
String name = 'arg$i';
- parameters.add(new js.Parameter(name));
- arguments.add(new js.VariableUse(name));
+ parameters.add(new jsAst.Parameter(name));
+ arguments.add(js[name]);
}
- js.Fun function =
- new js.Fun(parameters,
- new js.Block(
- <js.Statement>[
- new js.Return(
- buildGetter().dot(closureCallName)
- .callWith(arguments))]));
+ jsAst.Fun function = js.fun(
+ parameters,
+ js.return_(buildGetter()[closureCallName](arguments)));
defineStub(invocationName, function);
}
@@ -1792,13 +1911,10 @@
for (Element element in Elements.sortedByPosition(staticNonFinalFields)) {
compiler.withCurrentElement(element, () {
Constant initialValue = handler.getInitialValueFor(element);
- js.Expression init =
- new js.Assignment(
- new js.PropertyAccess.field(
- new js.VariableUse(isolateProperties),
- namer.getName(element)),
- constantEmitter.referenceInInitializationContext(initialValue));
- buffer.add(js.prettyPrint(init, compiler));
+ jsAst.Expression init =
+ js[isolateProperties][namer.getName(element)].assign(
+ constantEmitter.referenceInInitializationContext(initialValue));
+ buffer.add(jsAst.prettyPrint(init, compiler));
buffer.add('$N');
});
}
@@ -1808,36 +1924,35 @@
ConstantHandler handler = compiler.constantHandler;
List<VariableElement> lazyFields =
handler.getLazilyInitializedFieldsForEmission();
- JavaScriptBackend backend = compiler.backend;
if (!lazyFields.isEmpty) {
needsLazyInitializer = true;
for (VariableElement element in Elements.sortedByPosition(lazyFields)) {
assert(backend.generatedBailoutCode[element] == null);
- js.Expression code = backend.generatedCode[element];
+ jsAst.Expression code = backend.generatedCode[element];
assert(code != null);
// The code only computes the initial value. We build the lazy-check
// here:
// lazyInitializer(prototype, 'name', fieldName, getterName, initial);
// The name is used for error reporting. The 'initial' must be a
// closure that constructs the initial value.
- List<js.Expression> arguments = <js.Expression>[];
- arguments.add(js.use(isolateProperties));
+ List<jsAst.Expression> arguments = <jsAst.Expression>[];
+ arguments.add(js[isolateProperties]);
arguments.add(js.string(element.name.slowToString()));
arguments.add(js.string(namer.getName(element)));
arguments.add(js.string(namer.getLazyInitializerName(element)));
arguments.add(code);
- js.Expression getter = buildLazyInitializedGetter(element);
+ jsAst.Expression getter = buildLazyInitializedGetter(element);
if (getter != null) {
arguments.add(getter);
}
- js.Expression init = js.call(js.use(lazyInitializerName), arguments);
- buffer.add(js.prettyPrint(init, compiler));
+ jsAst.Expression init = js[lazyInitializerName](arguments);
+ buffer.add(jsAst.prettyPrint(init, compiler));
buffer.add("$N");
}
}
}
- js.Expression buildLazyInitializedGetter(VariableElement element) {
+ jsAst.Expression buildLazyInitializedGetter(VariableElement element) {
// Nothing to do, the 'lazy' function will create the getter.
return null;
}
@@ -1861,13 +1976,9 @@
addedMakeConstantList = true;
emitMakeConstantList(buffer);
}
- js.Expression init =
- new js.Assignment(
- new js.PropertyAccess.field(
- new js.VariableUse(isolateProperties),
- name),
- constantInitializerExpression(constant));
- buffer.add(js.prettyPrint(init, compiler));
+ jsAst.Expression init = js[isolateProperties][name].assign(
+ constantInitializerExpression(constant));
+ buffer.add(jsAst.prettyPrint(init, compiler));
buffer.add('$N');
}
}
@@ -1917,35 +2028,17 @@
// do not introduce duplicates (bad for code size).
Set<String> addedJsNames = new Set<String>();
- // Keep track of the noSuchMethod holders for each possible
- // receiver type.
- Map<ClassElement, Set<ClassElement>> noSuchMethodHolders =
- new Map<ClassElement, Set<ClassElement>>();
- Set<ClassElement> noSuchMethodHoldersFor(DartType type) {
- ClassElement element = type.element;
- Set<ClassElement> result = noSuchMethodHolders[element];
- if (result == null) {
- // For now, we check the entire world to see if an object of
- // the given type may have a user-defined noSuchMethod
- // implementation. We could do better by only looking at
- // instantiated (or otherwise needed) classes.
- result = compiler.world.findNoSuchMethodHolders(type);
- noSuchMethodHolders[element] = result;
- }
- return result;
- }
-
- js.Expression generateMethod(String jsName, Selector selector) {
+ jsAst.Expression generateMethod(String jsName, Selector selector) {
// Values match JSInvocationMirror in js-helper library.
int type = selector.invocationMirrorKind;
String methodName = selector.invocationMirrorMemberName;
- List<js.Parameter> parameters = <js.Parameter>[];
+ List<jsAst.Parameter> parameters = <jsAst.Parameter>[];
CodeBuffer args = new CodeBuffer();
for (int i = 0; i < selector.argumentCount; i++) {
- parameters.add(new js.Parameter('\$$i'));
+ parameters.add(new jsAst.Parameter('\$$i'));
}
- List<js.Expression> argNames =
+ List<jsAst.Expression> argNames =
selector.getOrderedNamedArguments().map((SourceString name) =>
js.string(name.slowToString())).toList();
@@ -1954,26 +2047,15 @@
String createInvocationMirror = namer.getName(
compiler.createInvocationMirrorElement);
- js.Expression expression =
- new js.This()
- .dot(noSuchMethodName)
- .callWith(
- <js.Expression>[
- new js.VariableUse(namer.CURRENT_ISOLATE)
- .dot(createInvocationMirror)
- .callWith(
- <js.Expression>[
- js.string(methodName),
- js.string(internalName),
- new js.LiteralNumber('$type'),
- new js.ArrayInitializer.from(
- parameters.map((param) => js.use(param.name))
- .toList()),
- new js.ArrayInitializer.from(argNames)])]);
- js.Expression function =
- new js.Fun(parameters,
- new js.Block(<js.Statement>[new js.Return(expression)]));
- return function;
+ jsAst.Expression expression = js['this.$noSuchMethodName'](
+ js[namer.CURRENT_ISOLATE][createInvocationMirror]([
+ js.string(methodName),
+ js.string(internalName),
+ type,
+ new jsAst.ArrayInitializer.from(
+ parameters.map((param) => js[param.name]).toList()),
+ new jsAst.ArrayInitializer.from(argNames)]));
+ return js.fun(parameters, js.return_(expression));
}
void addNoSuchMethodHandlers(SourceString ignore, Set<Selector> selectors) {
@@ -2011,11 +2093,10 @@
// If the selector is typed, we check to see if that type may
// have a user-defined noSuchMethod implementation. If not, we
// skip the selector altogether.
- DartType receiverType = objectType;
ClassElement receiverClass = objectClass;
if (selector is TypedSelector) {
TypedSelector typedSelector = selector;
- receiverType = typedSelector.receiverType;
+ DartType receiverType = typedSelector.receiverType;
receiverClass = receiverType.element;
}
@@ -2061,11 +2142,12 @@
// If we're calling bar on an object of type A we do need the
// handler because we may have to call B.noSuchMethod since B
// does not implement bar.
- Set<ClassElement> holders = noSuchMethodHoldersFor(receiverType);
+ Iterable<ClassElement> holders =
+ compiler.world.locateNoSuchMethodHolders(selector);
if (holders.every(hasMatchingMember)) continue;
String jsName = namer.invocationMirrorInternalName(selector);
if (!addedJsNames.contains(jsName)) {
- js.Expression method = generateMethod(jsName, selector);
+ jsAst.Expression method = generateMethod(jsName, selector);
defineStub(jsName, method);
addedJsNames.add(jsName);
}
@@ -2103,14 +2185,7 @@
} else {
mainCall = '${namer.isolateAccess(main)}()';
}
- if (!compiler.enableMinification) {
- buffer.add("""
-
-//
-// BEGIN invoke [main].
-//
-""");
- }
+ addComment('BEGIN invoke [main].', buffer);
buffer.add("""
if (typeof document !== 'undefined' && document.readyState !== 'complete') {
document.addEventListener('readystatechange', function () {
@@ -2130,50 +2205,39 @@
}
}
""");
- if (!compiler.enableMinification) {
- buffer.add("""
-//
-// END invoke [main].
-//
-
-""");
- }
+ addComment('END invoke [main].', buffer);
}
void emitGetInterceptorMethod(CodeBuffer buffer,
String objectName,
String key,
Collection<ClassElement> classes) {
- js.Statement buildReturnInterceptor(ClassElement cls) {
- return js.return_(js.fieldAccess(js.use(namer.isolateAccess(cls)),
- 'prototype'));
+ jsAst.Statement buildReturnInterceptor(ClassElement cls) {
+ return js.return_(js[namer.isolateAccess(cls)]['prototype']);
}
- js.VariableUse receiver = js.use('receiver');
- JavaScriptBackend backend = compiler.backend;
-
+ jsAst.VariableUse receiver = js['receiver'];
/**
* Build a JavaScrit AST node for doing a type check on
* [cls]. [cls] must be an interceptor class.
*/
- js.Statement buildInterceptorCheck(ClassElement cls) {
- js.Expression condition;
+ jsAst.Statement buildInterceptorCheck(ClassElement cls) {
+ jsAst.Expression condition;
assert(backend.isInterceptorClass(cls));
if (cls == backend.jsBoolClass) {
- condition = js.equals(js.typeOf(receiver), js.string('boolean'));
+ condition = receiver.typeof.equals(js.string('boolean'));
} else if (cls == backend.jsIntClass ||
cls == backend.jsDoubleClass ||
cls == backend.jsNumberClass) {
throw 'internal error';
} else if (cls == backend.jsArrayClass) {
- condition = js.equals(js.fieldAccess(receiver, 'constructor'),
- js.use('Array'));
+ condition = receiver['constructor'].equals('Array');
} else if (cls == backend.jsStringClass) {
- condition = js.equals(js.typeOf(receiver), js.string('string'));
+ condition = receiver.typeof.equals(js.string('string'));
} else if (cls == backend.jsNullClass) {
- condition = js.equals(receiver, new js.LiteralNull());
+ condition = receiver.equals(new jsAst.LiteralNull());
} else if (cls == backend.jsFunctionClass) {
- condition = js.equals(js.typeOf(receiver), js.string('function'));
+ condition = receiver.typeof.equals(js.string('function'));
} else {
throw 'internal error';
}
@@ -2205,31 +2269,28 @@
}
if (hasInt) hasNumber = true;
- js.Block block = new js.Block.empty();
+ jsAst.Block block = new jsAst.Block.empty();
if (hasNumber) {
- js.Statement whenNumber;
+ jsAst.Statement whenNumber;
/// Note: there are two number classes in play: Dart's [num],
/// and JavaScript's Number (typeof receiver == 'number'). This
/// is the fallback used when we have determined that receiver
/// is a JavaScript Number.
- js.Return returnNumberClass = buildReturnInterceptor(
+ jsAst.Return returnNumberClass = buildReturnInterceptor(
hasDouble ? backend.jsDoubleClass : backend.jsNumberClass);
if (hasInt) {
- js.Expression isInt =
- js.equals(js.call(js.fieldAccess(js.use('Math'), 'floor'),
- [receiver]),
- receiver);
- (whenNumber = js.emptyBlock()).statements
- ..add(js.if_(isInt, buildReturnInterceptor(backend.jsIntClass)))
- ..add(returnNumberClass);
+ jsAst.Expression isInt = js['Math']['floor'](receiver).equals(receiver);
+ whenNumber = js.block([
+ js.if_(isInt, buildReturnInterceptor(backend.jsIntClass)),
+ returnNumberClass]);
} else {
whenNumber = returnNumberClass;
}
block.statements.add(
- js.if_(js.equals(js.typeOf(receiver), js.string('number')),
+ js.if_(receiver.typeof.equals(js.string('number')),
whenNumber));
}
@@ -2242,7 +2303,7 @@
// Returning "undefined" here will provoke a JavaScript
// TypeError which is later identified as a null-error by
// [unwrapException] in js_helper.dart.
- block.statements.add(js.if_(js.equals(receiver, new js.LiteralNull()),
+ block.statements.add(js.if_(receiver.equals(new jsAst.LiteralNull()),
js.return_(js.undefined())));
}
if (hasFunction) {
@@ -2256,12 +2317,11 @@
if (hasArray) {
block.statements.add(buildInterceptorCheck(backend.jsArrayClass));
}
- block.statements.add(js.return_(js.fieldAccess(js.use(objectName),
- 'prototype')));
+ block.statements.add(js.return_(js[objectName]['prototype']));
- js.PropertyAccess name = js.fieldAccess(js.use(isolateProperties), key);
- buffer.add(js.prettyPrint(js.assign(name, js.fun(['receiver'], block)),
- compiler));
+ buffer.add(jsAst.prettyPrint(
+ js[isolateProperties][key].assign(js.fun(['receiver'], block)),
+ compiler));
buffer.add(N);
}
@@ -2269,7 +2329,6 @@
* Emit all versions of the [:getInterceptor:] method.
*/
void emitGetInterceptorMethods(CodeBuffer buffer) {
- JavaScriptBackend backend = compiler.backend;
// If no class needs to be intercepted, just return.
if (backend.objectInterceptorClass == null) return;
String objectName = namer.isolateAccess(backend.objectInterceptorClass);
@@ -2299,7 +2358,6 @@
int comparison = _compareSelectorNames(selector1, selector2);
if (comparison != 0) return comparison;
- JavaScriptBackend backend = compiler.backend;
Set<ClassElement> classes1 = backend.getInterceptedClassesOn(selector1);
Set<ClassElement> classes2 = backend.getInterceptedClassesOn(selector2);
if (classes1.length != classes2.length) {
@@ -2312,55 +2370,213 @@
return Comparable.compare(getInterceptor1, getInterceptor2);
}
+ // Optimize performance critical one shot interceptors.
+ jsAst.Statement tryOptimizeOneShotInterceptor(Selector selector,
+ Set<ClassElement> classes) {
+ jsAst.Expression isNumber(String variable) {
+ return js[variable].typeof.equals(js.string('number'));
+ }
+
+ jsAst.Expression isNotObject(String variable) {
+ return js[variable].typeof.equals(js.string('object')).not;
+ }
+
+ jsAst.Expression isInt(String variable) {
+ jsAst.Expression receiver = js[variable];
+ return isNumber(variable).binary('&&',
+ js['Math']['floor'](receiver).equals(receiver));
+ }
+
+ jsAst.Expression tripleShiftZero(jsAst.Expression receiver) {
+ return receiver.binary('>>>', js.toExpression(0));
+ }
+
+ if (selector.isOperator()) {
+ String name = selector.name.stringValue;
+ if (name == '==') {
+ // Unfolds to:
+ // [: if (receiver == null) return a0 == null;
+ // if (typeof receiver != 'object') {
+ // return a0 != null && receiver === a0;
+ // }
+ // :].
+ List<jsAst.Statement> body = <jsAst.Statement>[];
+ body.add(js.if_(js['receiver'].equals(new jsAst.LiteralNull()),
+ js.return_(js['a0'].equals(new jsAst.LiteralNull()))));
+ body.add(js.if_(
+ isNotObject('receiver'),
+ js.return_(js['a0'].equals(new jsAst.LiteralNull()).not.binary(
+ '&&', js['receiver'].strictEquals(js['a0'])))));
+ return new jsAst.Block(body);
+ }
+ if (!classes.contains(backend.jsIntClass)
+ && !classes.contains(backend.jsNumberClass)
+ && !classes.contains(backend.jsDoubleClass)) {
+ return null;
+ }
+ if (selector.argumentCount == 1) {
+ // The following operators do not map to a JavaScript
+ // operator.
+ if (name != '~/' && name != '<<' && name != '%' && name != '>>') {
+ jsAst.Expression result = js['receiver'].binary(name, js['a0']);
+ if (name == '&' || name == '|' || name == '^') {
+ result = tripleShiftZero(result);
+ }
+ // Unfolds to:
+ // [: if (typeof receiver == "number" && typeof a0 == "number")
+ // return receiver op a0;
+ // :].
+ return js.if_(
+ isNumber('receiver').binary('&&', isNumber('a0')),
+ js.return_(result));
+ }
+ } else if (name == 'unary-') {
+ // operator~ does not map to a JavaScript operator.
+ // Unfolds to:
+ // [: if (typeof receiver == "number") return -receiver:].
+ return js.if_(
+ isNumber('receiver'),
+ js.return_(new jsAst.Prefix('-', js['receiver'])));
+ } else {
+ assert(name == '~');
+ return js.if_(
+ isInt('receiver'),
+ js.return_(
+ tripleShiftZero(new jsAst.Prefix(name, js['receiver']))));
+ }
+ } else if (selector.isIndex() || selector.isIndexSet()) {
+ // For an index operation, this code generates:
+ //
+ // [: if (receiver.constructor == Array || typeof receiver == "string") {
+ // if (a0 >>> 0 === a0 && a0 < receiver.length) {
+ // return receiver[a0];
+ // }
+ // }
+ // :]
+ //
+ // For an index set operation, this code generates:
+ //
+ // [: if (receiver.constructor == Array && !receiver.immutable$list) {
+ // if (a0 >>> 0 === a0 && a0 < receiver.length) {
+ // return receiver[a0] = a1;
+ // }
+ // }
+ // :]
+ bool containsArray = classes.contains(backend.jsArrayClass);
+ bool containsString = classes.contains(backend.jsStringClass);
+ // The index set operator requires a check on its set value in
+ // checked mode, so we don't optimize the interceptor if the
+ // compiler has type assertions enabled.
+ if (selector.isIndexSet()
+ && (compiler.enableTypeAssertions || !containsArray)) {
+ return null;
+ }
+ if (!containsArray && !containsString) {
+ return null;
+ }
+ jsAst.Expression receiver = js['receiver'];
+ jsAst.Expression arg0 = js['a0'];
+ jsAst.Expression isIntAndAboveZero =
+ arg0.binary('>>>', js.toExpression(0)).strictEquals(arg0);
+ jsAst.Expression belowLength = arg0.binary('<', receiver['length']);
+ jsAst.Expression arrayCheck = receiver['constructor'].equals('Array');
+
+ if (selector.isIndex()) {
+ jsAst.Expression stringCheck =
+ receiver.typeof.equals(js.string('string'));
+ jsAst.Expression typeCheck;
+ if (containsArray) {
+ if (containsString) {
+ typeCheck = arrayCheck.binary('||', stringCheck);
+ } else {
+ typeCheck = arrayCheck;
+ }
+ } else {
+ assert(containsString);
+ typeCheck = stringCheck;
+ }
+
+ return js.if_(typeCheck,
+ js.if_(isIntAndAboveZero.binary('&&', belowLength),
+ js.return_(receiver[arg0])));
+ } else {
+ jsAst.Expression isImmutableArray = arrayCheck.binary(
+ '&&', receiver[r'immutable$list'].not);
+ return js.if_(isImmutableArray.binary(
+ '&&', isIntAndAboveZero.binary('&&', belowLength)),
+ js.return_(receiver[arg0].assign(js['a1'])));
+ }
+ }
+ return null;
+ }
+
void emitOneShotInterceptors(CodeBuffer buffer) {
- JavaScriptBackend backend = compiler.backend;
- for (Selector selector in
- backend.oneShotInterceptors.toList()..sort(_compareSelectors)) {
- Set<ClassElement> classes = backend.getInterceptedClassesOn(selector);
- String oneShotInterceptorName = namer.oneShotInterceptorName(selector);
+ List<String> names = backend.oneShotInterceptors.keys.toList();
+ names.sort();
+ for (String name in names) {
+ Selector selector = backend.oneShotInterceptors[name];
+ Set<ClassElement> classes =
+ backend.getInterceptedClassesOn(selector);
String getInterceptorName =
namer.getInterceptorName(backend.getInterceptorMethod, classes);
- List<js.Parameter> parameters = <js.Parameter>[];
- List<js.Expression> arguments = <js.Expression>[];
- parameters.add(new js.Parameter('receiver'));
- arguments.add(js.use('receiver'));
+ List<jsAst.Parameter> parameters = <jsAst.Parameter>[];
+ List<jsAst.Expression> arguments = <jsAst.Expression>[];
+ parameters.add(new jsAst.Parameter('receiver'));
+ arguments.add(js['receiver']);
if (selector.isSetter()) {
- parameters.add(new js.Parameter('value'));
- arguments.add(js.use('value'));
+ parameters.add(new jsAst.Parameter('value'));
+ arguments.add(js['value']);
} else {
for (int i = 0; i < selector.argumentCount; i++) {
String argName = 'a$i';
- parameters.add(new js.Parameter(argName));
- arguments.add(js.use(argName));
+ parameters.add(new jsAst.Parameter(argName));
+ arguments.add(js[argName]);
}
}
+ List<jsAst.Statement> body = <jsAst.Statement>[];
+ jsAst.Statement optimizedPath =
+ tryOptimizeOneShotInterceptor(selector, classes);
+ if (optimizedPath != null) {
+ body.add(optimizedPath);
+ }
+
String invocationName = backend.namer.invocationName(selector);
- js.Fun function =
- new js.Fun(parameters,
- js.block1(js.return_(
- js.use(isolateProperties)
- .dot(getInterceptorName)
- .callWith([js.use('receiver')])
- .dot(invocationName)
- .callWith(arguments))));
+ body.add(js.return_(
+ js[isolateProperties][getInterceptorName]('receiver')[invocationName](
+ arguments)));
- js.PropertyAccess property =
- js.fieldAccess(js.use(isolateProperties), oneShotInterceptorName);
+ jsAst.Fun function = js.fun(parameters, body);
- buffer.add(js.prettyPrint(js.assign(property, function), compiler));
+ jsAst.PropertyAccess property =
+ js[isolateProperties][name];
+
+ buffer.add(jsAst.prettyPrint(property.assign(function), compiler));
buffer.add(N);
}
}
+ void emitInitFunction(CodeBuffer buffer) {
+ jsAst.Fun fun = js.fun([], [
+ js['$isolateProperties = {}'],
+ ]
+ ..addAll(buildDefineClassAndFinishClassFunctionsIfNecessary())
+ ..addAll(buildLazyInitializerFunctionIfNecessary())
+ ..addAll(buildFinishIsolateConstructor())
+ );
+ jsAst.FunctionDeclaration decl = new jsAst.FunctionDeclaration(
+ new jsAst.VariableDeclaration('init'), fun);
+ buffer.add(jsAst.prettyPrint(decl, compiler).getText());
+ }
+
String assembleProgram() {
measure(() {
computeNeededClasses();
mainBuffer.add(GENERATED_BY);
- if (!compiler.enableMinification) mainBuffer.add(HOOKS_API_USAGE);
+ addComment(HOOKS_API_USAGE, mainBuffer);
mainBuffer.add('function ${namer.isolateName}()$_{}\n');
mainBuffer.add('init()$N$n');
// Shorten the code by using "$$" as temporary.
@@ -2392,8 +2608,10 @@
// The following code should not use the short-hand for the
// initialStatics.
mainBuffer.add('var ${namer.CURRENT_ISOLATE}$_=${_}null$N');
- mainBuffer.add(boundClosureBuffer);
- emitFinishClassesInvocationIfNecessary(mainBuffer);
+ if (!boundClosureBuffer.isEmpty) {
+ mainBuffer.add(boundClosureBuffer);
+ emitFinishClassesInvocationIfNecessary(mainBuffer);
+ }
// After this assignment we will produce invalid JavaScript code if we use
// the classesCollector variable.
classesCollector = 'classesCollector should not be used from now on';
@@ -2404,30 +2622,83 @@
nativeEmitter.assembleCode(mainBuffer);
emitMain(mainBuffer);
- mainBuffer.add('function init()$_{\n');
- mainBuffer.add('$isolateProperties$_=$_{}$N');
- addDefineClassAndFinishClassFunctionsIfNecessary(mainBuffer);
- addLazyInitializerFunctionIfNecessary(mainBuffer);
- emitFinishIsolateConstructor(mainBuffer);
- mainBuffer.add('}\n');
+ emitInitFunction(mainBuffer);
compiler.assembledCode = mainBuffer.getText();
+ outputSourceMap(mainBuffer, compiler.assembledCode, '');
- if (generateSourceMap) {
- SourceFile compiledFile = new SourceFile(null, compiler.assembledCode);
- String sourceMap = buildSourceMap(mainBuffer, compiledFile);
- compiler.outputProvider('', 'js.map')
- ..add(sourceMap)
- ..close();
- }
+ emitDeferredCode(deferredBuffer);
+
});
return compiler.assembledCode;
}
+ CodeBuffer bufferForElement(Element element, CodeBuffer eagerBuffer) {
+ if (!isDeferred(element)) return eagerBuffer;
+ emitDeferredPreambleWhenEmpty(deferredBuffer);
+ return deferredBuffer;
+ }
+
+ void emitDeferredCode(CodeBuffer buffer) {
+ if (buffer.isEmpty) return;
+
+ if (needsDefineClass) {
+ buffer.add('$finishClassesName(\$\$,'
+ '$_${namer.CURRENT_ISOLATE},'
+ '$_$isolatePropertiesName)$N');
+ // Reset the map.
+ buffer.add("\$\$$_=$_{}$N");
+ }
+
+ buffer.add('${namer.CURRENT_ISOLATE}$_=${_}old${namer.CURRENT_ISOLATE}$N');
+
+ String code = buffer.getText();
+ compiler.outputProvider('part', 'js')
+ ..add(code)
+ ..close();
+ outputSourceMap(buffer, compiler.assembledCode, 'part');
+ }
+
+ void emitDeferredPreambleWhenEmpty(CodeBuffer buffer) {
+ if (!buffer.isEmpty) return;
+ final classesCollector = r"$$";
+
+ buffer.add('$classesCollector$_=$_{}$N');
+ buffer.add('var old${namer.CURRENT_ISOLATE}$_='
+ '$_${namer.CURRENT_ISOLATE}$N');
+
+ // TODO(ahe): This defines a lot of properties on the
+ // Isolate.prototype object. We know this will turn it into a
+ // slow object in V8, so instead we should do something similar to
+ // Isolate.$finishIsolateConstructor.
+ buffer.add('${namer.CURRENT_ISOLATE}$_='
+ '$_${namer.isolateName}.prototype$N$n');
+ }
+
String buildSourceMap(CodeBuffer buffer, SourceFile compiledFile) {
SourceMapBuilder sourceMapBuilder = new SourceMapBuilder();
buffer.forEachSourceLocation(sourceMapBuilder.addMapping);
return sourceMapBuilder.build(compiledFile);
}
+
+ void outputSourceMap(CodeBuffer buffer, String code, String name) {
+ if (!generateSourceMap) return;
+ SourceFile compiledFile = new SourceFile(null, compiler.assembledCode);
+ String sourceMap = buildSourceMap(mainBuffer, compiledFile);
+ compiler.outputProvider(name, 'js.map')
+ ..add(sourceMap)
+ ..close();
+ }
+
+ bool isDeferred(Element element) {
+ return compiler.deferredLoadTask.isDeferred(element);
+ }
+
+ // TODO(ahe): Remove this when deferred loading is fully implemented.
+ void warnNotImplemented(Element element, String message) {
+ compiler.reportMessage(compiler.spanFromSpannable(element),
+ MessageKind.GENERIC.error({'text': message}),
+ api.Diagnostic.WARNING);
+ }
}
const String GENERATED_BY = """
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/js_backend.dart b/sdk/lib/_internal/compiler/implementation/js_backend/js_backend.dart
index 52e6ee0..8a2f2b8 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/js_backend.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/js_backend.dart
@@ -10,15 +10,18 @@
import '../../compiler.dart' as api;
import '../elements/elements.dart';
import '../elements/modelx.dart' show FunctionElementX;
-import '../dart2jslib.dart' hide Selector;
+
+// TODO(ahe): There seems to be a bug in the VM, so we have to hide "js".
+import '../dart2jslib.dart' hide Selector, js;
import '../dart_types.dart';
-import '../js/js.dart' as js;
+import '../js/js.dart' as jsAst;
+import '../js/js.dart' show js; // TODO(ahe): VM bug, see above.
import '../native_handler.dart' as native;
import '../source_file.dart';
import '../source_map_builder.dart';
-import '../ssa/ssa.dart';
+import '../ssa/ssa.dart' hide js; // TODO(ahe): VM bug, see above.
import '../tree/tree.dart';
-import '../universe/universe.dart';
+import '../universe/universe.dart' hide js; // TODO(ahe): VM bug, see above.
import '../util/characters.dart';
import '../util/util.dart';
@@ -26,7 +29,8 @@
part 'constant_emitter.dart';
part 'constant_system_javascript.dart';
part 'emitter.dart';
-part 'emitter_no_eval.dart';
+// TODO(8522): Restore --disallow-unsafe-eval.
+// part 'emitter_no_eval.dart';
part 'minify_namer.dart';
part 'namer.dart';
part 'native_emitter.dart';
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/namer.dart b/sdk/lib/_internal/compiler/implementation/js_backend/namer.dart
index aca3e61..4ac6919 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/namer.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/namer.dart
@@ -185,7 +185,6 @@
*/
final Compiler compiler;
final Map<Element, String> globals;
- final Map<Selector, String> oneShotInterceptorNames;
final Map<String, LibraryElement> shortPrivateNameOwners;
final Set<String> usedGlobalNames;
@@ -194,7 +193,7 @@
final Map<String, String> suggestedGlobalNames;
final Map<String, String> instanceNameMap;
final Map<String, String> suggestedInstanceNames;
-
+
final Map<String, String> operatorNameMap;
final Map<String, int> popularNameCounters;
@@ -204,7 +203,6 @@
Namer(this.compiler)
: globals = new Map<Element, String>(),
- oneShotInterceptorNames = new Map<Selector, String>(),
shortPrivateNameOwners = new Map<String, LibraryElement>(),
bailoutNames = new Map<Element, String>(),
usedGlobalNames = new Set<String>(),
@@ -323,7 +321,7 @@
!signature.optionalParameters.isEmpty) {
StringBuffer buffer = new StringBuffer();
signature.orderedOptionalParameters.forEach((Element element) {
- buffer.add('\$${safeName(element.name.slowToString())}');
+ buffer.write('\$${safeName(element.name.slowToString())}');
});
methodName = '$methodName$buffer';
}
@@ -362,7 +360,7 @@
assert(name == operatorNameToIdentifier(name));
StringBuffer buffer = new StringBuffer();
for (SourceString argumentName in selector.getOrderedNamedArguments()) {
- buffer.add(r'$');
+ buffer.write(r'$');
argumentName.printOn(buffer);
}
String suffix = '\$${selector.argumentCount}$buffer';
@@ -534,22 +532,40 @@
return name;
}
+ String getInterceptorSuffix(Collection<ClassElement> classes) {
+ String abbreviate(ClassElement cls) {
+ if (cls == compiler.objectClass) return "o";
+ JavaScriptBackend backend = compiler.backend;
+ if (cls == backend.jsStringClass) return "s";
+ if (cls == backend.jsArrayClass) return "a";
+ if (cls == backend.jsDoubleClass) return "d";
+ if (cls == backend.jsNumberClass) return "n";
+ if (cls == backend.jsNullClass) return "u";
+ if (cls == backend.jsFunctionClass) return "f";
+ if (cls == backend.jsBoolClass) return "b";
+ return cls.name.slowToString();
+ }
+ // Sort the names of the classes after abbreviating them to ensure
+ // the suffix is stable and predictable for the suggested names.
+ List<String> names = classes.map(abbreviate).toList();
+ names.sort();
+ return names.join();
+ }
+
String getInterceptorName(Element element, Collection<ClassElement> classes) {
if (classes.contains(compiler.objectClass)) {
// If the object class is in the set of intercepted classes, we
// need to go through the generic getInterceptorMethod.
return getName(element);
}
- // Use the unminified names here to construct the interceptor names. This
- // helps ensure that they don't all suddenly change names due to a name
- // clash in the minifier, which would affect the diff size. Sort the names
- // of the classes to ensure name is stable and predicatble for the suggested
- // names.
- StringBuffer buffer = new StringBuffer('${element.name.slowToString()}\$');
- List<String> names = classes.map((cls) => cls.name.slowToString()).toList();
- names.sort();
- names.forEach(buffer.add);
- return getMappedGlobalName(buffer.toString());
+ String suffix = getInterceptorSuffix(classes);
+ return getMappedGlobalName("${element.name.slowToString()}\$$suffix");
+ }
+
+ String getOneShotInterceptorName(Selector selector,
+ Collection<ClassElement> classes) {
+ String suffix = getInterceptorSuffix(classes);
+ return getMappedGlobalName("${invocationName(selector)}\$$suffix");
}
String getBailoutName(Element element) {
@@ -700,19 +716,6 @@
String safeName(String name) => _safeName(name, jsReserved);
String safeVariableName(String name) => _safeName(name, jsVariableReserved);
- String oneShotInterceptorName(Selector selector) {
- // TODO(ngeoffray): What to do about typed selectors? We could
- // filter them out, or keep them and hope the generated one shot
- // interceptor takes advantage of the type.
- String cached = oneShotInterceptorNames[selector];
- if (cached != null) return cached;
- SourceString name = operatorNameToIdentifier(selector.name);
- String result = getFreshName(name.slowToString(), usedGlobalNames,
- suggestedGlobalNames);
- oneShotInterceptorNames[selector] = result;
- return result;
- }
-
SourceString operatorNameToIdentifier(SourceString name) {
if (name == null) return null;
String value = name.stringValue;
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 0343457..6500fc3 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart
@@ -138,11 +138,11 @@
if (builder.properties.isEmpty) return;
String nativeTag = toNativeTag(classElement);
- js.Expression definition =
- js.call(js.use(defineNativeClassName),
- [js.string(nativeTag), builder.toObjectInitializer()]);
+ jsAst.Expression definition =
+ js[defineNativeClassName](
+ [js.string(nativeTag), builder.toObjectInitializer()]);
- nativeBuffer.add(js.prettyPrint(definition, compiler));
+ nativeBuffer.add(jsAst.prettyPrint(definition, compiler));
nativeBuffer.add('$N$n');
classesWithDynamicDispatch.add(classElement);
@@ -153,9 +153,10 @@
return result == null ? const<ClassElement>[] : result;
}
- void potentiallyConvertDartClosuresToJs(List<js.Statement> statements,
- FunctionElement member,
- List<js.Parameter> stubParameters) {
+ void potentiallyConvertDartClosuresToJs(
+ List<jsAst.Statement> statements,
+ FunctionElement member,
+ List<jsAst.Parameter> stubParameters) {
FunctionSignature parameters = member.computeSignature(compiler);
Element converter =
compiler.findHelper(const SourceString('convertDartClosureToJS'));
@@ -166,7 +167,7 @@
String name = parameter.name.slowToString();
// If [name] is not in [stubParameters], then the parameter is an optional
// parameter that was not provided for this stub.
- for (js.Parameter stubParameter in stubParameters) {
+ for (jsAst.Parameter stubParameter in stubParameters) {
if (stubParameter.name == name) {
DartType type = parameter.computeType(compiler).unalias(compiler);
if (type is FunctionType) {
@@ -175,11 +176,12 @@
int arity = type.computeArity();
statements.add(
- new js.ExpressionStatement(
+ new jsAst.ExpressionStatement(
js.assign(
- js.use(name),
- js.use(closureConverter).callWith(
- [js.use(name), new js.LiteralNumber('$arity')]))));
+ js[name],
+ js[closureConverter](
+ [js[name],
+ new jsAst.LiteralNumber('$arity')]))));
break;
}
}
@@ -187,11 +189,11 @@
});
}
- List<js.Statement> generateParameterStubStatements(
+ List<jsAst.Statement> generateParameterStubStatements(
Element member,
String invocationName,
- List<js.Parameter> stubParameters,
- List<js.Expression> argumentsBuffer,
+ List<jsAst.Parameter> stubParameters,
+ List<jsAst.Expression> argumentsBuffer,
int indexOfLastOptionalArgumentInParameters) {
// The target JS function may check arguments.length so we need to
// make sure not to pass any unspecified optional arguments to it.
@@ -205,11 +207,11 @@
ClassElement classElement = member.enclosingElement;
String nativeTagInfo = classElement.nativeTagInfo.slowToString();
- List<js.Statement> statements = <js.Statement>[];
+ List<jsAst.Statement> statements = <jsAst.Statement>[];
potentiallyConvertDartClosuresToJs(statements, member, stubParameters);
String target;
- List<js.Expression> arguments;
+ List<jsAst.Expression> arguments;
if (!nativeMethods.contains(member)) {
// When calling a method that has a native body, we call it with our
@@ -224,16 +226,16 @@
0, indexOfLastOptionalArgumentInParameters + 1);
}
statements.add(
- new js.Return(
- new js.VariableUse('this').dot(target).callWith(arguments)));
+ new jsAst.Return(
+ new jsAst.VariableUse('this')[target](arguments)));
if (!overriddenMethods.contains(member)) {
// Call the method directly.
return statements;
} else {
- return <js.Statement>[
+ return <jsAst.Statement>[
generateMethodBodyWithPrototypeCheck(
- invocationName, new js.Block(statements), stubParameters)];
+ invocationName, new jsAst.Block(statements), stubParameters)];
}
}
@@ -242,26 +244,23 @@
// super class. If the method is not available, we make a direct call to
// Object.prototype.$methodName. This method will patch the prototype of
// 'this' to the real method.
- js.Statement generateMethodBodyWithPrototypeCheck(
+ jsAst.Statement generateMethodBodyWithPrototypeCheck(
String methodName,
- js.Statement body,
- List<js.Parameter> parameters) {
+ jsAst.Statement body,
+ List<jsAst.Parameter> parameters) {
return js.if_(
- js.use('Object').dot('getPrototypeOf')
- .callWith([js.use('this')])
- .dot('hasOwnProperty').callWith([js.string(methodName)]),
+ js['(Object.getPrototypeOf(this)).hasOwnProperty("$methodName")'],
body,
js.return_(
- js.use('Object').dot('prototype').dot(methodName).dot('call')
- .callWith(
- <js.Expression>[js.use('this')]..addAll(
- parameters.map((param) => js.use(param.name))))));
+ js['Object.prototype.$methodName.call'](
+ <jsAst.Expression>[js['this']]..addAll(
+ parameters.map((param) => js[param.name])))));
}
- js.Block generateMethodBodyWithPrototypeCheckForElement(
+ jsAst.Block generateMethodBodyWithPrototypeCheckForElement(
FunctionElement element,
- js.Block body,
- List<js.Parameter> parameters) {
+ jsAst.Block body,
+ List<jsAst.Parameter> parameters) {
ElementKind kind = element.kind;
if (kind != ElementKind.FUNCTION &&
kind != ElementKind.GETTER &&
@@ -270,7 +269,7 @@
}
String methodName = backend.namer.getName(element);
- return new js.Block(
+ return new jsAst.Block(
[generateMethodBodyWithPrototypeCheck(methodName, body, parameters)]);
}
@@ -322,23 +321,23 @@
// Temporary variables for common substrings.
List<String> varNames = <String>[];
// Values of temporary variables.
- Map<String, js.Expression> varDefns = new Map<String, js.Expression>();
+ Map<String, jsAst.Expression> varDefns = new Map<String, jsAst.Expression>();
// Expression to compute tags string for a class. The expression will
// initially be a string or expression building a string, but may be
// replaced with a variable reference to the common substring.
- Map<ClassElement, js.Expression> tagDefns =
- new Map<ClassElement, js.Expression>();
+ Map<ClassElement, jsAst.Expression> tagDefns =
+ new Map<ClassElement, jsAst.Expression>();
- js.Expression makeExpression(ClassElement classElement) {
+ jsAst.Expression makeExpression(ClassElement classElement) {
// Expression fragments for this set of cls keys.
- List<js.Expression> expressions = <js.Expression>[];
+ List<jsAst.Expression> expressions = <jsAst.Expression>[];
// TODO: Remove if cls is abstract.
List<String> subtags = [toNativeTag(classElement)];
void walk(ClassElement cls) {
for (final ClassElement subclass in getDirectSubclasses(cls)) {
ClassElement tag = subclass;
- js.Expression existing = tagDefns[tag];
+ jsAst.Expression existing = tagDefns[tag];
if (existing == null) {
// [subclass] is still within the subtree between dispatch classes.
subtags.add(toNativeTag(tag));
@@ -346,19 +345,19 @@
} else {
// [subclass] is one of the preorderDispatchClasses, so CSE this
// reference with the previous reference.
- js.VariableUse use = existing.asVariableUse();
+ jsAst.VariableUse use = existing.asVariableUse();
if (use != null && varDefns.containsKey(use.name)) {
// We end up here if the subclasses have a DAG structure. We
// don't have DAGs yet, but if the dispatch is used for mixins
// that will be a possibility.
// Re-use the previously created temporary variable.
- expressions.add(new js.VariableUse(use.name));
+ expressions.add(new jsAst.VariableUse(use.name));
} else {
String varName = 'v${varNames.length}_${tag.name.slowToString()}';
varNames.add(varName);
varDefns[varName] = existing;
- tagDefns[tag] = new js.VariableUse(varName);
- expressions.add(new js.VariableUse(varName));
+ tagDefns[tag] = new jsAst.VariableUse(varName);
+ expressions.add(new jsAst.VariableUse(varName));
}
}
}
@@ -368,12 +367,12 @@
if (!subtags.isEmpty) {
expressions.add(js.string(subtags.join('|')));
}
- js.Expression expression;
+ jsAst.Expression expression;
if (expressions.length == 1) {
expression = expressions[0];
} else {
- js.Expression array = new js.ArrayInitializer.from(expressions);
- expression = js.call(array.dot('join'), [js.string('|')]);
+ jsAst.Expression array = new jsAst.ArrayInitializer.from(expressions);
+ expression = array['join']([js.string('|')]);
}
return expression;
}
@@ -384,46 +383,47 @@
// Write out a thunk that builds the metadata.
if (!tagDefns.isEmpty) {
- List<js.Statement> statements = <js.Statement>[];
+ List<jsAst.Statement> statements = <jsAst.Statement>[];
- List<js.VariableInitialization> initializations =
- <js.VariableInitialization>[];
+ List<jsAst.VariableInitialization> initializations =
+ <jsAst.VariableInitialization>[];
for (final String varName in varNames) {
initializations.add(
- new js.VariableInitialization(
- new js.VariableDeclaration(varName),
+ new jsAst.VariableInitialization(
+ new jsAst.VariableDeclaration(varName),
varDefns[varName]));
}
if (!initializations.isEmpty) {
statements.add(
- new js.ExpressionStatement(
- new js.VariableDeclarationList(initializations)));
+ new jsAst.ExpressionStatement(
+ new jsAst.VariableDeclarationList(initializations)));
}
// [table] is a list of lists, each inner list of the form:
// [dynamic-dispatch-tag, tags-of-classes-implementing-dispatch-tag]
// E.g.
// [['Node', 'Text|HTMLElement|HTMLDivElement|...'], ...]
- js.Expression table =
- new js.ArrayInitializer.from(
+ jsAst.Expression table =
+ new jsAst.ArrayInitializer.from(
preorderDispatchClasses.map((cls) =>
- new js.ArrayInitializer.from([
+ new jsAst.ArrayInitializer.from([
js.string(toNativeTag(cls)),
tagDefns[cls]])));
// $.dynamicSetMetadata(table);
statements.add(
- new js.ExpressionStatement(
- new js.Call(
- new js.VariableUse(dynamicSetMetadataName),
+ new jsAst.ExpressionStatement(
+ new jsAst.Call(
+ new jsAst.VariableUse(dynamicSetMetadataName),
[table])));
// (function(){statements})();
if (emitter.compiler.enableMinification) nativeBuffer.add(';');
nativeBuffer.add(
- js.prettyPrint(
- new js.ExpressionStatement(
- new js.Call(new js.Fun([], new js.Block(statements)), [])),
+ jsAst.prettyPrint(
+ new jsAst.ExpressionStatement(
+ new jsAst.Call(new jsAst.Fun([], new jsAst.Block(statements)),
+ [])),
compiler));
}
}
@@ -460,10 +460,10 @@
targetBuffer.add('$defineNativeClassName = '
'$defineNativeClassFunction$N$n');
- List<js.Property> objectProperties = <js.Property>[];
+ List<jsAst.Property> objectProperties = <jsAst.Property>[];
- void addProperty(String name, js.Expression value) {
- objectProperties.add(new js.Property(js.string(name), value));
+ void addProperty(String name, jsAst.Expression value) {
+ objectProperties.add(new jsAst.Property(js.string(name), value));
}
// Because of native classes, we have to generate some is checks
@@ -478,16 +478,14 @@
if (element.isObject(compiler)) continue;
String name = backend.namer.operatorIs(element);
addProperty(name,
- js.fun([], js.block1(js.return_(new js.LiteralBool(false)))));
+ js.fun([], js.return_(js['false'])));
}
}
emitIsChecks();
- js.Expression makeCallOnThis(String functionName) =>
- js.fun([],
- js.block1(
- js.return_(
- js.call(js.use(functionName), [js.use('this')]))));
+ jsAst.Expression makeCallOnThis(String functionName) {
+ return js.fun([], js.return_(js['$functionName(this)']));
+ }
// In order to have the toString method on every native class,
// we must patch the JS Object prototype with a helper method.
@@ -503,8 +501,8 @@
// Same as above, but for operator==.
String equalsName = backend.namer.publicInstanceMethodNameByArity(
const SourceString('=='), 1);
- addProperty(equalsName, js.fun(['a'], js.block1(
- js.return_(js.strictEquals(new js.This(), js.use('a'))))));
+ addProperty(equalsName, js.fun(['a'],
+ js.return_(js['this === a'])));
// If the native emitter has been asked to take care of the
// noSuchMethod handlers, we do that now.
@@ -515,28 +513,21 @@
// If we have any properties to add to Object.prototype, we run
// through them and add them using defineProperty.
if (!objectProperties.isEmpty) {
- js.Expression init =
- js.call(
- js.fun(['table'],
- js.block1(
- new js.ForIn(
- new js.VariableDeclarationList(
- [new js.VariableInitialization(
- new js.VariableDeclaration('key'),
- null)]),
- js.use('table'),
- new js.ExpressionStatement(
- js.call(
- js.use(defPropName),
- [js.use('Object').dot('prototype'),
- js.use('key'),
- new js.PropertyAccess(js.use('table'),
- js.use('key'))]))))),
- [new js.ObjectInitializer(objectProperties)]);
+ jsAst.Expression init =
+ js.fun(['table'],
+ new jsAst.ForIn(
+ new jsAst.VariableDeclarationList(
+ [new jsAst.VariableInitialization(
+ new jsAst.VariableDeclaration('key'),
+ null)]),
+ js['table'],
+ new jsAst.ExpressionStatement(
+ js['$defPropName(Object.prototype, key, table[key])'])))(
+ new jsAst.ObjectInitializer(objectProperties));
if (emitter.compiler.enableMinification) targetBuffer.add(';');
- targetBuffer.add(js.prettyPrint(
- new js.ExpressionStatement(init), compiler));
+ targetBuffer.add(jsAst.prettyPrint(
+ new jsAst.ExpressionStatement(init), compiler));
targetBuffer.add('\n');
}
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/runtime_types.dart b/sdk/lib/_internal/compiler/implementation/js_backend/runtime_types.dart
index b1cb9eb..bfad207 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/runtime_types.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/runtime_types.dart
@@ -81,7 +81,7 @@
addAllInterfaceTypeArguments(type, instantiatedArguments);
}
}
- for (ClassElement cls in instantiatedArguments) {
+ for (ClassElement cls in instantiatedArguments.toList()) {
for (DartType type in cls.allSupertypes) {
addAllInterfaceTypeArguments(type, instantiatedArguments);
}
@@ -234,25 +234,25 @@
StringBuffer builder = new StringBuffer();
void build(DartType part) {
if (part is TypeVariableType) {
- builder.add(onVariable(part));
+ builder.write(onVariable(part));
} else {
bool hasArguments = part is InterfaceType && !part.isRaw;
Element element = part.element;
if (element == compiler.dynamicClass) {
- builder.add('null');
+ builder.write('null');
} else {
String name = getJsName(element);
if (!hasArguments) {
- builder.add(name);
+ builder.write(name);
} else {
- builder.add('[');
- builder.add(name);
+ builder.write('[');
+ builder.write(name);
InterfaceType interface = part;
for (DartType argument in interface.typeArguments) {
- builder.add(', ');
+ builder.write(', ');
build(argument);
}
- builder.add(']');
+ builder.write(']');
}
}
}
diff --git a/sdk/lib/_internal/compiler/implementation/lib/async_patch.dart b/sdk/lib/_internal/compiler/implementation/lib/async_patch.dart
index 0e0a70d..cd63a24 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/async_patch.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/async_patch.dart
@@ -4,7 +4,8 @@
// Patch file for the dart:async library.
-import 'dart:_isolate_helper' show TimerImpl;
+import 'dart:_isolate_helper' show IsolateNatives, TimerImpl;
+import 'dart:_foreign_helper' show JS, DART_CLOSURE_TO_JS;
typedef void _TimerCallback0();
typedef void _TimerCallback1(Timer timer);
@@ -38,3 +39,59 @@
return new TimerImpl.repeating(milliseconds, callback);
}
}
+
+patch class DeferredLibrary {
+ patch Future<bool> load() {
+ return _load(libraryName, uri);
+ }
+}
+
+// TODO(ahe): This should not only apply to this isolate.
+final _loadedLibraries = <String, Completer<bool>>{};
+
+Future<bool> _load(String libraryName, String uri) {
+ // TODO(ahe): Validate libraryName. Kasper points out that you want
+ // to be able to experiment with the effect of toggling @DeferLoad,
+ // so perhaps we should silently ignore "bad" library names.
+ Completer completer = new Completer<bool>();
+ Future<bool> future = _loadedLibraries[libraryName];
+ if (future != null) {
+ future.then((_) { completer.complete(false); });
+ return completer.future;
+ }
+ _loadedLibraries[libraryName] = completer.future;
+
+ if (uri == null) {
+ uri = IsolateNatives.thisScript;
+ int index = uri.lastIndexOf('/');
+ uri = '${uri.substring(0, index + 1)}part.js';
+ }
+
+ if (_hasDocument) {
+ // Inject a script tag.
+ var script = JS('', 'document.createElement("script")');
+ JS('', '#.type = "text/javascript"', script);
+ JS('', '#.async = "async"', script);
+ JS('', '#.src = #', script, uri);
+ var onLoad = JS('', '#.bind(null, #)',
+ DART_CLOSURE_TO_JS(_onDeferredLibraryLoad), completer);
+ JS('', '#.addEventListener("load", #, false)', script, onLoad);
+ JS('', 'document.body.appendChild(#)', script);
+ } else if (JS('String', 'typeof load') == 'function') {
+ new Timer(0, (_) {
+ JS('void', 'load(#)', uri);
+ completer.complete(true);
+ });
+ } else {
+ throw new UnsupportedError('load not supported');
+ }
+ return completer.future;
+}
+
+/// Used to implement deferred loading. Used as callback on "load"
+/// event above in [load].
+_onDeferredLibraryLoad(Completer<bool> completer, event) {
+ completer.complete(true);
+}
+
+bool get _hasDocument => JS('String', 'typeof document') == 'object';
diff --git a/sdk/lib/_internal/compiler/implementation/lib/core_patch.dart b/sdk/lib/_internal/compiler/implementation/lib/core_patch.dart
index b0f09ccf..fad1d02 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/core_patch.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/core_patch.dart
@@ -240,3 +240,50 @@
patch String toString() => _contents;
}
+
+patch class NoSuchMethodError {
+ patch String toString() {
+ StringBuffer sb = new StringBuffer();
+ int i = 0;
+ if (_arguments != null) {
+ for (; i < _arguments.length; i++) {
+ if (i > 0) {
+ sb.add(", ");
+ }
+ sb.add(Error.safeToString(_arguments[i]));
+ }
+ }
+ if (_namedArguments != null) {
+ _namedArguments.forEach((String key, var value) {
+ if (i > 0) {
+ sb.add(", ");
+ }
+ sb.add(key);
+ sb.add(": ");
+ sb.add(Error.safeToString(value));
+ i++;
+ });
+ }
+ if (_existingArgumentNames == null) {
+ return "NoSuchMethodError : method not found: '$_memberName'\n"
+ "Receiver: ${Error.safeToString(_receiver)}\n"
+ "Arguments: [$sb]";
+ } else {
+ String actualParameters = sb.toString();
+ sb = new StringBuffer();
+ for (int i = 0; i < _existingArgumentNames.length; i++) {
+ if (i > 0) {
+ sb.add(", ");
+ }
+ sb.add(_existingArgumentNames[i]);
+ }
+ String formalParameters = sb.toString();
+ return "NoSuchMethodError: incorrect number of arguments passed to "
+ "method named '$_memberName'\n"
+ "Receiver: ${Error.safeToString(_receiver)}\n"
+ "Tried calling: $_memberName($actualParameters)\n"
+ "Found: $_memberName($formalParameters)";
+ }
+ }
+}
+
diff --git a/sdk/lib/_internal/compiler/implementation/lib/foreign_helper.dart b/sdk/lib/_internal/compiler/implementation/lib/foreign_helper.dart
index 97700a7..ac14c32 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/foreign_helper.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/foreign_helper.dart
@@ -96,7 +96,13 @@
/**
* Returns the isolate in which this code is running.
*/
-dynamic JS_CURRENT_ISOLATE() {}
+IsolateContext JS_CURRENT_ISOLATE() {}
+
+abstract class IsolateContext {
+ /// Holds a (native) JavaScript instance of Isolate, see
+ /// finishIsolateConstructorFunction in emitter.dart.
+ get isolateStatics;
+}
/**
* Invokes [function] in the context of [isolate].
diff --git a/sdk/lib/_internal/compiler/implementation/lib/io_patch.dart b/sdk/lib/_internal/compiler/implementation/lib/io_patch.dart
index 0374de5..00ed800 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/io_patch.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/io_patch.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
@@ -166,23 +166,43 @@
}
}
+patch class RawServerSocket {
+ patch static Future<RawServerSocket> bind([String address = "127.0.0.1",
+ int port = 0,
+ int backlog = 0]) {
+ throw new UnsupportedError("RawServerSocket.bind");
+ }
+}
+
patch class ServerSocket {
- patch factory ServerSocket(String bindAddress, int port, int backlog) {
- throw new UnsupportedError("ServerSocket constructor");
+ patch static Future<ServerSocket> bind([String address = "127.0.0.1",
+ int port = 0,
+ int backlog = 0]) {
+ throw new UnsupportedError("ServerSocket.bind");
+ }
+}
+
+patch class RawSocket {
+ patch static Future<RawSocket> connect(String host, int port) {
+ throw new UnsupportedError("RawSocket constructor");
}
}
patch class Socket {
- patch factory Socket(String host, int port) {
+ patch static Future<Socket> connect(String host, int port) {
throw new UnsupportedError("Socket constructor");
}
}
patch class SecureSocket {
+ patch factory SecureSocket._(RawSecureSocket rawSocket) {
+ throw new UnsupportedError("SecureSocket constructor");
+ }
+
patch static void initialize({String database,
String password,
bool useBuiltinRoots: true}) {
- throw new UnsupportedError("SecureSocket.setCertificateDatabase");
+ throw new UnsupportedError("SecureSocket.initialize");
}
}
@@ -193,13 +213,13 @@
}
patch class _StdIOUtils {
- patch static InputStream _getStdioInputStream() {
+ patch static Stream<List<int>> _getStdioInputStream() {
throw new UnsupportedError("StdIOUtils._getStdioInputStream");
}
- patch static OutputStream _getStdioOutputStream(int fd) {
+ patch static IOSink _getStdioOutputStream(int fd) {
throw new UnsupportedError("StdIOUtils._getStdioOutputStream");
}
- patch static int _socketType(Socket socket) {
+ patch static int _socketType(nativeSocket) {
throw new UnsupportedError("StdIOUtils._socketType");
}
}
diff --git a/sdk/lib/_internal/compiler/implementation/lib/isolate_helper.dart b/sdk/lib/_internal/compiler/implementation/lib/isolate_helper.dart
index 54bbdc1..539e7b4 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/isolate_helper.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/isolate_helper.dart
@@ -12,7 +12,9 @@
import 'dart:_foreign_helper' show DART_CLOSURE_TO_JS,
JS,
JS_CREATE_ISOLATE,
- JS_SET_CURRENT_ISOLATE;
+ JS_CURRENT_ISOLATE,
+ JS_SET_CURRENT_ISOLATE,
+ IsolateContext;
ReceivePort lazyPort;
@@ -202,7 +204,7 @@
}
/** Context information tracked for each isolate. */
-class _IsolateContext {
+class _IsolateContext implements IsolateContext {
/** Current isolate id. */
int id;
@@ -406,23 +408,46 @@
* JavaScript workers.
*/
static String computeThisScript() {
- // TODO(7369): Find a cross-platform non-brittle way of getting the
- // currently running script.
- var scripts = JS('', r"document.getElementsByTagName('script')");
- // The scripts variable only contains the scripts that have already been
- // executed. The last one is the currently running script.
- for (int i = 0, len = JS('int', '#.length', scripts); i < len; i++) {
- var script = JS('', '#[#]', scripts, i);
- var src = JS('String|Null', '# && #.src', script, script);
- // Filter out the test controller script, and the Dart
- // bootstrap script.
- if (src != null
- && !src.endsWith('test_controller.js')
- && !src.endsWith('dart.js')) {
- return src;
- }
+ var currentScript = JS('', r'$.$currentScript');
+ if (currentScript != null) {
+ return JS('String', 'String(#.src)', currentScript);
}
- return null;
+
+ // TODO(ahe): The following is for supporting command-line engines
+ // such as d8 and jsshell. We should move this code to a helper
+ // library that is only loaded when testing on those engines.
+
+ var stack = JS('String|Null', 'new Error().stack');
+ if (stack == null) {
+ // According to Internet Explorer documentation, the stack
+ // property is not set until the exception is thrown. The stack
+ // property was not provided until IE10.
+ stack = JS('String',
+ '(function() {'
+ 'try { throw new Error() } catch(e) { return e.stack }'
+ '})()');
+ }
+ var pattern, matches;
+
+ // This pattern matches V8, Chrome, and Internet Explorer stack
+ // traces that look like this:
+ // Error
+ // at methodName (URI:LINE:COLUMN)
+ pattern = JS('',
+ r'new RegExp("^ *at [^(]*\\((.*):[0-9]*:[0-9]*\\)$", "m")');
+
+
+ matches = JS('=List|Null', '#.match(#)', stack, pattern);
+ if (matches != null) return JS('String', '#[1]', matches);
+
+ // This pattern matches Firefox stack traces that look like this:
+ // methodName@URI:LINE
+ pattern = JS('', r'new RegExp("^[^@]*@(.*):[0-9]*$", "m")');
+
+ matches = JS('=List|Null', '#.match(#)', stack, pattern);
+ if (matches != null) return JS('String', '#[1]', matches);
+
+ throw new UnsupportedError('Cannot extract URI from "$stack"');
}
static computeGlobalThis() => JS('', 'function() { return this; }()');
diff --git a/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart b/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart
index 75724a7..19f6ce8 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart
@@ -805,7 +805,9 @@
// we're dealing with we fall back on looking at the exception
// message if it is available and a string.
if (message is String) {
- if (message.endsWith('is null') ||
+ if (message == 'null has no properties' ||
+ message == "'null' is not an object" ||
+ message.endsWith('is null') ||
message.endsWith('is undefined') ||
message.endsWith('is null or undefined') ||
message.endsWith('of undefined') ||
@@ -1336,6 +1338,11 @@
throwMalformedSubtypeError(value, type, reasons);
}
+malformedTypeCast(value, type, reasons) {
+ if (value == null) return value;
+ throw new CastErrorImplementation.malformedTypeCast(value, type, reasons);
+}
+
/**
* Special interface recognized by the compiler and implemented by DOM
* objects that support integer indexing. This interface is not
@@ -1372,17 +1379,27 @@
/** Thrown by the 'as' operator if the cast isn't valid. */
class CastErrorImplementation implements CastError {
// TODO(lrn): Rename to CastError (and move implementation into core).
- // TODO(lrn): Change actualType and expectedType to "Type" when reified
- // types are available.
- final Object actualType;
- final Object expectedType;
+ final String message;
- CastErrorImplementation(this.actualType, this.expectedType);
+ /**
+ * Normal cast error caused by a failed type cast.
+ */
+ CastErrorImplementation(Object actualType, Object expectedType)
+ : message = "CastError: Casting value of type $actualType to"
+ " incompatible type $expectedType";
- String toString() {
- return "CastError: Casting value of type $actualType to"
- " incompatible type $expectedType";
- }
+
+ /**
+ * Cast error caused by a type cast to a malformed type.
+ */
+ CastErrorImplementation.malformedTypeCast(Object value,
+ String type, String reasons)
+ : message = "CastError: Type '${Primitives.objectTypeName(value)}' "
+ "cannot be cast to type '$type' because '$type' is "
+ "malformed: $reasons.";
+
+
+ String toString() => message;
}
class FallThroughErrorImplementation implements FallThroughError {
diff --git a/sdk/lib/_internal/compiler/implementation/lib/js_number.dart b/sdk/lib/_internal/compiler/implementation/lib/js_number.dart
index c15fcae..a8bfc13 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/js_number.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/js_number.dart
@@ -220,17 +220,17 @@
num operator &(num other) {
if (other is !num) throw new ArgumentError(other);
- return JS('num', r'(# & #) >>> 0', this, other);
+ return JS('num', r'(# & #) >>> 0', this, other);
}
num operator |(num other) {
if (other is !num) throw new ArgumentError(other);
- return JS('num', r'(# | #) >>> 0', this, other);
+ return JS('num', r'(# | #) >>> 0', this, other);
}
num operator ^(num other) {
if (other is !num) throw new ArgumentError(other);
- return JS('num', r'(# ^ #) >>> 0', this, other);
+ return JS('num', r'(# ^ #) >>> 0', this, other);
}
bool operator <(num other) {
diff --git a/sdk/lib/_internal/compiler/implementation/lib/string_helper.dart b/sdk/lib/_internal/compiler/implementation/lib/string_helper.dart
index 6e4dccf..9749028 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/string_helper.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/string_helper.dart
@@ -77,7 +77,7 @@
return JS('String', r'#.replace(#, #)', receiver, replacer, to);
}
-final RegExp quoteRegExp = new JSSyntaxRegExp(r'[-[\]{}()*+?.,\\^$|#\s]');
+final RegExp quoteRegExp = const JSSyntaxRegExp(r'[-[\]{}()*+?.,\\^$|#\s]');
stringReplaceAllUnchecked(receiver, from, to) {
checkString(to);
diff --git a/sdk/lib/_internal/compiler/implementation/library_loader.dart b/sdk/lib/_internal/compiler/implementation/library_loader.dart
index e5854b5..216bba8 100644
--- a/sdk/lib/_internal/compiler/implementation/library_loader.dart
+++ b/sdk/lib/_internal/compiler/implementation/library_loader.dart
@@ -796,6 +796,9 @@
void registerDependency(LibraryElement library,
LibraryDependency tag,
LibraryElement loadedLibrary) {
+ if (tag != null) {
+ library.recordResolvedTag(tag, loadedLibrary);
+ }
if (tag is Export) {
// [loadedLibrary] is exported by [library].
LibraryDependencyNode exportingNode = nodeMap[library];
diff --git a/sdk/lib/_internal/compiler/implementation/native_handler.dart b/sdk/lib/_internal/compiler/implementation/native_handler.dart
index adce296..0d5b232 100644
--- a/sdk/lib/_internal/compiler/implementation/native_handler.dart
+++ b/sdk/lib/_internal/compiler/implementation/native_handler.dart
@@ -373,6 +373,7 @@
staticUse(const SourceString('defineProperty'));
staticUse(const SourceString('toStringForNativeObject'));
staticUse(const SourceString('hashCodeForNativeObject'));
+ staticUse(const SourceString('convertDartClosureToJS'));
addNativeExceptions();
}
@@ -478,7 +479,8 @@
|| libraryName == 'dart:html_common'
|| libraryName == 'dart:indexed_db'
|| libraryName == 'dart:svg'
- || libraryName == 'dart:web_audio') {
+ || libraryName == 'dart:web_audio'
+ || libraryName == 'dart:web_sql') {
library.canUseNative = true;
}
}
diff --git a/sdk/lib/_internal/compiler/implementation/resolution/members.dart b/sdk/lib/_internal/compiler/implementation/resolution/members.dart
index c00f783..54055cd 100644
--- a/sdk/lib/_internal/compiler/implementation/resolution/members.dart
+++ b/sdk/lib/_internal/compiler/implementation/resolution/members.dart
@@ -384,6 +384,14 @@
ResolverVisitor visitor = visitorFor(element);
initializerDo(tree, visitor.visit);
+ if (Elements.isStaticOrTopLevelField(element)) {
+ if (tree.asSendSet() != null) {
+ // TODO(ngeoffray): We could do better here by using the
+ // constant handler to figure out if it's a lazy field or not.
+ compiler.backend.registerLazyField();
+ }
+ }
+
// Perform various checks as side effect of "computing" the type.
element.computeType(compiler);
@@ -923,12 +931,12 @@
class InitializerResolver {
final ResolverVisitor visitor;
- final Map<SourceString, Node> initialized;
+ final Map<Element, Node> initialized;
Link<Node> initializers;
bool hasSuper;
InitializerResolver(this.visitor)
- : initialized = new Map<SourceString, Node>(), hasSuper = false;
+ : initialized = new Map<Element, Node>(), hasSuper = false;
error(Node node, MessageKind kind, [arguments = const {}]) {
visitor.error(node, kind, arguments);
@@ -945,13 +953,31 @@
return node.receiver.asIdentifier().isThis();
}
- void checkForDuplicateInitializers(SourceString name, Node init) {
- if (initialized.containsKey(name)) {
- error(init, MessageKind.DUPLICATE_INITIALIZER, {'fieldName': name});
- warning(initialized[name], MessageKind.ALREADY_INITIALIZED,
- {'fieldName': name});
+ reportDuplicateInitializerError(Element field, Node init, Node existing) {
+ visitor.compiler.reportError(
+ init,
+ new ResolutionError(MessageKind.DUPLICATE_INITIALIZER,
+ {'fieldName': field.name}));
+ visitor.compiler.reportMessage(
+ visitor.compiler.spanFromNode(existing),
+ new ResolutionError(MessageKind.ALREADY_INITIALIZED,
+ {'fieldName': field.name}),
+ Diagnostic.INFO);
+ }
+
+ void checkForDuplicateInitializers(Element field, Node init) {
+ // [field] can be null if it could not be resolved.
+ if (field == null) return;
+ SourceString name = field.name;
+ if (initialized.containsKey(field)) {
+ reportDuplicateInitializerError(field, init, initialized[field]);
+ } else if (field.modifiers.isFinal()) {
+ Node fieldNode = field.parseNode(visitor.compiler).asSendSet();
+ if (fieldNode != null) {
+ reportDuplicateInitializerError(field, init, fieldNode);
+ }
}
- initialized[name] = init;
+ initialized[field] = init;
}
void resolveFieldInitializer(FunctionElement constructor, SendSet init) {
@@ -974,7 +1000,7 @@
}
visitor.useElement(init, target);
visitor.world.registerStaticUse(target);
- checkForDuplicateInitializers(name, init);
+ checkForDuplicateInitializers(target, init);
// Resolve initializing value.
visitor.visitInStaticContext(init.arguments.head);
}
@@ -1113,7 +1139,8 @@
constructor.computeSignature(visitor.compiler);
functionParameters.forEachParameter((Element element) {
if (identical(element.kind, ElementKind.FIELD_PARAMETER)) {
- checkForDuplicateInitializers(element.name,
+ FieldParameterElement fieldParameter = element;
+ checkForDuplicateInitializers(fieldParameter.fieldElement,
element.parseNode(visitor.compiler));
}
});
@@ -1428,7 +1455,8 @@
type = new MalformedType(
new ErroneousElementX(MessageKind.TYPE_ARGUMENT_COUNT_MISMATCH,
{'type': node}, typeName.source, enclosingElement),
- new InterfaceType(cls.declaration, arguments.toLink()));
+ new InterfaceType.userProvidedBadType(cls.declaration,
+ arguments.toLink()));
} else {
if (arguments.isEmpty) {
type = cls.rawType;
@@ -1448,7 +1476,7 @@
type = new MalformedType(
new ErroneousElementX(MessageKind.TYPE_ARGUMENT_COUNT_MISMATCH,
{'type': node}, typeName.source, enclosingElement),
- new TypedefType(typdef, arguments.toLink()));
+ new TypedefType.userProvidedBadType(typdef, arguments.toLink()));
} else {
if (arguments.isEmpty) {
type = typdef.rawType;
@@ -1458,6 +1486,7 @@
}
} else if (element.isTypeVariable()) {
if (enclosingElement.isInStaticMember()) {
+ compiler.backend.registerThrowRuntimeError();
compiler.reportWarning(node,
MessageKind.TYPE_VARIABLE_WITHIN_STATIC_MEMBER.message(
{'typeVariableName': node}));
@@ -1539,6 +1568,7 @@
ClassElement currentClass;
ExpressionStatement currentExpressionStatement;
bool typeRequired = false;
+ bool sendIsMemberAccess = false;
StatementScope statementScope;
int allowedCategory = ElementCategory.VARIABLE | ElementCategory.FUNCTION
| ElementCategory.IMPLIES_TYPE;
@@ -1642,6 +1672,7 @@
element = warnAndCreateErroneousElement(node, node.source,
MessageKind.CANNOT_RESOLVE,
{'name': node});
+ compiler.backend.registerThrowNoSuchMethod();
}
} else if (element.isErroneous()) {
// Use the erroneous element.
@@ -1652,8 +1683,7 @@
{'text': "is not an expression $element"});
}
}
- if (!Elements.isUnresolved(element)
- && element.kind == ElementKind.CLASS) {
+ if (!Elements.isUnresolved(element) && element.isClass()) {
ClassElement classElement = element;
classElement.ensureResolved(compiler);
}
@@ -1833,7 +1863,6 @@
} else {
name = node.name.asIdentifier().source;
}
-
FunctionElement function = new FunctionElementX.node(
name, node, ElementKind.FUNCTION, Modifiers.EMPTY,
enclosingElement);
@@ -1925,9 +1954,12 @@
target = currentClass.lookupSuperMember(name);
// [target] may be null which means invoking noSuchMethod on
// super.
+ if (target == null) {
+ compiler.backend.registerSuperNoSuchMethod();
+ }
} else if (Elements.isUnresolved(resolvedReceiver)) {
return null;
- } else if (identical(resolvedReceiver.kind, ElementKind.CLASS)) {
+ } else if (resolvedReceiver.isClass()) {
ClassElement receiverClass = resolvedReceiver;
receiverClass.ensureResolved(compiler);
if (node.isOperator) {
@@ -1941,6 +1973,7 @@
}
target = receiverClass.lookupLocalMember(name);
if (target == null || target.isInstanceMember()) {
+ compiler.backend.registerThrowNoSuchMethod();
// TODO(johnniwinther): With the simplified [TreeElements] invariant,
// try to resolve injected elements if [currentClass] is in the patch
// library of [receiverClass].
@@ -1959,6 +1992,7 @@
PrefixElement prefix = resolvedReceiver;
target = prefix.lookupLocalMember(name);
if (Elements.isUnresolved(target)) {
+ compiler.backend.registerThrowNoSuchMethod();
return warnAndCreateErroneousElement(
node, name, MessageKind.NO_SUCH_LIBRARY_MEMBER,
{'libraryName': prefix.name, 'memberName': name});
@@ -2063,26 +2097,38 @@
}
visitSend(Send node) {
+ bool oldSendIsMemberAccess = sendIsMemberAccess;
+ sendIsMemberAccess = node.isPropertyAccess || node.isCall;
Element target = resolveSend(node);
- if (!Elements.isUnresolved(target)
- && target.kind == ElementKind.ABSTRACT_FIELD) {
- AbstractFieldElement field = target;
- target = field.getter;
- if (target == null && !inInstanceContext) {
- target =
- warnAndCreateErroneousElement(node.selector, field.name,
- MessageKind.CANNOT_RESOLVE_GETTER);
+ sendIsMemberAccess = oldSendIsMemberAccess;
+
+ if (!Elements.isUnresolved(target)) {
+ if (target.isAbstractField()) {
+ AbstractFieldElement field = target;
+ target = field.getter;
+ if (target == null && !inInstanceContext) {
+ compiler.backend.registerThrowNoSuchMethod();
+ target =
+ warnAndCreateErroneousElement(node.selector, field.name,
+ MessageKind.CANNOT_RESOLVE_GETTER);
+ }
+ } else if (target.impliesType() && !sendIsMemberAccess) {
+ compiler.backend.registerTypeLiteral();
}
}
bool resolvedArguments = false;
if (node.isOperator) {
String operatorString = node.selector.asOperator().source.stringValue;
- if (identical(operatorString, 'is') || identical(operatorString, 'as')) {
+ if (operatorString == 'is' || operatorString == 'as') {
assert(node.arguments.tail.isEmpty);
DartType type = resolveTypeTest(node.arguments.head);
if (type != null) {
- compiler.enqueuer.resolution.registerIsCheck(type);
+ if (operatorString == 'as') {
+ compiler.enqueuer.resolution.registerAsCheck(type);
+ } else {
+ compiler.enqueuer.resolution.registerIsCheck(type);
+ }
}
resolvedArguments = true;
} else if (identical(operatorString, '?')) {
@@ -2141,6 +2187,7 @@
}
void warnArgumentMismatch(Send node, Element target) {
+ compiler.backend.registerThrowNoSuchMethod();
// TODO(karlklose): we can be more precise about the reason of the
// mismatch.
warning(node.argumentsNode, MessageKind.INVALID_ARGUMENTS,
@@ -2163,20 +2210,30 @@
SourceString operatorName = node.assignmentOperator.source;
String source = operatorName.stringValue;
bool isComplex = !identical(source, '=');
- if (!Elements.isUnresolved(target)
- && target.kind == ElementKind.ABSTRACT_FIELD) {
- AbstractFieldElement field = target;
- setter = field.setter;
- getter = field.getter;
- if (setter == null && !inInstanceContext) {
+ if (!Elements.isUnresolved(target)) {
+ if (target.isAbstractField()) {
+ AbstractFieldElement field = target;
+ setter = field.setter;
+ getter = field.getter;
+ if (setter == null && !inInstanceContext) {
+ setter =
+ warnAndCreateErroneousElement(node.selector, field.name,
+ MessageKind.CANNOT_RESOLVE_SETTER);
+ compiler.backend.registerThrowNoSuchMethod();
+ }
+ if (isComplex && getter == null && !inInstanceContext) {
+ getter =
+ warnAndCreateErroneousElement(node.selector, field.name,
+ MessageKind.CANNOT_RESOLVE_GETTER);
+ compiler.backend.registerThrowNoSuchMethod();
+ }
+ } else if (target.impliesType()) {
+ compiler.backend.registerThrowNoSuchMethod();
+ } else if (target.modifiers.isFinal() || target.modifiers.isConst()) {
setter =
- warnAndCreateErroneousElement(node.selector, field.name,
+ warnAndCreateErroneousElement(node.selector, target.name,
MessageKind.CANNOT_RESOLVE_SETTER);
- }
- if (isComplex && getter == null && !inInstanceContext) {
- getter =
- warnAndCreateErroneousElement(node.selector, field.name,
- MessageKind.CANNOT_RESOLVE_GETTER);
+ compiler.backend.registerThrowNoSuchMethod();
}
}
@@ -2328,6 +2385,7 @@
if (!inCatchBlock && node.expression == null) {
error(node, MessageKind.THROW_WITHOUT_EXPRESSION);
}
+ compiler.backend.registerThrow();
visit(node.expression);
}
@@ -2353,7 +2411,10 @@
}
visitParenthesizedExpression(ParenthesizedExpression node) {
+ bool oldSendIsMemberAccess = sendIsMemberAccess;
+ sendIsMemberAccess = false;
visit(node.expression);
+ sendIsMemberAccess = oldSendIsMemberAccess;
}
visitNewExpression(NewExpression node) {
@@ -2363,26 +2424,34 @@
resolveArguments(node.send.argumentsNode);
useElement(node.send, constructor);
if (Elements.isUnresolved(constructor)) return constructor;
- // TODO(karlklose): handle optional arguments.
- if (node.send.argumentCount() != constructor.parameterCount(compiler)) {
- // TODO(ngeoffray): resolution error with wrong number of
- // parameters. We cannot do this rigth now because of the
- // List constructor.
+ Selector callSelector = mapping.getSelector(node.send);
+ if (!callSelector.applies(constructor, compiler)) {
+ warnArgumentMismatch(node.send, constructor);
+ compiler.backend.registerThrowNoSuchMethod();
}
- // [constructor] might be the implementation element and only declaration
- // elements may be registered.
- world.registerStaticUse(constructor.declaration);
compiler.withCurrentElement(constructor, () {
FunctionExpression tree = constructor.parseNode(compiler);
compiler.resolver.resolveConstructorImplementation(constructor, tree);
});
+
+ if (constructor.defaultImplementation != constructor) {
+ // Support for deprecated interface support.
+ // TODO(ngeoffray): Remove once we remove such support.
+ world.registerStaticUse(constructor.declaration);
+ world.registerInstantiatedClass(
+ constructor.getEnclosingClass().declaration);
+ constructor = constructor.defaultImplementation;
+ }
// [constructor.defaultImplementation] might be the implementation element
// and only declaration elements may be registered.
- world.registerStaticUse(constructor.defaultImplementation.declaration);
- ClassElement cls = constructor.defaultImplementation.getEnclosingClass();
+ world.registerStaticUse(constructor.declaration);
+ ClassElement cls = constructor.getEnclosingClass();
// [cls] might be the implementation element and only declaration elements
// may be registered.
world.registerInstantiatedClass(cls.declaration);
+ if (cls.isAbstract(compiler)) {
+ compiler.backend.registerAbstractClassInstantiation();
+ }
// [cls] might be the declaration element and we want to include injected
// members.
cls.implementation.forEachInstanceField(
@@ -2485,6 +2554,7 @@
visitStringInterpolation(StringInterpolation node) {
world.registerInstantiatedClass(compiler.stringClass);
+ compiler.backend.registerStringInterpolation();
node.visitChildren(this);
}
@@ -2625,6 +2695,9 @@
visitLiteralMap(LiteralMap node) {
world.registerInstantiatedClass(compiler.mapClass);
+ if (node.isConst()) {
+ compiler.backend.registerConstantMap();
+ }
node.visitChildren(this);
}
@@ -2702,6 +2775,9 @@
mapping.remove(label.label);
}
});
+ // TODO(ngeoffray): We should check here instead of the SSA backend if
+ // there might be an error.
+ compiler.backend.registerFallThroughError();
}
visitSwitchCase(SwitchCase node) {
@@ -2725,17 +2801,20 @@
}
visitCatchBlock(CatchBlock node) {
+ compiler.backend.registerCatchStatement();
// Check that if catch part is present, then
// it has one or two formal parameters.
if (node.formals != null) {
if (node.formals.isEmpty) {
error(node, MessageKind.EMPTY_CATCH_DECLARATION);
}
- if (!node.formals.nodes.tail.isEmpty &&
- !node.formals.nodes.tail.tail.isEmpty) {
- for (Node extra in node.formals.nodes.tail.tail) {
- error(extra, MessageKind.EXTRA_CATCH_DECLARATION);
+ if (!node.formals.nodes.tail.isEmpty) {
+ if (!node.formals.nodes.tail.tail.isEmpty) {
+ for (Node extra in node.formals.nodes.tail.tail) {
+ error(extra, MessageKind.EXTRA_CATCH_DECLARATION);
+ }
}
+ compiler.backend.registerStackTraceInCatch();
}
// Check that the formals aren't optional and that they have no
@@ -2749,7 +2828,7 @@
if (nodeList != null) {
error(nodeList, MessageKind.OPTIONAL_PARAMETER_IN_CATCH);
} else {
- VariableDefinitions declaration = link.head;
+ VariableDefinitions declaration = link.head;
for (Node modifier in declaration.modifiers.nodes) {
error(modifier, MessageKind.PARAMETER_WITH_MODIFIER_IN_CATCH);
}
@@ -3514,6 +3593,11 @@
failOrReturnErroneousElement(Element enclosing, Node diagnosticNode,
SourceString targetName, MessageKind kind,
Map arguments) {
+ if (kind == MessageKind.CANNOT_FIND_CONSTRUCTOR) {
+ compiler.backend.registerThrowNoSuchMethod();
+ } else {
+ compiler.backend.registerThrowRuntimeError();
+ }
if (inConstContext) {
error(diagnosticNode, kind, arguments);
} else {
diff --git a/sdk/lib/_internal/compiler/implementation/scanner/keyword.dart b/sdk/lib/_internal/compiler/implementation/scanner/keyword.dart
index e9f5930..6f47f73 100644
--- a/sdk/lib/_internal/compiler/implementation/scanner/keyword.dart
+++ b/sdk/lib/_internal/compiler/implementation/scanner/keyword.dart
@@ -106,7 +106,7 @@
Iterator<int> get iterator => new StringCodeIterator(syntax);
void printOn(StringBuffer sb) {
- sb.add(syntax);
+ sb.write(syntax);
}
String toString() => syntax;
@@ -203,19 +203,19 @@
String toString() {
StringBuffer sb = new StringBuffer();
- sb.add("[");
+ sb.write("[");
if (keyword != null) {
- sb.add("*");
- sb.add(keyword);
- sb.add(" ");
+ sb.write("*");
+ sb.write(keyword);
+ sb.write(" ");
}
List<KeywordState> foo = table;
for (int i = 0; i < foo.length; i++) {
if (foo[i] != null) {
- sb.add("${new String.fromCharCodes([i + $a])}: ${foo[i]}; ");
+ sb.write("${new String.fromCharCodes([i + $a])}: ${foo[i]}; ");
}
}
- sb.add("]");
+ sb.write("]");
return sb.toString();
}
}
diff --git a/sdk/lib/_internal/compiler/implementation/scanner/parser.dart b/sdk/lib/_internal/compiler/implementation/scanner/parser.dart
index 5a85e00..8955b00 100644
--- a/sdk/lib/_internal/compiler/implementation/scanner/parser.dart
+++ b/sdk/lib/_internal/compiler/implementation/scanner/parser.dart
@@ -556,7 +556,7 @@
* Returns true if the stringValue of the [token] is [value].
*/
bool optional(String value, Token token) {
- return identical(value, token.stringValue);
+ return identical(value, token.stringValue);
}
/**
diff --git a/sdk/lib/_internal/compiler/implementation/scanner/string_scanner.dart b/sdk/lib/_internal/compiler/implementation/scanner/string_scanner.dart
index 10ad12a..5d1a42a 100644
--- a/sdk/lib/_internal/compiler/implementation/scanner/string_scanner.dart
+++ b/sdk/lib/_internal/compiler/implementation/scanner/string_scanner.dart
@@ -75,7 +75,7 @@
}
void printOn(StringBuffer sb) {
- sb.add(internalString.substring(begin, end));
+ sb.write(internalString.substring(begin, end));
}
String slowToString() {
diff --git a/sdk/lib/_internal/compiler/implementation/scanner/token.dart b/sdk/lib/_internal/compiler/implementation/scanner/token.dart
index c4fda3c..fcf4e87 100644
--- a/sdk/lib/_internal/compiler/implementation/scanner/token.dart
+++ b/sdk/lib/_internal/compiler/implementation/scanner/token.dart
@@ -219,7 +219,7 @@
Iterator<int> get iterator => new StringCodeIterator(stringValue);
void printOn(StringBuffer sb) {
- sb.add(stringValue);
+ sb.write(stringValue);
}
String toString() => stringValue;
diff --git a/sdk/lib/_internal/compiler/implementation/source_file.dart b/sdk/lib/_internal/compiler/implementation/source_file.dart
index 14a4175..ca6d614 100644
--- a/sdk/lib/_internal/compiler/implementation/source_file.dart
+++ b/sdk/lib/_internal/compiler/implementation/source_file.dart
@@ -76,7 +76,7 @@
var buf = new StringBuffer(
'${filename}:${line + 1}:${column + 1}: $message');
if (includeText) {
- buf.add('\n');
+ buf.write('\n');
var textLine;
// +1 for 0-indexing, +1 again to avoid the last line of the file
if ((line + 2) < _lineStarts.length) {
@@ -86,17 +86,17 @@
}
int toColumn = min(column + (end-start), textLine.length);
- buf.add(textLine.substring(0, column));
- buf.add(color(textLine.substring(column, toColumn)));
- buf.add(textLine.substring(toColumn));
+ buf.write(textLine.substring(0, column));
+ buf.write(color(textLine.substring(column, toColumn)));
+ buf.write(textLine.substring(toColumn));
int i = 0;
for (; i < column; i++) {
- buf.add(' ');
+ buf.write(' ');
}
for (; i < toColumn; i++) {
- buf.add(color('^'));
+ buf.write(color('^'));
}
}
diff --git a/sdk/lib/_internal/compiler/implementation/source_map_builder.dart b/sdk/lib/_internal/compiler/implementation/source_map_builder.dart
index 80960d3..6f379b2 100644
--- a/sdk/lib/_internal/compiler/implementation/source_map_builder.dart
+++ b/sdk/lib/_internal/compiler/implementation/source_map_builder.dart
@@ -54,15 +54,15 @@
void printStringListOn(List<String> strings, StringBuffer buffer) {
bool first = true;
- buffer.add('[');
+ buffer.write('[');
for (String string in strings) {
- if (!first) buffer.add(',');
- buffer.add('"');
+ if (!first) buffer.write(',');
+ buffer.write('"');
writeJsonEscapedCharsOn(string, buffer);
- buffer.add('"');
+ buffer.write('"');
first = false;
}
- buffer.add(']');
+ buffer.write(']');
}
String build(SourceFile targetFile) {
@@ -70,18 +70,18 @@
entries.forEach((SourceMapEntry entry) => writeEntry(entry, targetFile,
mappingsBuffer));
StringBuffer buffer = new StringBuffer();
- buffer.add('{\n');
- buffer.add(' "version": 3,\n');
- buffer.add(' "sourceRoot": "",\n');
- buffer.add(' "sources": ');
+ buffer.write('{\n');
+ buffer.write(' "version": 3,\n');
+ buffer.write(' "sourceRoot": "",\n');
+ buffer.write(' "sources": ');
printStringListOn(sourceUrlList, buffer);
- buffer.add(',\n');
- buffer.add(' "names": ');
+ buffer.write(',\n');
+ buffer.write(' "names": ');
printStringListOn(sourceNameList, buffer);
- buffer.add(',\n');
- buffer.add(' "mappings": "');
- buffer.add(mappingsBuffer);
- buffer.add('"\n}\n');
+ buffer.write(',\n');
+ buffer.write(' "mappings": "');
+ buffer.write(mappingsBuffer);
+ buffer.write('"\n}\n');
return buffer.toString();
}
@@ -91,7 +91,7 @@
if (targetLine > previousTargetLine) {
for (int i = previousTargetLine; i < targetLine; ++i) {
- output.add(';');
+ output.write(';');
}
previousTargetLine = targetLine;
previousTargetColumn = 0;
@@ -99,7 +99,7 @@
}
if (!firstEntryInLine) {
- output.add(',');
+ output.write(',');
}
firstEntryInLine = false;
@@ -152,7 +152,7 @@
if (value > 0) {
digit |= VLQ_CONTINUATION_BIT;
}
- output.add(BASE64_DIGITS[digit]);
+ output.write(BASE64_DIGITS[digit]);
} while (value > 0);
}
}
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/bailout.dart b/sdk/lib/_internal/compiler/implementation/ssa/bailout.dart
index 0e10ecf..78ecae6 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/bailout.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/bailout.dart
@@ -58,39 +58,51 @@
/**
* Visits the graph in dominator order and inserts TypeGuards in places where
- * we consider the guard to be of value.
- *
- * Might modify the [types] in an inconsistent way. No further analysis should
- * rely on them.
+ * we consider the guard to be of value. This phase also does type
+ * propagation to help find valuable type guards.
*/
-class SsaTypeGuardInserter extends HGraphVisitor implements OptimizationPhase {
- final Compiler compiler;
+class SsaTypeGuardInserter extends SsaNonSpeculativeTypePropagator
+ implements OptimizationPhase {
final String name = 'SsaTypeGuardInserter';
final CodegenWorkItem work;
- final HTypeMap types;
bool calledInLoop = false;
bool isRecursiveMethod = false;
int stateId = 1;
+ Map<HInstruction, HType> savedTypes = new Map<HInstruction, HType>();
- SsaTypeGuardInserter(this.compiler, this.work, this.types);
+ SsaTypeGuardInserter(compiler, this.work) : super(compiler);
void visitGraph(HGraph graph) {
+ // Run the speculative type propagator. This does in-place
+ // update of the type of the instructions, and saves the
+ // previous types in the [savedTypes] map.
+ SsaTypePropagator propagator =
+ new SsaSpeculativeTypePropagator(compiler, savedTypes);
+ propagator.visitGraph(graph);
+
+ // Put back the original types in the instructions, and save the
+ // speculated types in [savedTypes].
+ Map<HInstruction, HType> speculativeTypes = new Map<HInstruction, HType>();
+
+ savedTypes.forEach((HInstruction instruction, HType type) {
+ speculativeTypes[instruction] = instruction.instructionType;
+ instruction.instructionType = type;
+ });
+ savedTypes = speculativeTypes;
+
+ // Propagate types again, and insert type guards in the graph.
isRecursiveMethod = graph.isRecursiveMethod;
calledInLoop = graph.calledInLoop;
work.guards = <HTypeGuard>[];
visitDominatorTree(graph);
- }
- void visitBasicBlock(HBasicBlock block) {
- block.forEachPhi(visitInstruction);
+ // We need to disable the guards, and therefore re-run a
+ // non-speculative type propagator that will not use the
+ // speculated types.
+ work.guards.forEach((HTypeGuard guard) { guard.disable(); });
- HInstruction instruction = block.first;
- while (instruction != null) {
- // Note that visitInstruction (from the phis and here) might insert an
- // HTypeGuard instruction. We have to skip those.
- if (instruction is !HTypeGuard) visitInstruction(instruction);
- instruction = instruction.next;
- }
+ propagator = new SsaNonSpeculativeTypePropagator(compiler);
+ propagator.visitGraph(graph);
}
// Primitive types that are not null are valuable. These include
@@ -147,10 +159,10 @@
if (isNested(userLoopHeader, currentLoopHeader)) return true;
}
- bool isIndexOperatorOnIndexablePrimitive(instruction, types) {
+ bool isIndexOperatorOnIndexablePrimitive(instruction) {
return instruction is HIndex
|| (instruction is HInvokeDynamicMethod
- && instruction.isIndexOperatorOnIndexablePrimitive(types));
+ && instruction.isIndexOperatorOnIndexablePrimitive());
}
// To speed up computations on values loaded from arrays, we
@@ -161,7 +173,7 @@
// type guard is much smaller than the first one that causes the
// generation of a bailout method.
if (hasTypeGuards
- && isIndexOperatorOnIndexablePrimitive(instruction, types)) {
+ && isIndexOperatorOnIndexablePrimitive(instruction)) {
HBasicBlock loopHeader = instruction.block.enclosingLoopHeader;
if (loopHeader != null && loopHeader.parentLoopHeader != null) {
return true;
@@ -183,34 +195,21 @@
return calledInLoop;
}
- bool shouldInsertTypeGuard(HInstruction instruction,
- HType speculativeType,
- HType computedType) {
+ bool shouldInsertTypeGuard(HInstruction instruction, HType speculativeType) {
if (!speculativeType.isUseful()) return false;
// If the types agree we don't need to check.
- if (speculativeType == computedType) return false;
+ if (speculativeType == instruction.instructionType) return false;
// If a bailout check is more expensive than doing the actual operation
// don't do it either.
return typeGuardWouldBeValuable(instruction, speculativeType);
}
- void visitInstruction(HInstruction instruction) {
- HType speculativeType = types[instruction];
- HType computedType = instruction.computeTypeFromInputTypes(types, compiler);
- // Currently the type in [types] is the speculative type each instruction
- // would like to have. We start by recomputing the type non-speculatively.
- // If we add a type guard then the guard will expose the speculative type.
- // If we don't add a type guard then this avoids that subsequent
- // instructions use the wrong (speculative) type.
- //
- // Note that just setting the speculative type of the instruction is not
- // complete since the type could lead to a phi node which in turn could
- // change the speculative type. In this case we might miss some guards we
- // would have liked to insert. Most of the time this should however be
- // fine, due to dominator-order visiting.
- types[instruction] = computedType;
+ bool updateType(HInstruction instruction) {
+ bool hasChanged = super.updateType(instruction);
+ HType speculativeType = savedTypes[instruction];
+ if (speculativeType == null) return hasChanged;
- if (shouldInsertTypeGuard(instruction, speculativeType, computedType)) {
+ if (shouldInsertTypeGuard(instruction, speculativeType)) {
HInstruction insertionPoint;
if (instruction is HPhi) {
insertionPoint = instruction.block.first;
@@ -238,11 +237,16 @@
insertionPoint.block.addBefore(insertionPoint, target);
}
HTypeGuard guard = new HTypeGuard(speculativeType, instruction, target);
- types[guard] = speculativeType;
work.guards.add(guard);
+ // By setting the type of the guard to the speculated type, we
+ // help the analysis find valuable type guards. This however
+ // requires to run a non-speculative type propagation again
+ // after this analysis.
+ guard.instructionType = speculativeType;
instruction.block.rewrite(instruction, guard);
insertionPoint.block.addBefore(insertionPoint, guard);
}
+ return hasChanged;
}
}
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/builder.dart b/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
index 264334a..0c387a8 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
@@ -21,17 +21,12 @@
class SsaBuilderTask extends CompilerTask {
final CodeEmitterTask emitter;
- // Loop tracking information.
- final Set<FunctionElement> functionsCalledInLoop;
- final Map<SourceString, Selector> selectorsCalledInLoop;
final JavaScriptBackend backend;
String get name => 'SSA builder';
SsaBuilderTask(JavaScriptBackend backend)
: emitter = backend.emitter,
- functionsCalledInLoop = new Set<FunctionElement>(),
- selectorsCalledInLoop = new Map<SourceString, Selector>(),
backend = backend,
super(backend.compiler);
@@ -58,12 +53,9 @@
}
assert(graph.isValid());
if (!identical(kind, ElementKind.FIELD)) {
- bool inLoop = functionsCalledInLoop.contains(element.declaration);
- if (!inLoop) {
- Selector selector = selectorsCalledInLoop[element.name];
- inLoop = selector != null && selector.applies(element, compiler);
- }
- graph.calledInLoop = inLoop;
+ Set<Selector> selectors = backend.selectorsCalledInLoop[element.name];
+ graph.calledInLoop = selectors != null &&
+ selectors.any((selector) => selector.applies(element, compiler));
// If there is an estimate of the parameter types assume these types
// when compiling.
@@ -92,7 +84,7 @@
if (!parameterTypes.allUnknown) {
int i = 0;
signature.forEachParameter((Element param) {
- builder.parameters[param].guaranteedType = parameterTypes[i++];
+ builder.parameters[param].instructionType = parameterTypes[i++];
});
}
backend.registerParameterTypesOptimization(
@@ -250,21 +242,27 @@
HType cachedTypeOfThis;
- HType computeTypeOfThis() {
- Element element = closureData.thisElement;
- ClassElement cls = element.enclosingElement.getEnclosingClass();
- Compiler compiler = builder.compiler;
- DartType type = cls.computeType(compiler);
- if (compiler.world.isUsedAsMixin(cls)) {
- // If the enclosing class is used as a mixin, [:this:] can be
- // of the class that mixins the enclosing class. These two
- // classes do not have a subclass relationship, so, for
- // simplicity, we mark the type as an interface type.
- cachedTypeOfThis = new HType.nonNullSubtype(type, compiler);
- } else {
- cachedTypeOfThis = new HType.nonNullSubclass(type, compiler);
+ HType getTypeOfThis() {
+ HType result = cachedTypeOfThis;
+ if (result == null) {
+ Element element = closureData.thisElement;
+ ClassElement cls = element.enclosingElement.getEnclosingClass();
+ Compiler compiler = builder.compiler;
+ // Use the raw type because we don't have the type context for the
+ // type parameters.
+ DartType type = cls.rawType;
+ if (compiler.world.isUsedAsMixin(cls)) {
+ // If the enclosing class is used as a mixin, [:this:] can be
+ // of the class that mixins the enclosing class. These two
+ // classes do not have a subclass relationship, so, for
+ // simplicity, we mark the type as an interface type.
+ result = new HType.nonNullSubtype(type, compiler);
+ } else {
+ result = new HType.nonNullSubclass(type, compiler);
+ }
+ cachedTypeOfThis = result;
}
- return cachedTypeOfThis;
+ return result;
}
/**
@@ -295,8 +293,8 @@
HInstruction parameter = builder.addParameter(parameterElement);
builder.parameters[parameterElement] = parameter;
directLocals[parameterElement] = parameter;
- parameter.guaranteedType =
- builder.getGuaranteedTypeOfElement(parameterElement);
+ parameter.instructionType =
+ new HType.inferredForElement(parameterElement, compiler);
});
}
@@ -320,7 +318,7 @@
// not have any thisElement if the closure was created inside a static
// context.
HThis thisInstruction = new HThis(
- closureData.thisElement, computeTypeOfThis());
+ closureData.thisElement, getTypeOfThis());
builder.graph.thisInstruction = thisInstruction;
builder.graph.entry.addAtEntry(thisInstruction);
directLocals[closureData.thisElement] = thisInstruction;
@@ -351,7 +349,7 @@
builder.graph.entry.addAfter(
directLocals[closureData.thisElement], value);
directLocals[closureData.thisElement] = value;
- value.guaranteedType = type;
+ value.instructionType = type;
}
}
@@ -434,11 +432,8 @@
HInstruction readThis() {
HInstruction res = readLocal(closureData.thisElement);
- if (res.guaranteedType == null) {
- if (cachedTypeOfThis == null) {
- computeTypeOfThis();
- }
- res.guaranteedType = cachedTypeOfThis;
+ if (res.instructionType == null) {
+ res.instructionType = getTypeOfThis();
}
return res;
}
@@ -1009,6 +1004,12 @@
compiledArguments);
assert(succeeded);
+ // Create the inlining state after evaluating the arguments, that
+ // may have an impact on the state of the current method.
+ InliningState state = new InliningState(
+ function, returnElement, returnType, elements, stack, localsHandler);
+ localsHandler = new LocalsHandler.from(localsHandler);
+
FunctionSignature signature = function.computeSignature(compiler);
int index = 0;
signature.orderedForEachParameter((Element parameter) {
@@ -1036,8 +1037,6 @@
}
}
}
- InliningState state =
- new InliningState(function, returnElement, returnType, elements, stack);
// TODO(kasperl): Bad smell. We shouldn't be constructing elements here.
returnElement = new ElementX(const SourceString("result"),
@@ -1063,6 +1062,7 @@
assert(stack.length == 1);
state.oldStack.add(stack[0]);
stack = state.oldStack;
+ localsHandler = state.oldLocalsHandler;
}
/**
@@ -1073,6 +1073,12 @@
Selector selector,
Link<Node> arguments,
Node currentNode) {
+ // We cannot inline a method from a deferred library into a method
+ // which isn't deferred.
+ // TODO(ahe): But we should still inline into the same
+ // connected-component of the deferred library.
+ if (compiler.deferredLoadTask.isDeferred(element)) return false;
+
if (compiler.disableInlining) return false;
// Ensure that [element] is an implementation element.
element = element.implementation;
@@ -2322,9 +2328,7 @@
buildInvokeDynamic(send, selector, left, [right]),
op);
if (op.source.stringValue == '!=') {
- HBoolify bl = new HBoolify(pop());
- add(bl);
- pushWithPosition(new HNot(bl), op);
+ pushWithPosition(new HNot(popBoolified()), op);
}
}
@@ -2367,22 +2371,23 @@
Set<ClassElement> interceptedClasses = getInterceptedClassesOn(selector);
bool hasGetter = compiler.world.hasAnyUserDefinedGetter(selector);
+ HInstruction instruction;
if (interceptedClasses != null) {
// If we're using an interceptor class, emit a call to the
// interceptor method and then the actual dynamic call on the
// interceptor object.
- HInstruction instruction =
+ instruction =
invokeInterceptor(interceptedClasses, receiver, send);
instruction = new HInvokeDynamicGetter(
selector, null, instruction, !hasGetter);
// Add the receiver as an argument to the getter call on the
// interceptor.
instruction.inputs.add(receiver);
- pushWithPosition(instruction, send);
} else {
- pushWithPosition(
- new HInvokeDynamicGetter(selector, null, receiver, !hasGetter), send);
+ instruction = new HInvokeDynamicGetter(
+ selector, null, receiver, !hasGetter);
}
+ pushWithPosition(instruction, send);
}
void generateGetter(Send send, Element element) {
@@ -2655,8 +2660,7 @@
void argumentsCheck() {
HInstruction typeInfo = getRuntimeTypeInfo(expression);
- Element helper =
- compiler.findHelper(const SourceString('checkArguments'));
+ Element helper = backend.getCheckArguments();
HInstruction helperCall = new HStatic(helper);
add(helperCall);
List<HInstruction> representations =
@@ -2789,26 +2793,28 @@
}
}
- visitDynamicSend(Send node) {
+ bool isThisSend(Send send) {
+ Node receiver = send.receiver;
+ if (receiver == null) return true;
+ Identifier identifier = receiver.asIdentifier();
+ return identifier != null && identifier.isThis();
+ }
+
+ visitDynamicSend(Send node, {bool inline: true}) {
Selector selector = elements.getSelector(node);
- SourceString dartMethodName;
- bool isNotEquals = false;
- if (node.isIndex && !node.arguments.tail.isEmpty) {
- dartMethodName = Elements.constructOperatorName(
- const SourceString('[]='), false);
- } else if (node.selector.asOperator() != null) {
- SourceString name = node.selector.asIdentifier().source;
- isNotEquals = identical(name.stringValue, '!=');
- dartMethodName = Elements.constructOperatorName(
- name, node.argumentsNode is Prefix);
- } else {
- dartMethodName = node.selector.asIdentifier().source;
+ // TODO(kasperl): It would be much better to try to get the
+ // guaranteed type of the receiver after we've evaluated it, but
+ // because of the way inlining currently works that is hard to do
+ // with re-evaluating the receiver.
+ if (isThisSend(node)) {
+ HType receiverType = localsHandler.getTypeOfThis();
+ selector = receiverType.refine(selector, compiler);
}
- Element element = elements[node];
+ Element element = compiler.world.locateSingleElement(selector);
bool isClosureCall = false;
- if (element != null && compiler.world.hasNoOverridingMember(element)) {
+ if (inline && element != null) {
if (tryInlineMethod(element, selector, node.arguments, node)) {
if (element.isGetter()) {
// If the element is a getter, we are doing a closure call
@@ -2845,11 +2851,6 @@
}
pushWithPosition(invoke, node);
-
- if (isNotEquals) {
- HNot not = new HNot(popBoolified());
- push(not);
- }
}
visitClosureSend(Send node) {
@@ -2887,7 +2888,7 @@
native.NativeBehavior nativeBehavior =
compiler.enqueuer.resolution.nativeEnqueuer.getNativeBehaviorOf(node);
- HType ssaType = mapNativeBehaviorType(nativeBehavior);
+ HType ssaType = new HType.fromNativeBehavior(nativeBehavior, compiler);
if (code is StringNode) {
StringNode codeString = code;
if (!codeString.isInterpolation) {
@@ -3069,8 +3070,7 @@
Constant internalNameConstant =
constantSystem.createString(new DartString.literal(internalName), node);
- Element createInvocationMirror =
- compiler.findHelper(Compiler.CREATE_INVOCATION_MIRROR);
+ Element createInvocationMirror = backend.getCreateInvocationMirror();
var arguments = new List<HInstruction>();
if (node.argumentsNode != null) {
@@ -3375,7 +3375,7 @@
}
HInvokeStatic instruction = new HInvokeStatic(inputs, HType.UNKNOWN);
- HType returnType = getGuaranteedTypeOfElement(element);
+ HType returnType = new HType.inferredForElement(element, compiler);
if (returnType.isUnknown()) {
// TODO(ngeoffray): Only do this if knowing the return type is
// useful.
@@ -3383,7 +3383,7 @@
builder.backend.optimisticReturnTypesWithRecompilationOnTypeChange(
currentElement, element);
}
- if (returnType != null) instruction.guaranteedType = returnType;
+ if (returnType != null) instruction.instructionType = returnType;
pushWithPosition(instruction, node);
} else {
generateGetter(node, element);
@@ -3456,8 +3456,7 @@
{Link<Node> argumentNodes,
List<HInstruction> argumentValues,
List<String> existingArguments}) {
- Element helper =
- compiler.findHelper(const SourceString('throwNoSuchMethod'));
+ Element helper = backend.getThrowNoSuchMethod();
Constant receiverConstant =
constantSystem.createString(new DartString.empty(), diagnosticNode);
HInstruction receiver = graph.addConstant(receiverConstant);
@@ -3574,14 +3573,7 @@
}
inputs.add(receiver);
inputs.addAll(arguments);
- HInstruction invoke = new HInvokeDynamicMethod(
- selector, inputs, isIntercepted);
- HType returnType = mapInferredType(
- compiler.typesTask.getGuaranteedTypeOfNode(work.element, node));
- if (returnType != null) {
- invoke.guaranteedType = returnType;
- }
- return invoke;
+ return new HInvokeDynamicMethod(selector, inputs, isIntercepted);
}
visitSendSet(SendSet node) {
@@ -3594,7 +3586,9 @@
}
Operator op = node.assignmentOperator;
if (node.isSuperCall) {
- if (element == null) return generateSuperNoSuchMethodSend(node);
+ if (Elements.isUnresolved(element)) {
+ return generateSuperNoSuchMethodSend(node);
+ }
HInstruction target = new HStatic(element);
HInstruction context = localsHandler.readThis();
add(target);
@@ -3607,7 +3601,9 @@
push(new HInvokeSuper(inputs, isSetter: true));
} else if (node.isIndex) {
if (const SourceString("=") == op.source) {
- visitDynamicSend(node);
+ // TODO(kasperl): We temporarily disable inlining because the
+ // code here cannot deal with it yet.
+ visitDynamicSend(node, inline: false);
HInvokeDynamicMethod method = pop();
// Push the value.
stack.add(method.inputs.last);
@@ -3620,7 +3616,6 @@
// Compound assignments are considered as being prefix.
bool isCompoundAssignment = op.source.stringValue.endsWith('=');
bool isPrefix = !node.isPostfix;
- Element getter = elements[node.selector];
if (isCompoundAssignment) {
value = pop();
index = pop();
@@ -3644,7 +3639,6 @@
}
}
} else if (const SourceString("=") == op.source) {
- Element element = elements[node];
Link<Node> link = node.arguments;
assert(!link.isEmpty && link.tail.isEmpty);
visit(link.head);
@@ -3656,13 +3650,11 @@
assert(const SourceString("++") == op.source ||
const SourceString("--") == op.source ||
node.assignmentOperator.source.stringValue.endsWith("="));
- Element element = elements[node];
bool isCompoundAssignment = !node.arguments.isEmpty;
bool isPrefix = !node.isPostfix; // Compound assignments are prefix.
// [receiver] is only used if the node is an instance send.
HInstruction receiver = null;
- Element selectorElement = elements[node];
if (Elements.isInstanceSend(node, elements)) {
receiver = generateInstanceSendReceiver(node);
generateInstanceGetterWithCompiledReceiver(node, receiver);
@@ -3932,12 +3924,12 @@
bool hasGetter = compiler.world.hasAnyUserDefinedGetter(selector);
if (interceptedClasses == null) {
iterator =
- new HInvokeDynamicGetter(selector, null, receiver, hasGetter);
+ new HInvokeDynamicGetter(selector, null, receiver, !hasGetter);
} else {
HInterceptor interceptor =
invokeInterceptor(interceptedClasses, receiver, null);
iterator =
- new HInvokeDynamicGetter(selector, null, interceptor, hasGetter);
+ new HInvokeDynamicGetter(selector, null, interceptor, !hasGetter);
// Add the receiver as an argument to the getter call on the
// interceptor.
iterator.inputs.add(receiver);
@@ -3948,7 +3940,6 @@
SourceString name = const SourceString('moveNext');
Selector selector = new Selector.call(
name, currentElement.getLibrary(), 0);
- bool hasGetter = compiler.world.hasAnyUserDefinedGetter(selector);
push(new HInvokeDynamicMethod(selector, <HInstruction>[iterator]));
return popBoolified();
}
@@ -3956,7 +3947,7 @@
SourceString name = const SourceString('current');
Selector call = new Selector.getter(name, currentElement.getLibrary());
bool hasGetter = compiler.world.hasAnyUserDefinedGetter(call);
- push(new HInvokeDynamicGetter(call, null, iterator, hasGetter));
+ push(new HInvokeDynamicGetter(call, null, iterator, !hasGetter));
Element variable;
if (node.declaredIdentifier.asSend() != null) {
@@ -4186,8 +4177,7 @@
List<List<Constant>> matchExpressions = <List<Constant>>[];
List<HStatementInformation> statements = <HStatementInformation>[];
bool hasDefault = false;
- Element getFallThroughErrorElement =
- compiler.findHelper(const SourceString("getFallThroughError"));
+ Element getFallThroughErrorElement = backend.getFallThroughError();
HasNextIterator<Node> caseIterator =
new HasNextIterator<Node>(node.cases.iterator);
while (caseIterator.hasNext) {
@@ -4480,14 +4470,19 @@
if (catchBlock.onKeyword != null) {
DartType type = elements.getType(catchBlock.type);
if (type == null) {
- compiler.cancel('On with unresolved type',
- node: catchBlock.type);
+ compiler.internalError('On with no type', node: catchBlock.type);
}
- HInstruction condition =
- new HIs(type, <HInstruction>[unwrappedException]);
- push(condition);
- }
- else {
+ if (type.isMalformed) {
+ // TODO(johnniwinther): Handle malformed types in [HIs] instead.
+ HInstruction condition =
+ graph.addConstantBool(true, constantSystem);
+ stack.add(condition);
+ } else {
+ HInstruction condition =
+ new HIs(type, <HInstruction>[unwrappedException]);
+ push(condition);
+ }
+ } else {
VariableDefinitions declaration = catchBlock.formals.nodes.head;
HInstruction condition = null;
if (declaration.type == null) {
@@ -4512,6 +4507,21 @@
void visitThen() {
CatchBlock catchBlock = link.head;
link = link.tail;
+
+ if (compiler.enableTypeAssertions) {
+ // In checked mode: throw a type error if the on-catch type is
+ // malformed.
+ if (catchBlock.onKeyword != null) {
+ DartType type = elements.getType(catchBlock.type);
+ if (type != null && type.isMalformed) {
+ String reasons = Types.fetchReasonsFromMalformedType(type);
+ generateMalformedSubtypeError(node,
+ unwrappedException, type, reasons);
+ pop();
+ return;
+ }
+ }
+ }
if (catchBlock.exception != null) {
localsHandler.updateLocal(elements[catchBlock.exception],
unwrappedException);
@@ -4639,64 +4649,6 @@
visitTypeVariable(TypeVariable node) {
compiler.internalError('SsaBuilder.visitTypeVariable');
}
-
- HType mapBaseType(BaseType baseType) {
- if (!baseType.isClass()) return HType.UNKNOWN;
- ClassBaseType classBaseType = baseType;
- ClassElement cls = classBaseType.element;
- // Special case the list and map classes that are used as types
- // for literals in the type inferrer.
- if (cls == compiler.listClass) {
- return HType.READABLE_ARRAY;
- } else if (cls == compiler.mapClass) {
- // TODO(ngeoffray): get the actual implementation of a map
- // literal.
- return new HType.nonNullSubtype(
- compiler.mapLiteralClass.computeType(compiler), compiler);
- } else {
- return new HType.nonNullExactClass(
- cls.computeType(compiler), compiler);
- }
- }
-
- HType mapInferredType(ConcreteType concreteType) {
- if (concreteType == null) return HType.UNKNOWN;
- HType ssaType = HType.CONFLICTING;
- for (BaseType baseType in concreteType.baseTypes) {
- ssaType = ssaType.union(mapBaseType(baseType), compiler);
- }
- if (ssaType.isConflicting()) return HType.UNKNOWN;
- return ssaType;
- }
-
- HType getGuaranteedTypeOfElement(Element element) {
- return mapInferredType(
- compiler.typesTask.getGuaranteedTypeOfElement(element));
- }
-
- // [type] is either an instance of [DartType] or special objects
- // like [native.SpecialType.JsObject], or [native.SpecialType.JsArray].
- HType mapNativeType(type) {
- if (type == native.SpecialType.JsObject) {
- return new HType.nonNullExactClass(
- compiler.objectClass.computeType(compiler), compiler);
- } else if (type == native.SpecialType.JsArray) {
- return HType.READABLE_ARRAY;
- } else {
- return new HType.nonNullSubclass(type, compiler);
- }
- }
-
- HType mapNativeBehaviorType(native.NativeBehavior nativeBehavior) {
- if (nativeBehavior.typesInstantiated.isEmpty) return HType.UNKNOWN;
-
- HType ssaType = HType.CONFLICTING;
- for (final type in nativeBehavior.typesInstantiated) {
- ssaType = ssaType.union(mapNativeType(type), compiler);
- }
- assert(!ssaType.isConflicting());
- return ssaType;
- }
}
/**
@@ -4838,12 +4790,14 @@
final DartType oldReturnType;
final TreeElements oldElements;
final List<HInstruction> oldStack;
+ final LocalsHandler oldLocalsHandler;
InliningState(this.function,
this.oldReturnElement,
this.oldReturnType,
this.oldElements,
- this.oldStack) {
+ this.oldStack,
+ this.oldLocalsHandler) {
assert(function.isImplementation);
}
}
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/codegen.dart b/sdk/lib/_internal/compiler/implementation/ssa/codegen.dart
index def1a12..527ce59 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/codegen.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/codegen.dart
@@ -162,7 +162,6 @@
final JavaScriptBackend backend;
final CodegenWorkItem work;
- final HTypeMap types;
final Set<HInstruction> generateAtUseSite;
final Set<HInstruction> controlFlowOperators;
@@ -207,8 +206,6 @@
SsaCodeGenerator(this.backend, CodegenWorkItem work)
: this.work = work,
- this.types =
- (work.compilationContext as JavaScriptItemCompilationContext).types,
declaredLocals = new Set<String>(),
collectedVariableDeclarations = new OrderedSet<String>(),
currentContainer = new js.Block.empty(),
@@ -338,9 +335,9 @@
endLabeledBlock(HLabeledBlockInformation labeledBlockInfo);
void preGenerateMethod(HGraph graph) {
- new SsaInstructionMerger(types, generateAtUseSite).visitGraph(graph);
+ new SsaInstructionMerger(generateAtUseSite).visitGraph(graph);
new SsaConditionMerger(
- types, generateAtUseSite, controlFlowOperators).visitGraph(graph);
+ generateAtUseSite, controlFlowOperators).visitGraph(graph);
SsaLiveIntervalBuilder intervalBuilder =
new SsaLiveIntervalBuilder(compiler, generateAtUseSite);
intervalBuilder.visitGraph(graph);
@@ -361,7 +358,8 @@
HInstruction last = block.last;
assert(last is HGoto || last is HReturn);
if (last is HReturn) {
- backend.registerReturnType(work.element, types[last.inputs[0]]);
+ backend.registerReturnType(
+ work.element, last.inputs[0].instructionType);
} else {
backend.registerReturnType(work.element, HType.NULL);
}
@@ -1269,13 +1267,15 @@
}
}
- void emitIdentityComparison(HInstruction left, HInstruction right) {
- String op = singleIdentityComparison(left, right, types);
+ void emitIdentityComparison(HInstruction left,
+ HInstruction right,
+ bool inverse) {
+ String op = singleIdentityComparison(left, right);
if (op != null) {
use(left);
js.Expression jsLeft = pop();
use(right);
- push(new js.Binary(op, jsLeft, pop()));
+ push(new js.Binary(mapRelationalOperator(op, inverse), jsLeft, pop()));
} else {
assert(NullConstant.JsNull == 'null');
use(left);
@@ -1283,17 +1283,19 @@
new js.Binary("==", pop(), new js.LiteralNull());
use(right);
js.Binary rightEqualsNull =
- new js.Binary("==", pop(), new js.LiteralNull());
+ new js.Binary(mapRelationalOperator("==", inverse),
+ pop(), new js.LiteralNull());
use(right);
use(left);
- js.Binary tripleEq = new js.Binary("===", pop(), pop());
+ js.Binary tripleEq = new js.Binary(mapRelationalOperator("===", inverse),
+ pop(), pop());
push(new js.Conditional(leftEqualsNull, rightEqualsNull, tripleEq));
}
}
visitIdentity(HIdentity node) {
- emitIdentityComparison(node.left, node.right);
+ emitIdentityComparison(node.left, node.right, false);
}
visitAdd(HAdd node) => visitInvokeBinary(node, '+');
@@ -1566,11 +1568,9 @@
void visitOneShotInterceptor(HOneShotInterceptor node) {
List<js.Expression> arguments = visitArguments(node.inputs);
var isolate = new js.VariableUse(backend.namer.CURRENT_ISOLATE);
- Selector selector = node.selector;
- String methodName = backend.namer.oneShotInterceptorName(selector);
+ Selector selector = getOptimizedSelectorFor(node, node.selector);
+ String methodName = backend.registerOneShotInterceptor(selector);
push(jsPropertyCall(isolate, methodName, arguments), node);
- backend.registerSpecializedGetInterceptor(node.interceptedClasses);
- backend.addOneShotInterceptor(selector);
if (selector.isGetter()) {
registerGetter(node);
} else if (selector.isSetter()) {
@@ -1580,51 +1580,35 @@
}
}
- Selector getOptimizedSelectorFor(HInvokeDynamic node,
- Selector defaultSelector) {
+ Selector getOptimizedSelectorFor(HInvokeDynamic node, Selector selector) {
// If [JSInvocationMirror.invokeOn] has been called, we must not create a
// typed selector based on the receiver type.
if (node.element == null && // Invocation is not exact.
backend.compiler.enabledInvokeOn) {
- return defaultSelector;
+ return selector;
}
int receiverIndex = node.isInterceptorCall ? 1 : 0;
- HType receiverHType = types[node.inputs[receiverIndex]];
- DartType receiverType = receiverHType.computeType(compiler);
- if (receiverType != null && !receiverType.isMalformed) {
- if (receiverHType.isExact()) {
- return new TypedSelector.exact(receiverType, defaultSelector);
- } else if (receiverHType.isInterfaceType()) {
- return new TypedSelector.subtype(receiverType, defaultSelector);
- } else {
- return new TypedSelector.subclass(receiverType, defaultSelector);
- }
- } else {
- return defaultSelector;
- }
+ HType receiverType = node.inputs[receiverIndex].instructionType;
+ return receiverType.refine(selector, compiler);
}
- void registerInvoke(HInvokeDynamic node) {
+ void registerInvoke(HInvokeDynamic node, Selector selector) {
bool inLoop = node.block.enclosingLoopHeader != null;
- SourceString name = node.selector.name;
if (inLoop) {
- Element target = node.element;
- if (target != null) {
- backend.builder.functionsCalledInLoop.add(target);
- } else {
- backend.builder.selectorsCalledInLoop[name] = node.selector;
- }
+ Set<Selector> selectors = backend.selectorsCalledInLoop.putIfAbsent(
+ selector.name, () => new Set<Selector>());
+ selectors.add(selector);
}
if (node.isInterceptorCall) {
- backend.addInterceptedSelector(node.selector);
+ backend.addInterceptedSelector(selector);
}
}
void registerMethodInvoke(HInvokeDynamic node) {
Selector selector = getOptimizedSelectorFor(node, node.selector);
// Register this invocation to collect the types used at all call sites.
- backend.registerDynamicInvocation(node, selector, types);
+ backend.registerDynamicInvocation(node, selector);
// If we don't know what we're calling or if we are calling a getter,
// we need to register that fact that we may be calling a closure
@@ -1647,39 +1631,36 @@
SourceString name = node.selector.name;
world.registerDynamicInvocation(name, selector);
}
- registerInvoke(node);
+ registerInvoke(node, selector);
}
void registerSetter(HInvokeDynamic node) {
Selector selector = getOptimizedSelectorFor(node, node.selector);
world.registerDynamicSetter(selector.name, selector);
HType valueType = node.isInterceptorCall
- ? types[node.inputs[2]]
- : types[node.inputs[1]];
+ ? node.inputs[2].instructionType
+ : node.inputs[1].instructionType;
backend.addedDynamicSetter(selector, valueType);
- registerInvoke(node);
+ registerInvoke(node, selector);
}
void registerGetter(HInvokeDynamic node) {
- Selector getter = node.selector;
- world.registerDynamicGetter(
- getter.name, getOptimizedSelectorFor(node, getter));
+ Selector selector = getOptimizedSelectorFor(node, node.selector);
+ world.registerDynamicGetter(selector.name, selector);
world.registerInstantiatedClass(compiler.functionClass);
- registerInvoke(node);
+ registerInvoke(node, selector);
}
visitInvokeDynamicSetter(HInvokeDynamicSetter node) {
use(node.receiver);
- Selector setter = node.selector;
- String name = backend.namer.invocationName(setter);
+ String name = backend.namer.invocationName(node.selector);
push(jsPropertyCall(pop(), name, visitArguments(node.inputs)), node);
registerSetter(node);
}
visitInvokeDynamicGetter(HInvokeDynamicGetter node) {
use(node.receiver);
- Selector getter = node.selector;
- String name = backend.namer.invocationName(getter);
+ String name = backend.namer.invocationName(node.selector);
push(jsPropertyCall(pop(), name, visitArguments(node.inputs)), node);
registerGetter(node);
}
@@ -1697,7 +1678,7 @@
visitInvokeStatic(HInvokeStatic node) {
if (node.typeCode() == HInstruction.INVOKE_STATIC_TYPECODE) {
// Register this invocation to collect the types used at all call sites.
- backend.registerStaticInvocation(node, types);
+ backend.registerStaticInvocation(node);
}
use(node.target);
push(new js.Call(pop(), visitArguments(node.inputs)), node);
@@ -1748,7 +1729,7 @@
} else {
String name = _fieldPropertyName(element);
push(new js.PropertyAccess.field(pop(), name), node);
- DartType type = types[node.receiver].computeType(compiler);
+ DartType type = node.receiver.instructionType.computeType(compiler);
if (type != null && !identical(type.kind, TypeKind.MALFORMED_TYPE)) {
world.registerFieldGetter(element);
}
@@ -1758,13 +1739,14 @@
visitFieldSet(HFieldSet node) {
Element element = node.element;
String name = _fieldPropertyName(element);
- DartType type = types[node.receiver].computeType(compiler);
+ DartType type = node.receiver.instructionType.computeType(compiler);
if (type != null && !identical(type.kind, TypeKind.MALFORMED_TYPE)) {
// Field setters in the generative constructor body are handled in a
// step "SsaConstructionFieldTypes" in the ssa optimizer.
if (!work.element.isGenerativeConstructorBody()) {
world.registerFieldSetter(element);
- backend.registerFieldSetter(work.element, element, types[node.value]);
+ backend.registerFieldSetter(
+ work.element, element, node.value.instructionType);
}
}
use(node.receiver);
@@ -1813,7 +1795,7 @@
}
push(new js.LiteralExpression.withData(code, data), node);
}
- registerForeignType(types[node]);
+ registerForeignType(node.instructionType);
// TODO(sra): Tell world.nativeEnqueuer about the types created here.
}
@@ -1829,7 +1811,7 @@
// TODO(floitsch): jsClassReference is an Access. We shouldn't treat it
// as if it was a string.
push(new js.New(new js.VariableUse(jsClassReference), arguments), node);
- registerForeignType(types[node]);
+ registerForeignType(node.instructionType);
}
js.Expression newLiteralBool(bool value) {
@@ -1866,6 +1848,20 @@
attachLocationToLast(node);
}
+ static String mapRelationalOperator(String op, bool inverse) {
+ Map<String, String> inverseOperator = const <String, String>{
+ "==" : "!=",
+ "!=" : "==",
+ "===": "!==",
+ "!==": "===",
+ "<" : ">=",
+ "<=" : ">",
+ ">" : "<=",
+ ">=" : "<"
+ };
+ return inverse ? inverseOperator[op] : op;
+ }
+
void generateNot(HInstruction input) {
bool canGenerateOptimizedComparison(HInstruction instruction) {
if (instruction is !HRelational) return false;
@@ -1874,28 +1870,21 @@
HInstruction right = relational.right;
// This optimization doesn't work for NaN, so we only do it if the
// type is known to be an integer.
- return types[left].isUseful() && left.isInteger(types)
- && types[right].isUseful() && right.isInteger(types);
+ return left.instructionType.isUseful() && left.isInteger()
+ && right.instructionType.isUseful() && right.isInteger();
}
- if (input is HBoolify && isGenerateAtUseSite(input)) {
+ bool generateAtUseSite = isGenerateAtUseSite(input);
+ if (input is HIdentity && generateAtUseSite) {
+ emitIdentityComparison(input.left, input.right, true);
+ } else if (input is HBoolify && generateAtUseSite) {
use(input.inputs[0]);
push(new js.Binary("!==", pop(), newLiteralBool(true)), input);
- } else if (canGenerateOptimizedComparison(input) &&
- isGenerateAtUseSite(input)) {
- Map<String, String> inverseOperator = const <String, String>{
- "==" : "!=",
- "!=" : "==",
- "===": "!==",
- "!==": "===",
- "<" : ">=",
- "<=" : ">",
- ">" : "<=",
- ">=" : "<"
- };
+ } else if (canGenerateOptimizedComparison(input) && generateAtUseSite) {
HRelational relational = input;
BinaryOperation operation = relational.operation(backend.constantSystem);
- visitRelational(input, inverseOperator[operation.name.stringValue]);
+ String op = mapRelationalOperator(operation.name.stringValue, true);
+ visitRelational(input, op);
} else {
use(input);
push(new js.Prefix("!", pop()));
@@ -2113,10 +2102,10 @@
}
void useStringified(HInstruction node) {
- if (node.isString(types)) {
+ if (node.isString()) {
use(node);
} else {
- Element convertToString = compiler.findHelper(const SourceString("S"));
+ Element convertToString = backend.getStringInterpolationHelper();
world.registerStaticUse(convertToString);
js.VariableUse variableUse =
new js.VariableUse(backend.namer.isolateAccess(convertToString));
@@ -2376,7 +2365,8 @@
checkType(input, type);
push(new js.Binary('&&', nullTest, pop()));
attachLocationToLast(node);
- } else if (types[input].canBePrimitive() || types[input].canBeNull()) {
+ } else if (input.instructionType.canBePrimitive()
+ || input.instructionType.canBeNull()) {
checkObject(input, '===');
js.Expression objectTest = pop();
checkType(input, type);
@@ -2391,47 +2381,9 @@
}
}
- // TODO(johnniwinther): Refactor this method.
void visitTypeConversion(HTypeConversion node) {
- Map<String, SourceString> castNames = const <String, SourceString> {
- "stringTypeCheck":
- const SourceString("stringTypeCast"),
- "doubleTypeCheck":
- const SourceString("doubleTypeCast"),
- "numTypeCheck":
- const SourceString("numTypeCast"),
- "boolTypeCheck":
- const SourceString("boolTypeCast"),
- "functionTypeCheck":
- const SourceString("functionTypeCast"),
- "intTypeCheck":
- const SourceString("intTypeCast"),
- "numberOrStringSuperNativeTypeCheck":
- const SourceString("numberOrStringSuperNativeTypeCast"),
- "numberOrStringSuperTypeCheck":
- const SourceString("numberOrStringSuperTypeCast"),
- "stringSuperNativeTypeCheck":
- const SourceString("stringSuperNativeTypeCast"),
- "stringSuperTypeCheck":
- const SourceString("stringSuperTypeCast"),
- "listTypeCheck":
- const SourceString("listTypeCast"),
- "listSuperNativeTypeCheck":
- const SourceString("listSuperNativeTypeCast"),
- "listSuperTypeCheck":
- const SourceString("listSuperTypeCast"),
- "callTypeCheck":
- const SourceString("callTypeCast"),
- "propertyTypeCheck":
- const SourceString("propertyTypeCast"),
- // TODO(johnniwinther): Add a malformedTypeCast which produces a TypeError
- // with another message.
- "malformedTypeCheck":
- const SourceString("malformedTypeCheck")
- };
-
if (node.isChecked) {
- DartType type = node.type.computeType(compiler);
+ DartType type = node.instructionType.computeType(compiler);
Element element = type.element;
world.registerIsCheck(type);
@@ -2454,22 +2406,22 @@
}
assert(node.isCheckedModeCheck || node.isCastTypeCheck);
- SourceString helper;
+ FunctionElement helperElement;
if (node.isBooleanConversionCheck) {
- helper = const SourceString('boolConversionCheck');
+ helperElement =
+ compiler.findHelper(const SourceString('boolConversionCheck'));
} else {
- helper = backend.getCheckedModeHelper(type);
- if (node.isCastTypeCheck) {
- helper = castNames[helper.stringValue];
- }
+ helperElement = backend.getCheckedModeHelper(type,
+ typeCast: node.isCastTypeCheck);
}
- FunctionElement helperElement = compiler.findHelper(helper);
world.registerStaticUse(helperElement);
List<js.Expression> arguments = <js.Expression>[];
use(node.checkedInput);
arguments.add(pop());
int parameterCount =
helperElement.computeSignature(compiler).parameterCount;
+ // TODO(johnniwinther): Refactor this to avoid using the parameter count
+ // to determine how the helper should be called.
if (parameterCount == 2) {
// 2 arguments implies that the method is either [propertyTypeCheck]
// or [propertyTypeCast].
@@ -2549,27 +2501,27 @@
HInstruction input = node.guarded;
DartType indexingBehavior =
backend.jsIndexingBehaviorInterface.computeType(compiler);
- if (node.isInteger(types)) {
+ if (node.isInteger()) {
// if (input is !int) bailout
checkInt(input, '!==');
js.Statement then = bailout(node, 'Not an integer');
pushStatement(new js.If.noElse(pop(), then), node);
- } else if (node.isNumber(types)) {
+ } else if (node.isNumber()) {
// if (input is !num) bailout
checkNum(input, '!==');
js.Statement then = bailout(node, 'Not a number');
pushStatement(new js.If.noElse(pop(), then), node);
- } else if (node.isBoolean(types)) {
+ } else if (node.isBoolean()) {
// if (input is !bool) bailout
checkBool(input, '!==');
js.Statement then = bailout(node, 'Not a boolean');
pushStatement(new js.If.noElse(pop(), then), node);
- } else if (node.isString(types)) {
+ } else if (node.isString()) {
// if (input is !string) bailout
checkString(input, '!==');
js.Statement then = bailout(node, 'Not a string');
pushStatement(new js.If.noElse(pop(), then), node);
- } else if (node.isExtendableArray(types)) {
+ } else if (node.isExtendableArray()) {
// if (input is !Object || input is !Array || input.isFixed) bailout
checkObject(input, '!==');
js.Expression objectTest = pop();
@@ -2580,7 +2532,7 @@
test = new js.Binary('||', test, pop());
js.Statement then = bailout(node, 'Not an extendable array');
pushStatement(new js.If.noElse(test, then), node);
- } else if (node.isMutableArray(types)) {
+ } else if (node.isMutableArray()) {
// if (input is !Object
// || ((input is !Array || input.isImmutable)
// && input is !JsIndexingBehavior)) bailout
@@ -2595,7 +2547,7 @@
js.Binary test = new js.Binary('||', objectTest, notIndexing);
js.Statement then = bailout(node, 'Not a mutable array');
pushStatement(new js.If.noElse(test, then), node);
- } else if (node.isReadableArray(types)) {
+ } else if (node.isReadableArray()) {
// if (input is !Object
// || (input is !Array && input is !JsIndexingBehavior)) bailout
checkObject(input, '!==');
@@ -2607,7 +2559,7 @@
js.Binary test = new js.Binary('||', objectTest, notIndexing);
js.Statement then = bailout(node, 'Not an array');
pushStatement(new js.If.noElse(test, then), node);
- } else if (node.isIndexablePrimitive(types)) {
+ } else if (node.isIndexablePrimitive()) {
// if (input is !String
// && (input is !Object
// || (input is !Array && input is !JsIndexingBehavior))) bailout
@@ -3001,15 +2953,13 @@
}
}
-String singleIdentityComparison(HInstruction left,
- HInstruction right,
- HTypeMap propagatedTypes) {
+String singleIdentityComparison(HInstruction left, HInstruction right) {
// Returns the single identity comparison (== or ===) or null if a more
// complex expression is required.
if ((left.isConstant() && left.isConstantSentinel()) ||
(right.isConstant() && right.isConstantSentinel())) return '===';
- HType leftType = propagatedTypes[left];
- HType rightType = propagatedTypes[right];
+ HType leftType = left.instructionType;
+ HType rightType = right.instructionType;
if (leftType.canBeNull() && rightType.canBeNull()) {
if (left.isConstantNull() || right.isConstantNull() ||
(leftType.isPrimitive() && leftType == rightType)) {
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/codegen_helpers.dart b/sdk/lib/_internal/compiler/implementation/ssa/codegen_helpers.dart
index 625eafb..fac0952 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/codegen_helpers.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/codegen_helpers.dart
@@ -15,7 +15,6 @@
* t2 = add(4, 3);
*/
class SsaInstructionMerger extends HBaseVisitor {
- HTypeMap types;
/**
* List of [HInstruction] that the instruction merger expects in
* order when visiting the inputs of an instruction.
@@ -34,7 +33,7 @@
generateAtUseSite.add(instruction);
}
- SsaInstructionMerger(this.types, this.generateAtUseSite);
+ SsaInstructionMerger(this.generateAtUseSite);
void visitGraph(HGraph graph) {
visitDominatorTree(graph);
@@ -100,7 +99,7 @@
void visitIdentity(HIdentity instruction) {
HInstruction left = instruction.left;
HInstruction right = instruction.right;
- if (singleIdentityComparison(left, right, types) != null) {
+ if (singleIdentityComparison(left, right) != null) {
super.visitIdentity(instruction);
}
// Do nothing.
@@ -215,7 +214,6 @@
* using these operators instead of nested ifs and boolean variables.
*/
class SsaConditionMerger extends HGraphVisitor {
- final HTypeMap types;
Set<HInstruction> generateAtUseSite;
Set<HInstruction> controlFlowOperators;
@@ -224,9 +222,7 @@
generateAtUseSite.add(instruction);
}
- SsaConditionMerger(this.types,
- this.generateAtUseSite,
- this.controlFlowOperators);
+ SsaConditionMerger(this.generateAtUseSite, this.controlFlowOperators);
void visitGraph(HGraph graph) {
visitPostDominatorTree(graph);
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/invoke_dynamic_specializers.dart b/sdk/lib/_internal/compiler/implementation/ssa/invoke_dynamic_specializers.dart
index 375f617..234e7ab 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/invoke_dynamic_specializers.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/invoke_dynamic_specializers.dart
@@ -15,19 +15,16 @@
HType computeDesiredTypeForInput(HInvokeDynamic instruction,
HInstruction input,
- HTypeMap types,
Compiler compiler) {
return HType.UNKNOWN;
}
HType computeTypeFromInputTypes(HInvokeDynamic instruction,
- HTypeMap types,
Compiler compiler) {
- return HType.UNKNOWN;
+ return instruction.instructionType;
}
- HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
- HTypeMap types) {
+ HInstruction tryConvertToBuiltin(HInvokeDynamic instruction) {
return null;
}
@@ -86,11 +83,10 @@
HType computeDesiredTypeForInput(HInvokeDynamic instruction,
HInstruction input,
- HTypeMap types,
Compiler compiler) {
HInstruction index = instruction.inputs[2];
if (input == instruction.inputs[1] &&
- (index.isTypeUnknown(types) || index.isNumber(types))) {
+ (index.isTypeUnknown() || index.isNumber())) {
return HType.MUTABLE_ARRAY;
}
// The index should be an int when the receiver is a string or array.
@@ -100,9 +96,8 @@
return HType.UNKNOWN;
}
- HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
- HTypeMap types) {
- if (instruction.inputs[1].isMutableArray(types)) {
+ HInstruction tryConvertToBuiltin(HInvokeDynamic instruction) {
+ if (instruction.inputs[1].isMutableArray()) {
return new HIndexAssign(instruction.inputs[1],
instruction.inputs[2],
instruction.inputs[3]);
@@ -116,11 +111,10 @@
HType computeDesiredTypeForInput(HInvokeDynamic instruction,
HInstruction input,
- HTypeMap types,
Compiler compiler) {
HInstruction index = instruction.inputs[2];
if (input == instruction.inputs[1] &&
- (index.isTypeUnknown(types) || index.isNumber(types))) {
+ (index.isTypeUnknown() || index.isNumber())) {
return HType.INDEXABLE_PRIMITIVE;
}
// The index should be an int when the receiver is a string or array.
@@ -130,9 +124,8 @@
return HType.UNKNOWN;
}
- HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
- HTypeMap types) {
- if (instruction.inputs[1].isIndexablePrimitive(types)) {
+ HInstruction tryConvertToBuiltin(HInvokeDynamic instruction) {
+ if (instruction.inputs[1].isIndexablePrimitive()) {
return new HIndex(instruction.inputs[1], instruction.inputs[2]);
}
return null;
@@ -148,10 +141,9 @@
HType computeDesiredTypeForInput(HInvokeDynamic instruction,
HInstruction input,
- HTypeMap types,
Compiler compiler) {
if (input == instruction.inputs[1]) {
- HType propagatedType = types[instruction];
+ HType propagatedType = instruction.instructionType;
if (propagatedType.isUnknown() || propagatedType.isNumber()) {
return HType.INTEGER;
}
@@ -160,18 +152,16 @@
}
HType computeTypeFromInputTypes(HInvokeDynamic instruction,
- HTypeMap types,
Compiler compiler) {
// All bitwise operations on primitive types either produce an
// integer or throw an error.
- if (instruction.inputs[1].isPrimitive(types)) return HType.INTEGER;
- return HType.UNKNOWN;
+ if (instruction.inputs[1].isPrimitive()) return HType.INTEGER;
+ return instruction.instructionType;
}
- HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
- HTypeMap types) {
+ HInstruction tryConvertToBuiltin(HInvokeDynamic instruction) {
HInstruction input = instruction.inputs[1];
- if (input.isNumber(types)) return new HBitNot(input);
+ if (input.isNumber()) return new HBitNot(input);
return null;
}
}
@@ -185,10 +175,9 @@
HType computeDesiredTypeForInput(HInvokeDynamic instruction,
HInstruction input,
- HTypeMap types,
Compiler compiler) {
if (input == instruction.inputs[1]) {
- HType propagatedType = types[instruction];
+ HType propagatedType = instruction.instructionType;
// If the outgoing type should be a number (integer, double or both) we
// want the outgoing type to be the input too.
// If we don't know the outgoing type we try to make it a number.
@@ -199,17 +188,15 @@
}
HType computeTypeFromInputTypes(HInvokeDynamic instruction,
- HTypeMap types,
Compiler compiler) {
- HType operandType = types[instruction.inputs[1]];
+ HType operandType = instruction.inputs[1].instructionType;
if (operandType.isNumber()) return operandType;
- return HType.UNKNOWN;
+ return instruction.instructionType;
}
- HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
- HTypeMap types) {
+ HInstruction tryConvertToBuiltin(HInvokeDynamic instruction) {
HInstruction input = instruction.inputs[1];
- if (input.isNumber(types)) return new HNegate(input);
+ if (input.isNumber()) return new HNegate(input);
return null;
}
}
@@ -218,25 +205,23 @@
const BinaryArithmeticSpecializer();
HType computeTypeFromInputTypes(HInvokeDynamic instruction,
- HTypeMap types,
Compiler compiler) {
HInstruction left = instruction.inputs[1];
HInstruction right = instruction.inputs[2];
- if (left.isInteger(types) && right.isInteger(types)) return HType.INTEGER;
- if (left.isNumber(types)) {
- if (left.isDouble(types) || right.isDouble(types)) return HType.DOUBLE;
+ if (left.isInteger() && right.isInteger()) return HType.INTEGER;
+ if (left.isNumber()) {
+ if (left.isDouble() || right.isDouble()) return HType.DOUBLE;
return HType.NUMBER;
}
- return HType.UNKNOWN;
+ return instruction.instructionType;
}
HType computeDesiredTypeForInput(HInvokeDynamic instruction,
HInstruction input,
- HTypeMap types,
Compiler compiler) {
if (input == instruction.inputs[0]) return HType.UNKNOWN;
- HType propagatedType = types[instruction];
+ HType propagatedType = instruction.instructionType;
// If the desired output type should be an integer we want to get two
// integers as arguments.
if (propagatedType.isInteger()) return HType.INTEGER;
@@ -255,18 +240,17 @@
// to the array case.
HInstruction left = instruction.inputs[1];
HInstruction right = instruction.inputs[2];
- if (input == right && left.isNumber(types)) return HType.NUMBER;
+ if (input == right && left.isNumber()) return HType.NUMBER;
return HType.UNKNOWN;
}
- bool isBuiltin(HInvokeDynamic instruction, HTypeMap types) {
- return instruction.inputs[1].isNumber(types)
- && instruction.inputs[2].isNumber(types);
+ bool isBuiltin(HInvokeDynamic instruction) {
+ return instruction.inputs[1].isNumber()
+ && instruction.inputs[2].isNumber();
}
- HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
- HTypeMap types) {
- if (isBuiltin(instruction, types)) {
+ HInstruction tryConvertToBuiltin(HInvokeDynamic instruction) {
+ if (isBuiltin(instruction)) {
HInstruction builtin =
newBuiltinVariant(instruction.inputs[1], instruction.inputs[2]);
if (builtin != null) return builtin;
@@ -303,22 +287,20 @@
}
HType computeTypeFromInputTypes(HInstruction instruction,
- HTypeMap types,
Compiler compiler) {
HInstruction left = instruction.inputs[1];
- if (left.isNumber(types)) return HType.DOUBLE;
- return HType.UNKNOWN;
+ if (left.isNumber()) return HType.DOUBLE;
+ return instruction.instructionType;
}
HType computeDesiredTypeForInput(HInstruction instruction,
HInstruction input,
- HTypeMap types,
Compiler compiler) {
if (input == instruction.inputs[0]) return HType.UNKNOWN;
// A division can never return an integer. So don't ask for integer inputs.
- if (instruction.isInteger(types)) return HType.UNKNOWN;
+ if (instruction.isInteger()) return HType.UNKNOWN;
return super.computeDesiredTypeForInput(
- instruction, input, types, compiler);
+ instruction, input, compiler);
}
HInstruction newBuiltinVariant(HInstruction left, HInstruction right) {
@@ -380,21 +362,19 @@
const BinaryBitOpSpecializer();
HType computeTypeFromInputTypes(HInvokeDynamic instruction,
- HTypeMap types,
Compiler compiler) {
// All bitwise operations on primitive types either produce an
// integer or throw an error.
HInstruction left = instruction.inputs[1];
- if (left.isPrimitive(types)) return HType.INTEGER;
- return HType.UNKNOWN;
+ if (left.isPrimitive()) return HType.INTEGER;
+ return instruction.instructionType;
}
HType computeDesiredTypeForInput(HInvokeDynamic instruction,
HInstruction input,
- HTypeMap types,
Compiler compiler) {
if (input == instruction.inputs[0]) return HType.UNKNOWN;
- HType propagatedType = types[instruction];
+ HType propagatedType = instruction.instructionType;
// If the outgoing type should be a number we can get that only if both
// inputs are integers. If we don't know the outgoing type we try to make
// it an integer.
@@ -412,11 +392,10 @@
return constantSystem.shiftLeft;
}
- HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
- HTypeMap types) {
+ HInstruction tryConvertToBuiltin(HInvokeDynamic instruction) {
HInstruction left = instruction.inputs[1];
HInstruction right = instruction.inputs[2];
- if (!left.isNumber(types) || !right.isConstantInteger()) return null;
+ if (!left.isNumber() || !right.isConstantInteger()) return null;
HConstant rightConstant = right;
IntConstant intConstant = rightConstant.constant;
int count = intConstant.value;
@@ -484,35 +463,34 @@
const RelationalSpecializer();
HType computeTypeFromInputTypes(HInvokeDynamic instruction,
- HTypeMap types,
Compiler compiler) {
- if (types[instruction.inputs[1]].isPrimitiveOrNull()) return HType.BOOLEAN;
- return HType.UNKNOWN;
+ if (instruction.inputs[1].instructionType.isPrimitiveOrNull()) {
+ return HType.BOOLEAN;
+ }
+ return instruction.instructionType;
}
HType computeDesiredTypeForInput(HInvokeDynamic instruction,
HInstruction input,
- HTypeMap types,
Compiler compiler) {
if (input == instruction.inputs[0]) return HType.UNKNOWN;
- HType propagatedType = types[instruction];
+ HType propagatedType = instruction.instructionType;
// For all relational operations except HIdentity, we expect to get numbers
// only. With numbers the outgoing type is a boolean. If something else
// is desired, then numbers are incorrect, though.
if (propagatedType.isUnknown() || propagatedType.isBoolean()) {
HInstruction left = instruction.inputs[1];
- if (left.isTypeUnknown(types) || left.isNumber(types)) {
+ if (left.isTypeUnknown() || left.isNumber()) {
return HType.NUMBER;
}
}
return HType.UNKNOWN;
}
- HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
- HTypeMap types) {
+ HInstruction tryConvertToBuiltin(HInvokeDynamic instruction) {
HInstruction left = instruction.inputs[1];
HInstruction right = instruction.inputs[2];
- if (left.isNumber(types) && right.isNumber(types)) {
+ if (left.isNumber() && right.isNumber()) {
return newBuiltinVariant(left, right);
}
return null;
@@ -526,37 +504,35 @@
HType computeDesiredTypeForInput(HInvokeDynamic instruction,
HInstruction input,
- HTypeMap types,
Compiler compiler) {
HInstruction left = instruction.inputs[1];
HInstruction right = instruction.inputs[2];
- HType propagatedType = types[instruction];
- if (input == left && types[right].isUseful()) {
+ HType propagatedType = instruction.instructionType;
+ if (input == left && right.instructionType.isUseful()) {
// All our useful types have 'identical' semantics. But we don't want to
// speculatively test for all possible types. Therefore we try to match
// the two types. That is, if we see x == 3, then we speculatively test
// if x is a number and bailout if it isn't.
// If right is a number we don't need more than a number (no need to match
// the exact type of right).
- if (right.isNumber(types)) return HType.NUMBER;
- return types[right];
+ if (right.isNumber()) return HType.NUMBER;
+ return right.instructionType;
}
// String equality testing is much more common than array equality testing.
- if (input == left && left.isIndexablePrimitive(types)) {
+ if (input == left && left.isIndexablePrimitive()) {
return HType.READABLE_ARRAY;
}
// String equality testing is much more common than array equality testing.
- if (input == right && right.isIndexablePrimitive(types)) {
+ if (input == right && right.isIndexablePrimitive()) {
return HType.STRING;
}
return HType.UNKNOWN;
}
- HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
- HTypeMap types) {
+ HInstruction tryConvertToBuiltin(HInvokeDynamic instruction) {
HInstruction left = instruction.inputs[1];
HInstruction right = instruction.inputs[2];
- if (types[left].isPrimitiveOrNull() || right.isConstantNull()) {
+ if (left.instructionType.isPrimitiveOrNull() || right.isConstantNull()) {
return newBuiltinVariant(left, right);
}
return null;
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/nodes.dart b/sdk/lib/_internal/compiler/implementation/ssa/nodes.dart
index 4d83538..b6714b8 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/nodes.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/nodes.dart
@@ -254,7 +254,7 @@
visitInstruction(HInstruction instruction) {}
visitBinaryArithmetic(HBinaryArithmetic node) => visitInvokeBinary(node);
- visitBinaryBitOp(HBinaryBitOp node) => visitBinaryArithmetic(node);
+ visitBinaryBitOp(HBinaryBitOp node) => visitInvokeBinary(node);
visitInvoke(HInvoke node) => visitInstruction(node);
visitInvokeBinary(HInvokeBinary node) => visitInstruction(node);
visitInvokeDynamic(HInvokeDynamic node) => visitInvoke(node);
@@ -867,84 +867,44 @@
bool isControlFlow() => false;
// All isFunctions work on the propagated types.
- bool isArray(HTypeMap types) => types[this].isArray();
- bool isReadableArray(HTypeMap types) => types[this].isReadableArray();
- bool isMutableArray(HTypeMap types) => types[this].isMutableArray();
- bool isExtendableArray(HTypeMap types) => types[this].isExtendableArray();
- bool isFixedArray(HTypeMap types) => types[this].isFixedArray();
- bool isBoolean(HTypeMap types) => types[this].isBoolean();
- bool isInteger(HTypeMap types) => types[this].isInteger();
- bool isDouble(HTypeMap types) => types[this].isDouble();
- bool isNumber(HTypeMap types) => types[this].isNumber();
- bool isNumberOrNull(HTypeMap types) => types[this].isNumberOrNull();
- bool isString(HTypeMap types) => types[this].isString();
- bool isTypeUnknown(HTypeMap types) => types[this].isUnknown();
- bool isIndexablePrimitive(HTypeMap types)
- => types[this].isIndexablePrimitive();
- bool isPrimitive(HTypeMap types) => types[this].isPrimitive();
- bool canBePrimitive(HTypeMap types) => types[this].canBePrimitive();
- bool canBeNull(HTypeMap types) => types[this].canBeNull();
+ bool isArray() => instructionType.isArray();
+ bool isReadableArray() => instructionType.isReadableArray();
+ bool isMutableArray() => instructionType.isMutableArray();
+ bool isExtendableArray() => instructionType.isExtendableArray();
+ bool isFixedArray() => instructionType.isFixedArray();
+ bool isBoolean() => instructionType.isBoolean();
+ bool isInteger() => instructionType.isInteger();
+ bool isDouble() => instructionType.isDouble();
+ bool isNumber() => instructionType.isNumber();
+ bool isNumberOrNull() => instructionType.isNumberOrNull();
+ bool isString() => instructionType.isString();
+ bool isTypeUnknown() => instructionType.isUnknown();
+ bool isIndexablePrimitive() => instructionType.isIndexablePrimitive();
+ bool isPrimitive() => instructionType.isPrimitive();
+ bool canBePrimitive() => instructionType.canBePrimitive();
+ bool canBeNull() => instructionType.canBeNull();
/**
- * This is the type the instruction is guaranteed to have. It does not
- * take any propagation into account.
+ * Type of the unstruction.
*/
- HType guaranteedType = HType.UNKNOWN;
- bool hasGuaranteedType() => !guaranteedType.isUnknown();
-
- /**
- * Some instructions have a good idea of their return type, but cannot
- * guarantee the type. The computed does not need to be more specialized
- * than the provided type for [this].
- *
- * Examples: the likely type of [:x == y:] is a boolean. In most cases this
- * cannot be guaranteed, but when merging types we still want to use this
- * information.
- *
- * Similarily the [HAdd] instruction is likely a number. Note that, even if
- * the incoming type is already set to integer, the likely type might still
- * just return the number type.
- */
- HType computeLikelyType(HTypeMap types, Compiler compiler) => types[this];
-
- /**
- * Compute the type of the instruction by propagating the input types through
- * the instruction.
- *
- * By default just copy the guaranteed type.
- */
- HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
- return guaranteedType;
- }
-
- /**
- * Compute the desired type for the the given [input]. Aside from using
- * other inputs to compute the desired type one should also use
- * the given [types] which, during the invocation of this method,
- * represents the desired type of [this].
- */
- HType computeDesiredTypeForInput(HInstruction input,
- HTypeMap types,
- Compiler compiler) {
- return HType.UNKNOWN;
- }
+ HType instructionType = HType.UNKNOWN;
bool isInBasicBlock() => block != null;
String inputsToString() {
void addAsCommaSeparated(StringBuffer buffer, List<HInstruction> list) {
for (int i = 0; i < list.length; i++) {
- if (i != 0) buffer.add(', ');
- buffer.add("@${list[i].id}");
+ if (i != 0) buffer.write(', ');
+ buffer.write("@${list[i].id}");
}
}
StringBuffer buffer = new StringBuffer();
- buffer.add('(');
+ buffer.write('(');
addAsCommaSeparated(buffer, inputs);
- buffer.add(') - used at [');
+ buffer.write(') - used at [');
addAsCommaSeparated(buffer, usedBy);
- buffer.add(']');
+ buffer.write(']');
return buffer.toString();
}
@@ -1151,12 +1111,12 @@
if (identical(type.element, compiler.objectClass)) return this;
// If the original can't be null, type conversion also can't produce null.
- bool canBeNull = this.guaranteedType.canBeNull();
+ bool canBeNull = instructionType.canBeNull();
HType convertedType = new HType.subtype(type, compiler);
// No need to convert if we know the instruction has
// [convertedType] as a bound.
- if (this.guaranteedType == convertedType) {
+ if (instructionType == convertedType) {
return this;
}
@@ -1176,10 +1136,9 @@
HBoolify(HInstruction value) : super(<HInstruction>[value]) {
assert(!hasSideEffects());
setUseGvn();
+ instructionType = HType.BOOLEAN;
}
- HType get guaranteedType => HType.BOOLEAN;
-
accept(HVisitor visitor) => visitor.visitBoolify(this);
int typeCode() => HInstruction.BOOLIFY_TYPECODE;
bool typeEquals(other) => other is HBoolify;
@@ -1215,6 +1174,10 @@
setUseGvn();
}
+ void disable() {
+ isEnabled = false;
+ }
+
bool isControlFlow() => isEnabled;
bool isJsStatement() => isEnabled;
@@ -1225,7 +1188,7 @@
}
class HTypeGuard extends HCheck {
- final HType guardedType;
+ HType guardedType;
bool isEnabled = false;
HTypeGuard(this.guardedType, HInstruction guarded, HInstruction bailoutTarget)
@@ -1236,11 +1199,15 @@
HBailoutTarget get bailoutTarget => inputs[1];
int get state => bailoutTarget.state;
- HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
- return isEnabled ? guardedType : types[guarded];
+ void enable() {
+ isEnabled = true;
+ instructionType = guardedType;
}
- HType get guaranteedType => isEnabled ? guardedType : HType.UNKNOWN;
+ void disable() {
+ isEnabled = false;
+ instructionType = guarded.instructionType;
+ }
bool isControlFlow() => true;
bool isJsStatement() => isEnabled;
@@ -1264,14 +1231,14 @@
*/
int staticChecks = FULL_CHECK;
- HBoundsCheck(length, index) : super(<HInstruction>[length, index]);
+ HBoundsCheck(length, index) : super(<HInstruction>[length, index]) {
+ instructionType = HType.INTEGER;
+ }
HInstruction get length => inputs[1];
HInstruction get index => inputs[0];
bool isControlFlow() => true;
- HType get guaranteedType => HType.INTEGER;
-
accept(HVisitor visitor) => visitor.visitBoundsCheck(this);
int typeCode() => HInstruction.BOUNDS_CHECK_TYPECODE;
bool typeEquals(other) => other is HBoundsCheck;
@@ -1281,23 +1248,13 @@
class HIntegerCheck extends HCheck {
bool alwaysFalse = false;
- HIntegerCheck(value) : super(<HInstruction>[value]);
+ HIntegerCheck(value) : super(<HInstruction>[value]) {
+ instructionType = HType.INTEGER;
+ }
HInstruction get value => inputs[0];
bool isControlFlow() => true;
- HType get guaranteedType => HType.INTEGER;
-
- HType computeDesiredTypeForInput(HInstruction input,
- HTypeMap types,
- Compiler compiler) {
- // If the desired type of the input is already a number, we want
- // to specialize it to an integer.
- return input.isNumber(types)
- ? HType.INTEGER
- : super.computeDesiredTypeForInput(input, types, compiler);
- }
-
accept(HVisitor visitor) => visitor.visitIntegerCheck(this);
int typeCode() => HInstruction.INTEGER_CHECK_TYPECODE;
bool typeEquals(other) => other is HIntegerCheck;
@@ -1358,19 +1315,7 @@
int typeCode() => HInstruction.INVOKE_DYNAMIC_TYPECODE;
bool typeEquals(other) => other is HInvokeDynamic;
bool dataEquals(HInvokeDynamic other) {
- return selector == other.selector
- && element == other.element;
- }
-
- HType computeDesiredTypeForInput(HInstruction input,
- HTypeMap types,
- Compiler compiler) {
- return specializer.computeDesiredTypeForInput(this, input, types, compiler);
- }
-
- HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
- HType type = specializer.computeTypeFromInputTypes(this, types, compiler);
- return type.isUnknown() ? guaranteedType : type;
+ return selector == other.selector && element == other.element;
}
}
@@ -1391,11 +1336,11 @@
String toString() => 'invoke dynamic method: $selector';
accept(HVisitor visitor) => visitor.visitInvokeDynamicMethod(this);
- bool isIndexOperatorOnIndexablePrimitive(HTypeMap types) {
+ bool isIndexOperatorOnIndexablePrimitive() {
return isInterceptorCall
&& selector.kind == SelectorKind.INDEX
&& selector.name == const SourceString('[]')
- && inputs[1].isIndexablePrimitive(types);
+ && inputs[1].isIndexablePrimitive();
}
}
@@ -1442,7 +1387,7 @@
class HInvokeStatic extends HInvoke {
/** The first input must be the target. */
HInvokeStatic(inputs, HType type) : super(inputs) {
- guaranteedType = type;
+ instructionType = type;
}
toString() => 'invoke static: ${element.name}';
@@ -1543,16 +1488,16 @@
class HForeign extends HInstruction {
final DartString code;
- final HType type;
final bool isStatement;
HForeign(this.code,
- this.type,
+ HType type,
List<HInstruction> inputs,
{this.isStatement: false})
: super(inputs) {
setAllSideEffects();
setDependsOnSomething();
+ instructionType = type;
}
HForeign.statement(code, List<HInstruction> inputs)
@@ -1560,8 +1505,6 @@
accept(HVisitor visitor) => visitor.visitForeign(this);
- HType get guaranteedType => type;
-
bool isJsStatement() => isStatement;
bool canThrow() => true;
}
@@ -1588,13 +1531,6 @@
abstract class HBinaryArithmetic extends HInvokeBinary {
HBinaryArithmetic(HInstruction left, HInstruction right) : super(left, right);
-
- HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
- if (left.isInteger(types) && right.isInteger(types)) return HType.INTEGER;
- if (left.isDouble(types)) return HType.DOUBLE;
- return HType.NUMBER;
- }
-
BinaryOperation operation(ConstantSystem constantSystem);
}
@@ -1610,11 +1546,11 @@
}
class HDivide extends HBinaryArithmetic {
- HDivide(HInstruction left, HInstruction right) : super(left, right);
+ HDivide(HInstruction left, HInstruction right) : super(left, right) {
+ instructionType = HType.DOUBLE;
+ }
accept(HVisitor visitor) => visitor.visitDivide(this);
- HType get guaranteedType => HType.DOUBLE;
-
BinaryOperation operation(ConstantSystem constantSystem)
=> constantSystem.divide;
int typeCode() => HInstruction.DIVIDE_TYPECODE;
@@ -1667,13 +1603,9 @@
String toString() => "HSwitch cases = $inputs";
}
-// TODO(floitsch): Should HBinaryArithmetic really be the super class of
-// HBinaryBitOp?
-abstract class HBinaryBitOp extends HBinaryArithmetic {
- HBinaryBitOp(HInstruction left, HInstruction right) : super(left, right);
- HType get guaranteedType => HType.INTEGER;
- HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
- return guaranteedType;
+abstract class HBinaryBitOp extends HInvokeBinary {
+ HBinaryBitOp(HInstruction left, HInstruction right) : super(left, right) {
+ instructionType = HType.INTEGER;
}
}
@@ -1736,10 +1668,6 @@
HNegate(HInstruction input) : super(input);
accept(HVisitor visitor) => visitor.visitNegate(this);
- HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
- return types[operand];
- }
-
UnaryOperation operation(ConstantSystem constantSystem)
=> constantSystem.negate;
int typeCode() => HInstruction.NEGATE_TYPECODE;
@@ -1748,10 +1676,11 @@
}
class HBitNot extends HInvokeUnary {
- HBitNot(HInstruction input) : super(input);
+ HBitNot(HInstruction input) : super(input) {
+ instructionType = HType.INTEGER;
+ }
accept(HVisitor visitor) => visitor.visitBitNot(this);
- HType get guaranteedType => HType.INTEGER;
UnaryOperation operation(ConstantSystem constantSystem)
=> constantSystem.bitNot;
int typeCode() => HInstruction.BIT_NOT_TYPECODE;
@@ -1868,15 +1797,14 @@
class HConstant extends HInstruction {
final Constant constant;
- final HType constantType;
- HConstant.internal(this.constant, HType this.constantType)
- : super(<HInstruction>[]);
+ HConstant.internal(this.constant, HType constantType)
+ : super(<HInstruction>[]) {
+ instructionType = constantType;
+ }
toString() => 'literal: $constant';
accept(HVisitor visitor) => visitor.visitConstant(this);
- HType get guaranteedType => constantType;
-
bool isConstant() => true;
bool isConstantBoolean() => constant.isBool();
bool isConstantNull() => constant.isNull();
@@ -1896,15 +1824,7 @@
class HNot extends HInstruction {
HNot(HInstruction value) : super(<HInstruction>[value]) {
setUseGvn();
- }
-
- HType get guaranteedType => HType.BOOLEAN;
-
- // 'Not' only works on booleans. That's what we want as input.
- HType computeDesiredTypeForInput(HInstruction input,
- HTypeMap types,
- Compiler compiler) {
- return HType.BOOLEAN;
+ instructionType = HType.BOOLEAN;
}
accept(HVisitor visitor) => visitor.visitNot(this);
@@ -1936,7 +1856,7 @@
class HThis extends HParameterValue {
HThis(Element element, [HType type = HType.UNKNOWN]) : super(element) {
- guaranteedType = type;
+ instructionType = type;
}
toString() => 'this';
accept(HVisitor visitor) => visitor.visitThis(this);
@@ -1968,58 +1888,6 @@
input.usedBy.add(this);
}
- // Compute the (shared) type of the inputs if any. If all inputs
- // have the same known type return it. If any two inputs have
- // different known types, we'll return a conflict -- otherwise we'll
- // simply return an unknown type.
- HType computeInputsType(bool ignoreUnknowns,
- HTypeMap types,
- Compiler compiler) {
- HType candidateType = HType.CONFLICTING;
- for (int i = 0, length = inputs.length; i < length; i++) {
- HType inputType = types[inputs[i]];
- if (ignoreUnknowns && inputType.isUnknown()) continue;
- // Phis need to combine the incoming types using the union operation.
- // For example, if one incoming edge has type integer and the other has
- // type double, then the phi is either an integer or double and thus has
- // type number.
- candidateType = candidateType.union(inputType, compiler);
- if (candidateType.isUnknown()) return HType.UNKNOWN;
- }
- return candidateType;
- }
-
- HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
- HType inputsType = computeInputsType(false, types, compiler);
- if (inputsType.isConflicting()) return HType.UNKNOWN;
- return inputsType;
- }
-
- HType computeDesiredTypeForInput(HInstruction input,
- HTypeMap types,
- Compiler compiler) {
- HType propagatedType = types[this];
- // Best case scenario for a phi is, when all inputs have the same type. If
- // there is no desired outgoing type we therefore try to unify the input
- // types (which is basically the [likelyType]).
- if (propagatedType.isUnknown()) return computeLikelyType(types, compiler);
- // When the desired outgoing type is conflicting we don't need to give any
- // requirements on the inputs.
- if (propagatedType.isConflicting()) return HType.UNKNOWN;
- // Otherwise the input type must match the desired outgoing type.
- return propagatedType;
- }
-
- HType computeLikelyType(HTypeMap types, Compiler compiler) {
- HType agreedType = computeInputsType(true, types, compiler);
- if (agreedType.isConflicting()) return HType.UNKNOWN;
- // Don't be too restrictive. If the agreed type is integer or double just
- // say that the likely type is number. If more is expected the type will be
- // propagated back.
- if (agreedType.isNumber()) return HType.NUMBER;
- return agreedType;
- }
-
bool isLogicalOperator() => logicalOperatorType != IS_NOT_LOGICAL_OPERATOR;
String logicalOperator() {
@@ -2035,8 +1903,9 @@
abstract class HRelational extends HInvokeBinary {
bool usesBoolifiedInterceptor = false;
- HRelational(HInstruction left, HInstruction right) : super(left, right);
- HType get guaranteedType => HType.BOOLEAN;
+ HRelational(HInstruction left, HInstruction right) : super(left, right) {
+ instructionType = HType.BOOLEAN;
+ }
}
class HIdentity extends HRelational {
@@ -2139,24 +2008,6 @@
accept(HVisitor visitor) => visitor.visitInterceptor(this);
HInstruction get receiver => inputs[0];
- HType computeDesiredTypeForInput(HInstruction input,
- HTypeMap types,
- Compiler compiler) {
- if (interceptedClasses.length != 1) return HType.UNKNOWN;
- // If the only class being intercepted is of type number, we
- // make this interceptor call say it wants that class as input.
- Element interceptor = interceptedClasses.toList()[0];
- JavaScriptBackend backend = compiler.backend;
- if (interceptor == backend.jsNumberClass) {
- return HType.NUMBER;
- } else if (interceptor == backend.jsIntClass) {
- return HType.INTEGER;
- } else if (interceptor == backend.jsDoubleClass) {
- return HType.DOUBLE;
- }
- return HType.UNKNOWN;
- }
-
int typeCode() => HInstruction.INTERCEPTOR_TYPECODE;
bool typeEquals(other) => other is HInterceptor;
bool dataEquals(HInterceptor other) {
@@ -2182,7 +2033,7 @@
this.interceptedClasses)
: super(selector, null, inputs, true) {
assert(inputs[0] is HConstant);
- assert(inputs[0].guaranteedType == HType.NULL);
+ assert(inputs[0].instructionType == HType.NULL);
}
String toString() => 'one shot interceptor on $selector';
@@ -2225,11 +2076,11 @@
}
class HLiteralList extends HInstruction {
- HLiteralList(inputs) : super(inputs);
+ HLiteralList(inputs) : super(inputs) {
+ instructionType = HType.EXTENDABLE_ARRAY;
+ }
toString() => 'literal list';
accept(HVisitor visitor) => visitor.visitLiteralList(this);
-
- HType get guaranteedType => HType.EXTENDABLE_ARRAY;
}
/**
@@ -2282,6 +2133,7 @@
HIs(this.typeExpression, List<HInstruction> inputs, {this.nullOk: false})
: super(inputs) {
setUseGvn();
+ instructionType = HType.BOOLEAN;
}
HInstruction get expression => inputs[0];
@@ -2289,8 +2141,6 @@
bool hasArgumentsCheck() => inputs.length > 1;
- HType get guaranteedType => HType.BOOLEAN;
-
accept(HVisitor visitor) => visitor.visitIs(this);
toString() => "$expression is $typeExpression";
@@ -2304,7 +2154,6 @@
}
class HTypeConversion extends HCheck {
- final HType type;
final int kind;
static const int NO_CHECK = 0;
@@ -2313,10 +2162,11 @@
static const int CAST_TYPE_CHECK = 3;
static const int BOOLEAN_CONVERSION_CHECK = 4;
- HTypeConversion(this.type, HInstruction input, [this.kind = NO_CHECK])
+ HTypeConversion(HType type, HInstruction input, [this.kind = NO_CHECK])
: super(<HInstruction>[input]) {
assert(type != null);
sourceElement = input.sourceElement;
+ instructionType = type;
}
HTypeConversion.checkedModeCheck(HType type, HInstruction input)
: this(type, input, CHECKED_MODE_CHECK);
@@ -2334,8 +2184,6 @@
bool get isCastTypeCheck => kind == CAST_TYPE_CHECK;
bool get isBooleanConversionCheck => kind == BOOLEAN_CONVERSION_CHECK;
- HType get guaranteedType => type;
-
accept(HVisitor visitor) => visitor.visitTypeConversion(this);
bool isJsStatement() => kind == ARGUMENT_TYPE_CHECK;
@@ -2345,18 +2193,17 @@
int typeCode() => HInstruction.TYPE_CONVERSION_TYPECODE;
bool typeEquals(HInstruction other) => other is HTypeConversion;
bool dataEquals(HTypeConversion other) {
- return type == other.type && kind == other.kind;
+ return instructionType == other.instructionType && kind == other.kind;
}
}
class HRangeConversion extends HCheck {
HRangeConversion(HInstruction input) : super(<HInstruction>[input]) {
sourceElement = input.sourceElement;
+ // We currently only do range analysis for integers.
+ instructionType = HType.INTEGER;
}
accept(HVisitor visitor) => visitor.visitRangeConversion(this);
-
- // We currently only do range analysis for integers.
- HType get guaranteedType => HType.INTEGER;
}
class HStringConcat extends HInstruction {
@@ -2365,8 +2212,8 @@
: super(<HInstruction>[left, right]) {
setAllSideEffects();
setDependsOnSomething();
+ instructionType = HType.STRING;
}
- HType get guaranteedType => HType.STRING;
HInstruction get left => inputs[0];
HInstruction get right => inputs[1];
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart b/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
index 1b2ae05..ced53a5 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
@@ -32,37 +32,36 @@
void optimize(CodegenWorkItem work, HGraph graph, bool speculative) {
ConstantSystem constantSystem = compiler.backend.constantSystem;
JavaScriptItemCompilationContext context = work.compilationContext;
- HTypeMap types = context.types;
measure(() {
List<OptimizationPhase> phases = <OptimizationPhase>[
// Run trivial constant folding first to optimize
// some patterns useful for type conversion.
- new SsaConstantFolder(constantSystem, backend, work, types),
+ new SsaConstantFolder(constantSystem, backend, work),
new SsaTypeConversionInserter(compiler),
- new SsaTypePropagator(compiler, types),
- new SsaConstantFolder(constantSystem, backend, work, types),
+ new SsaNonSpeculativeTypePropagator(compiler),
+ new SsaConstantFolder(constantSystem, backend, work),
// The constant folder affects the types of instructions, so
// we run the type propagator again. Note that this would
// not be necessary if types were directly stored on
// instructions.
- new SsaTypePropagator(compiler, types),
- new SsaCheckInserter(backend, work, types, context.boundsChecked),
+ new SsaNonSpeculativeTypePropagator(compiler),
+ new SsaCheckInserter(backend, work, context.boundsChecked),
new SsaRedundantPhiEliminator(),
new SsaDeadPhiEliminator(),
- new SsaConstantFolder(constantSystem, backend, work, types),
- new SsaTypePropagator(compiler, types),
+ new SsaConstantFolder(constantSystem, backend, work),
+ new SsaNonSpeculativeTypePropagator(compiler),
new SsaReceiverSpecialization(compiler),
- new SsaGlobalValueNumberer(compiler, types),
+ new SsaGlobalValueNumberer(compiler),
new SsaCodeMotion(),
- new SsaValueRangeAnalyzer(constantSystem, types, work),
+ new SsaValueRangeAnalyzer(constantSystem, work),
// Previous optimizations may have generated new
// opportunities for constant folding.
- new SsaConstantFolder(constantSystem, backend, work, types),
- new SsaSimplifyInterceptors(constantSystem, types),
- new SsaDeadCodeEliminator(types)];
+ new SsaConstantFolder(constantSystem, backend, work),
+ new SsaSimplifyInterceptors(constantSystem),
+ new SsaDeadCodeEliminator()];
runPhases(graph, phases);
if (!speculative) {
- runPhase(graph, new SsaConstructionFieldTypes(backend, work, types));
+ runPhase(graph, new SsaConstructionFieldTypes(backend, work));
}
});
}
@@ -73,24 +72,15 @@
return false;
}
JavaScriptItemCompilationContext context = work.compilationContext;
- HTypeMap types = context.types;
return measure(() {
// Run the phases that will generate type guards.
List<OptimizationPhase> phases = <OptimizationPhase>[
- new SsaSpeculativeTypePropagator(compiler, types),
- new SsaTypeGuardInserter(compiler, work, types),
+ new SsaTypeGuardInserter(compiler, work),
new SsaEnvironmentBuilder(compiler),
- // Change the propagated types back to what they were before we
- // speculatively propagated, so that we can generate the bailout
- // version.
- // Note that we do this even if there were no guards inserted. If a
- // guard is not beneficial enough we don't emit one, but there might
- // still be speculative types on the instructions.
- new SsaTypePropagator(compiler, types),
// Then run the [SsaCheckInserter] because the type propagator also
// propagated types non-speculatively. For example, it might have
// propagated the type array for a call to the List constructor.
- new SsaCheckInserter(backend, work, types, context.boundsChecked)];
+ new SsaCheckInserter(backend, work, context.boundsChecked)];
runPhases(graph, phases);
return !work.guards.isEmpty;
});
@@ -98,14 +88,13 @@
void prepareForSpeculativeOptimizations(CodegenWorkItem work, HGraph graph) {
JavaScriptItemCompilationContext context = work.compilationContext;
- HTypeMap types = context.types;
measure(() {
// In order to generate correct code for the bailout version, we did not
// propagate types from the instruction to the type guard. We do it
// now to be able to optimize further.
work.guards.forEach((HTypeGuard guard) {
- guard.bailoutTarget.isEnabled = false;
- guard.isEnabled = true;
+ guard.bailoutTarget.disable();
+ guard.enable();
});
// We also need to insert range and integer checks for the type
// guards. Now that they claim to have a certain type, some
@@ -114,8 +103,8 @@
// Also run the type propagator, to please the codegen in case
// no other optimization is run.
runPhases(graph, <OptimizationPhase>[
- new SsaCheckInserter(backend, work, types, context.boundsChecked),
- new SsaTypePropagator(compiler, types)]);
+ new SsaCheckInserter(backend, work, context.boundsChecked),
+ new SsaNonSpeculativeTypePropagator(compiler)]);
});
}
}
@@ -129,11 +118,10 @@
final JavaScriptBackend backend;
final CodegenWorkItem work;
final ConstantSystem constantSystem;
- final HTypeMap types;
HGraph graph;
Compiler get compiler => backend.compiler;
- SsaConstantFolder(this.constantSystem, this.backend, this.work, this.types);
+ SsaConstantFolder(this.constantSystem, this.backend, this.work);
void visitGraph(HGraph visitee) {
graph = visitee;
@@ -150,10 +138,8 @@
// If we can replace [instruction] with [replacement], then
// [replacement]'s type can be narrowed.
- types[replacement] = types[replacement].intersection(
- types[instruction], compiler);
- replacement.guaranteedType = replacement.guaranteedType.intersection(
- instruction.guaranteedType, compiler);
+ replacement.instructionType = replacement.instructionType.intersection(
+ instruction.instructionType, compiler);
// If the replacement instruction does not know its
// source element, use the source element of the
@@ -187,7 +173,7 @@
List<HInstruction> inputs = node.inputs;
assert(inputs.length == 1);
HInstruction input = inputs[0];
- HType type = types[input];
+ HType type = input.instructionType;
if (type.isBoolean()) return input;
// All values !== true are boolified to false.
if (!type.isBooleanOrNull() && !type.isUnknown()) {
@@ -225,9 +211,9 @@
return null;
}
- HInstruction optimizeLengthInterceptedGetter(HInvokeDynamic node) {
+ HInstruction tryOptimizeLengthInterceptedGetter(HInvokeDynamic node) {
HInstruction actualReceiver = node.inputs[1];
- if (actualReceiver.isIndexablePrimitive(types)) {
+ if (actualReceiver.isIndexablePrimitive()) {
if (actualReceiver.isConstantString()) {
HConstant constantInput = actualReceiver;
StringConstant constant = constantInput.constant;
@@ -239,24 +225,23 @@
}
Element element;
bool isAssignable;
- if (actualReceiver.isString(types)) {
+ if (actualReceiver.isString()) {
element = backend.jsStringLength;
isAssignable = false;
} else {
element = backend.jsArrayLength;
- isAssignable = !actualReceiver.isFixedArray(types);
+ isAssignable = !actualReceiver.isFixedArray();
}
HFieldGet result = new HFieldGet(
element, actualReceiver, isAssignable: isAssignable);
- result.guaranteedType = HType.INTEGER;
- types[result] = HType.INTEGER;
+ result.instructionType = HType.INTEGER;
return result;
} else if (actualReceiver.isConstantMap()) {
HConstant constantInput = actualReceiver;
MapConstant constant = constantInput.constant;
return graph.addConstantInt(constant.length, constantSystem);
}
- return node;
+ return null;
}
HInstruction handleInterceptorCall(HInvokeDynamic node) {
@@ -271,38 +256,67 @@
// Try converting the instruction to a builtin instruction.
HInstruction instruction =
- node.specializer.tryConvertToBuiltin(node, types);
+ node.specializer.tryConvertToBuiltin(node);
if (instruction != null) return instruction;
Selector selector = node.selector;
var interceptor = node.inputs[0];
+ HInstruction input = node.inputs[1];
- // If the intercepted call is through a constant interceptor, we
- // know which element to call.
- if (node is !HOneShotInterceptor
- && interceptor.isConstant()
- && selector.isCall()) {
- DartType type = types[interceptor].computeType(compiler);
- ClassElement cls = type.element;
- node.element = cls.lookupSelector(selector);
+ if (selector.isCall()) {
+ Element target;
+ if (input.isExtendableArray()) {
+ if (selector.applies(backend.jsArrayRemoveLast, compiler)) {
+ target = backend.jsArrayRemoveLast;
+ } else if (selector.applies(backend.jsArrayAdd, compiler)) {
+ // The codegen special cases array calls, but does not
+ // inline argument type checks.
+ if (!compiler.enableTypeAssertions) {
+ target = backend.jsArrayAdd;
+ }
+ }
+ } else if (input.isString()) {
+ if (selector.applies(backend.jsStringSplit, compiler)) {
+ if (node.inputs[2].isString()) {
+ target = backend.jsStringSplit;
+ }
+ } else if (selector.applies(backend.jsStringConcat, compiler)) {
+ if (node.inputs[2].isString()) {
+ target = backend.jsStringConcat;
+ }
+ } else if (selector.applies(backend.jsStringToString, compiler)) {
+ return input;
+ }
+ }
+ if (target != null) {
+ // TODO(ngeoffray): There is a strong dependency between codegen
+ // and this optimization that the dynamic invoke does not need an
+ // interceptor. We currently need to keep a
+ // HInvokeDynamicMethod and not create a HForeign because
+ // HForeign is too opaque for the SsaCheckInserter (that adds a
+ // bounds check on removeLast). Once we start inlining, the
+ // bounds check will become explicit, so we won't need this
+ // optimization.
+ HInvokeDynamicMethod result = new HInvokeDynamicMethod(
+ node.selector, node.inputs.getRange(1, node.inputs.length - 1));
+ result.element = target;
+ return result;
+ }
+ } else if (selector.isGetter()) {
+ if (selector.applies(backend.jsArrayLength, compiler)) {
+ HInstruction optimized = tryOptimizeLengthInterceptedGetter(node);
+ if (optimized != null) return optimized;
+ }
}
- HInstruction input = node.inputs[1];
- HType type = types[input];
// Check if this call does not need to be intercepted.
- if (interceptor is !HThis && !type.canBePrimitive()) {
- // If the type can be null, and the intercepted method can be in
- // the object class, keep the interceptor.
- if (type.canBeNull()) {
- Set<ClassElement> interceptedClasses;
- if (interceptor is HInterceptor) {
- interceptedClasses = interceptor.interceptedClasses;
- } else if (node is HOneShotInterceptor) {
- var oneShotInterceptor = node;
- interceptedClasses = oneShotInterceptor.interceptedClasses;
- }
- if (interceptedClasses.contains(compiler.objectClass)) return node;
- }
+ HType type = input.instructionType;
+ // TODO(kasperl): Get rid of this refinement once the receiver
+ // specialization phase takes care of it.
+ Selector refined = type.refine(selector, compiler);
+ Set<ClassElement> classes = backend.getInterceptedClassesOn(
+ refined, canBeNull: type.canBeNull());
+ if (classes == null) {
if (selector.isGetter()) {
// Change the call to a regular invoke dynamic call.
return new HInvokeDynamicGetter(selector, null, input, false);
@@ -316,49 +330,14 @@
}
}
- if (selector.isCall()) {
- Element target;
- if (input.isExtendableArray(types)) {
- if (selector.applies(backend.jsArrayRemoveLast, compiler)) {
- target = backend.jsArrayRemoveLast;
- } else if (selector.applies(backend.jsArrayAdd, compiler)) {
- // The codegen special cases array calls, but does not
- // inline argument type checks.
- if (!compiler.enableTypeAssertions) {
- target = backend.jsArrayAdd;
- }
- }
- } else if (input.isString(types)) {
- if (selector.applies(backend.jsStringSplit, compiler)) {
- if (node.inputs[2].isString(types)) {
- target = backend.jsStringSplit;
- }
- } else if (selector.applies(backend.jsStringConcat, compiler)) {
- if (node.inputs[2].isString(types)) {
- target = backend.jsStringConcat;
- }
- } else if (selector.applies(backend.jsStringToString, compiler)) {
- return input;
- }
- }
- if (target != null) {
- // TODO(ngeoffray): There is a strong dependency between codegen
- // and this optimization that the dynamic invoke does not need an
- // interceptor. We currently need to keep a
- // HInvokeDynamicMethod and not create a HForeign because
- // HForeign is too opaque for the SssaCheckInserter (that adds a
- // bounds check on removeLast). Once we start inlining, the
- // bounds check will become explicit, so we won't need this
- // optimization.
- HInvokeDynamicMethod result = new HInvokeDynamicMethod(
- node.selector, node.inputs.getRange(1, node.inputs.length - 1));
- result.element = target;
- return result;
- }
- } else if (selector.isGetter()) {
- if (selector.applies(backend.jsArrayLength, compiler)) {
- return optimizeLengthInterceptedGetter(node);
- }
+ // If the intercepted call is through a constant interceptor, we
+ // know which element to call.
+ if (node is !HOneShotInterceptor
+ && interceptor.isConstant()
+ && selector.isCall()) {
+ DartType type = interceptor.instructionType.computeType(compiler);
+ ClassElement cls = type.element;
+ node.element = cls.lookupSelector(selector);
}
return node;
}
@@ -376,20 +355,21 @@
// other optimizations to reason on a fixed length constructor
// that we know takes an int.
return element == backend.fixedLengthListConstructor
- && node.inputs[1].isInteger(types);
+ && node.inputs[1].isInteger();
}
HInstruction visitInvokeStatic(HInvokeStatic node) {
if (isFixedSizeListConstructor(node)) {
- node.guaranteedType = HType.FIXED_ARRAY;
+ node.instructionType = HType.FIXED_ARRAY;
}
return node;
}
HInstruction visitInvokeDynamicMethod(HInvokeDynamicMethod node) {
if (node.isInterceptorCall) return handleInterceptorCall(node);
- HType receiverType = types[node.receiver];
- Element element = receiverType.lookupSingleTarget(node.selector, compiler);
+ HType receiverType = node.receiver.instructionType;
+ Selector selector = receiverType.refine(node.selector, compiler);
+ Element element = compiler.world.locateSingleElement(selector);
// TODO(ngeoffray): Also fold if it's a getter or variable.
if (element != null && element.isFunction()) {
FunctionElement method = element;
@@ -406,7 +386,7 @@
HInstruction visitIntegerCheck(HIntegerCheck node) {
HInstruction value = node.value;
- if (value.isInteger(types)) return value;
+ if (value.isInteger()) return value;
if (value.isConstant()) {
HConstant constantInstruction = value;
assert(!constantInstruction.constant.isInt());
@@ -461,16 +441,16 @@
HInstruction handleIdentityCheck(HRelational node) {
HInstruction left = node.left;
HInstruction right = node.right;
- HType leftType = types[left];
- HType rightType = types[right];
+ HType leftType = left.instructionType;
+ HType rightType = right.instructionType;
// We don't optimize on numbers to preserve the runtime semantics.
- if (!(left.isNumberOrNull(types) && right.isNumberOrNull(types)) &&
+ if (!(left.isNumberOrNull() && right.isNumberOrNull()) &&
leftType.intersection(rightType, compiler).isConflicting()) {
return graph.addConstantBool(false, constantSystem);
}
- if (left.isConstantBoolean() && right.isBoolean(types)) {
+ if (left.isConstantBoolean() && right.isBoolean()) {
HConstant constant = left;
if (constant.constant.isTrue()) {
return right;
@@ -479,7 +459,7 @@
}
}
- if (right.isConstantBoolean() && left.isBoolean(types)) {
+ if (right.isConstantBoolean() && left.isBoolean()) {
HConstant constant = right;
if (constant.constant.isTrue()) {
return left;
@@ -501,8 +481,9 @@
// If the intersection of the types is still the incoming type then
// the incoming type was a subtype of the guarded type, and no check
// is required.
- HType combinedType = types[value].intersection(node.guardedType, compiler);
- return (combinedType == types[value]) ? value : node;
+ HType combinedType =
+ value.instructionType.intersection(node.guardedType, compiler);
+ return (combinedType == value.instructionType) ? value : node;
}
HInstruction visitIs(HIs node) {
@@ -514,7 +495,7 @@
return node;
}
- HType expressionType = types[node.expression];
+ HType expressionType = node.expression.instructionType;
if (identical(element, compiler.objectClass)
|| identical(element, compiler.dynamicClass)) {
return graph.addConstantBool(true, constantSystem);
@@ -584,26 +565,24 @@
HInstruction visitTypeConversion(HTypeConversion node) {
HInstruction value = node.inputs[0];
- DartType type = types[node].computeType(compiler);
+ DartType type = node.instructionType.computeType(compiler);
if (identical(type.element, compiler.dynamicClass)
|| identical(type.element, compiler.objectClass)) {
return value;
}
- if (types[value].canBeNull() && node.isBooleanConversionCheck) {
+ if (value.instructionType.canBeNull() && node.isBooleanConversionCheck) {
return node;
}
- HType combinedType = types[value].intersection(types[node], compiler);
- return (combinedType == types[value]) ? value : node;
+ HType combinedType =
+ value.instructionType.intersection(node.instructionType, compiler);
+ return (combinedType == value.instructionType) ? value : node;
}
Element findConcreteFieldForDynamicAccess(HInstruction receiver,
Selector selector) {
- HType receiverType = types[receiver];
- if (!receiverType.isUseful()) return null;
- DartType type = receiverType.computeType(compiler);
- if (type == null) return null;
- if (Elements.isErroneousElement(type.element)) return null;
- return compiler.world.locateSingleField(type, selector);
+ HType receiverType = receiver.instructionType;
+ return compiler.world.locateSingleField(
+ receiverType.refine(selector, compiler));
}
HInstruction visitFieldGet(HFieldGet node) {
@@ -638,14 +617,19 @@
field, node.inputs[0], isAssignable: !isFinalOrConst);
if (field.getEnclosingClass().isNative()) {
- result.guaranteedType =
+ result.instructionType =
new HType.subtype(field.computeType(compiler), compiler);
} else {
- HType type = backend.optimisticFieldType(field);
+ HType type = new HType.inferredForElement(field, compiler);
+ if (type.isUnknown()) {
+ type = backend.optimisticFieldType(field);
+ if (type != null) {
+ backend.registerFieldTypesOptimization(
+ work.element, field, result.instructionType);
+ }
+ }
if (type != null) {
- backend.registerFieldTypesOptimization(
- work.element, field, result.guaranteedType);
- result.guaranteedType = type;
+ result.instructionType = type;
}
}
return result;
@@ -694,7 +678,7 @@
HInstruction tryComputeConstantInterceptor(HInstruction input,
Set<ClassElement> intercepted) {
- HType type = types[input];
+ HType type = input.instructionType;
ClassElement constantInterceptor;
if (type.isInteger()) {
constantInterceptor = backend.jsIntClass;
@@ -758,7 +742,6 @@
}
class SsaCheckInserter extends HBaseVisitor implements OptimizationPhase {
- final HTypeMap types;
final Set<HInstruction> boundsChecked;
final CodegenWorkItem work;
final JavaScriptBackend backend;
@@ -767,7 +750,6 @@
SsaCheckInserter(this.backend,
this.work,
- this.types,
this.boundsChecked);
void visitGraph(HGraph graph) {
@@ -787,11 +769,11 @@
HBoundsCheck insertBoundsCheck(HInstruction node,
HInstruction receiver,
HInstruction index) {
- bool isAssignable = !receiver.isFixedArray(types);
+ bool isAssignable = !receiver.isFixedArray();
HFieldGet length = new HFieldGet(
backend.jsArrayLength, receiver, isAssignable: isAssignable);
- length.guaranteedType = HType.INTEGER;
- types[length] = HType.INTEGER;
+ length.instructionType = HType.INTEGER;
+ length.instructionType = HType.INTEGER;
node.block.addBefore(node, length);
HBoundsCheck check = new HBoundsCheck(index, length);
@@ -813,7 +795,7 @@
void visitIndex(HIndex node) {
if (boundsChecked.contains(node)) return;
HInstruction index = node.index;
- if (!node.index.isInteger(types)) {
+ if (!node.index.isInteger()) {
index = insertIntegerCheck(node, index);
}
index = insertBoundsCheck(node, node.receiver, index);
@@ -821,10 +803,10 @@
}
void visitIndexAssign(HIndexAssign node) {
- if (!node.receiver.isMutableArray(types)) return;
+ if (!node.receiver.isMutableArray()) return;
if (boundsChecked.contains(node)) return;
HInstruction index = node.index;
- if (!node.index.isInteger(types)) {
+ if (!node.index.isInteger()) {
index = insertIntegerCheck(node, index);
}
index = insertBoundsCheck(node, node.receiver, index);
@@ -842,10 +824,9 @@
}
class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase {
- final HTypeMap types;
final String name = "SsaDeadCodeEliminator";
- SsaDeadCodeEliminator(this.types);
+ SsaDeadCodeEliminator();
bool isDeadCode(HInstruction instruction) {
return !instruction.hasSideEffects()
@@ -977,13 +958,12 @@
class SsaGlobalValueNumberer implements OptimizationPhase {
final String name = "SsaGlobalValueNumberer";
final Compiler compiler;
- final HTypeMap types;
final Set<int> visited;
List<int> blockChangesFlags;
List<int> loopChangesFlags;
- SsaGlobalValueNumberer(this.compiler, this.types) : visited = new Set<int>();
+ SsaGlobalValueNumberer(this.compiler) : visited = new Set<int>();
void visitGraph(HGraph graph) {
computeChangesFlags(graph);
@@ -1323,7 +1303,6 @@
extends HBaseVisitor implements OptimizationPhase {
final JavaScriptBackend backend;
final CodegenWorkItem work;
- final HTypeMap types;
final String name = "SsaConstructionFieldTypes";
final Set<HInstruction> thisUsers;
final Set<Element> allSetters;
@@ -1332,9 +1311,7 @@
HGraph currentGraph;
Map<Element, HType> currentFieldSetters;
- SsaConstructionFieldTypes(JavaScriptBackend this.backend,
- CodegenWorkItem this.work,
- HTypeMap this.types)
+ SsaConstructionFieldTypes(this.backend, this.work)
: thisUsers = new Set<HInstruction>(),
allSetters = new Set<Element>(),
blockFieldSetters = new Map<HBasicBlock, Map<Element, HType>>();
@@ -1417,7 +1394,8 @@
int j = 0;
node.element.forEachInstanceField(
(ClassElement enclosingClass, Element element) {
- backend.registerFieldInitializer(element, types[node.inputs[j]]);
+ backend.registerFieldInitializer(
+ element, node.inputs[j].instructionType);
j++;
},
includeBackendMembers: false,
@@ -1427,14 +1405,14 @@
visitFieldSet(HFieldSet node) {
Element field = node.element;
HInstruction value = node.value;
- HType type = types[value];
+ HType type = value.instructionType;
// [HFieldSet] is also used for variables in try/catch.
if (field.isField()) allSetters.add(field);
// Don't handle fields defined in superclasses. Given that the field is
// always added to the [allSetters] set, setting a field defined in a
// superclass will get an inferred type of UNKNOWN.
if (identical(work.element.getEnclosingClass(), field.getEnclosingClass()) &&
- value.hasGuaranteedType()) {
+ !value.instructionType.isUnknown()) {
currentFieldSetters[field] = type;
}
}
@@ -1490,7 +1468,12 @@
// double class.
if (otherIntercepted.contains(backend.jsIntClass)
|| otherIntercepted.contains(backend.jsDoubleClass)) {
- interceptor.interceptedClasses.addAll(user.interceptedClasses);
+ // We cannot modify the intercepted classes set without
+ // copying because it may be shared by multiple interceptors
+ // because of caching in the backend.
+ Set<ClassElement> copy = interceptor.interceptedClasses.toSet();
+ copy.addAll(user.interceptedClasses);
+ interceptor.interceptedClasses = copy;
}
user.interceptedClasses = interceptor.interceptedClasses;
}
@@ -1509,10 +1492,9 @@
implements OptimizationPhase {
final String name = "SsaSimplifyInterceptors";
final ConstantSystem constantSystem;
- final HTypeMap types;
HGraph graph;
- SsaSimplifyInterceptors(this.constantSystem, this.types);
+ SsaSimplifyInterceptors(this.constantSystem);
void visitGraph(HGraph graph) {
this.graph = graph;
@@ -1539,8 +1521,7 @@
user.selector, inputs, node.interceptedClasses);
interceptor.sourcePosition = user.sourcePosition;
interceptor.sourceElement = user.sourceElement;
- interceptor.guaranteedType = user.guaranteedType;
- types[interceptor] = types[user];
+ interceptor.instructionType = user.instructionType;
HBasicBlock block = user.block;
block.addAfter(user, interceptor);
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/tracer.dart b/sdk/lib/_internal/compiler/implementation/ssa/tracer.dart
index 7a8f6bc..fb62f89 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/tracer.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/tracer.dart
@@ -77,7 +77,6 @@
void addInstructions(HInstructionStringifier stringifier,
HInstructionList list) {
- HTypeMap types = context.types;
for (HInstruction instruction = list.first;
instruction != null;
instruction = instruction.next) {
@@ -115,8 +114,8 @@
String phiId = stringifier.temporaryId(phi);
StringBuffer inputIds = new StringBuffer();
for (int i = 0; i < phi.inputs.length; i++) {
- inputIds.add(stringifier.temporaryId(phi.inputs[i]));
- inputIds.add(" ");
+ inputIds.write(stringifier.temporaryId(phi.inputs[i]));
+ inputIds.write(" ");
}
println("${phi.id} $phiId [ $inputIds]");
});
@@ -176,7 +175,7 @@
String temporaryId(HInstruction instruction) {
String prefix;
- HType type = context.types[instruction];
+ HType type = instruction.instructionType;
if (!type.isPrimitive()) {
prefix = 'U';
} else {
@@ -215,7 +214,7 @@
StringBuffer envBuffer = new StringBuffer();
List<HInstruction> inputs = node.inputs;
for (int i = 0; i < inputs.length; i++) {
- envBuffer.add(" ${temporaryId(inputs[i])}");
+ envBuffer.write(" ${temporaryId(inputs[i])}");
}
String on = node.isEnabled ? "enabled" : "disabled";
return "BailoutTarget($on): id: ${node.state} env: $envBuffer";
@@ -316,8 +315,8 @@
List<HInstruction> arguments) {
StringBuffer argumentsString = new StringBuffer();
for (int i = 0; i < arguments.length; i++) {
- if (i != 0) argumentsString.add(", ");
- argumentsString.add(temporaryId(arguments[i]));
+ if (i != 0) argumentsString.write(", ");
+ argumentsString.write(temporaryId(arguments[i]));
}
return "$invokeType: $functionName($argumentsString)";
}
@@ -397,8 +396,8 @@
String visitLiteralList(HLiteralList node) {
StringBuffer elementsString = new StringBuffer();
for (int i = 0; i < node.inputs.length; i++) {
- if (i != 0) elementsString.add(", ");
- elementsString.add(temporaryId(node.inputs[i]));
+ if (i != 0) elementsString.write(", ");
+ elementsString.write(temporaryId(node.inputs[i]));
}
return "Literal list: [$elementsString]";
}
@@ -429,12 +428,12 @@
String visitPhi(HPhi phi) {
StringBuffer buffer = new StringBuffer();
- buffer.add("Phi(");
+ buffer.write("Phi(");
for (int i = 0; i < phi.inputs.length; i++) {
- if (i > 0) buffer.add(", ");
- buffer.add(temporaryId(phi.inputs[i]));
+ if (i > 0) buffer.write(", ");
+ buffer.write(temporaryId(phi.inputs[i]));
}
- buffer.add(")");
+ buffer.write(")");
return buffer.toString();
}
@@ -466,17 +465,17 @@
String visitSwitch(HSwitch node) {
StringBuffer buf = new StringBuffer();
- buf.add("Switch: (");
- buf.add(temporaryId(node.inputs[0]));
- buf.add(") ");
+ buf.write("Switch: (");
+ buf.write(temporaryId(node.inputs[0]));
+ buf.write(") ");
for (int i = 1; i < node.inputs.length; i++) {
- buf.add(temporaryId(node.inputs[i]));
- buf.add(": B");
- buf.add(node.block.successors[i - 1].id);
- buf.add(", ");
+ buf.write(temporaryId(node.inputs[i]));
+ buf.write(": B");
+ buf.write(node.block.successors[i - 1].id);
+ buf.write(", ");
}
- buf.add("default: B");
- buf.add(node.block.successors.last.id);
+ buf.write("default: B");
+ buf.write(node.block.successors.last.id);
return buf.toString();
}
@@ -539,7 +538,7 @@
assert(inputs[0] == guarded);
assert(inputs[1] == bailoutTarget);
for (int i = 2; i < inputs.length; i++) {
- envBuffer.add(" ${temporaryId(inputs[i])}");
+ envBuffer.write(" ${temporaryId(inputs[i])}");
}
String on = node.isEnabled ? "enabled" : "disabled";
String guardedId = temporaryId(node.guarded);
@@ -554,7 +553,8 @@
}
String visitTypeConversion(HTypeConversion node) {
- return "TypeConversion: ${temporaryId(node.checkedInput)} to ${node.type}";
+ return "TypeConversion: ${temporaryId(node.checkedInput)} to "
+ "${node.instructionType}";
}
String visitRangeConversion(HRangeConversion node) {
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/types.dart b/sdk/lib/_internal/compiler/implementation/ssa/types.dart
index 8b55c23..8372c2b 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/types.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/types.dart
@@ -29,7 +29,7 @@
JavaScriptBackend backend = compiler.backend;
if (element == compiler.intClass || element == backend.jsIntClass) {
return canBeNull ? HType.INTEGER_OR_NULL : HType.INTEGER;
- } else if (element == compiler.numClass
+ } else if (element == compiler.numClass
|| element == backend.jsNumberClass) {
return canBeNull ? HType.NUMBER_OR_NULL : HType.NUMBER;
} else if (element == compiler.doubleClass
@@ -48,7 +48,7 @@
return canBeNull
? HType.READABLE_ARRAY.union(HType.NULL, compiler)
: HType.READABLE_ARRAY;
- } else if (!isExact) {
+ } else if (isInterfaceType) {
if (element == compiler.listClass
|| Elements.isListSupertype(element, compiler)) {
return new HBoundedPotentialPrimitiveArray(
@@ -65,15 +65,16 @@
type,
canBeNull: canBeNull,
isInterfaceType: isInterfaceType);
- } else if (element == compiler.objectClass
- || element == compiler.dynamicClass) {
- return new HBoundedPotentialPrimitiveType(
- compiler.objectClass.computeType(compiler),
- true,
- canBeNull: canBeNull,
- isInterfaceType: isInterfaceType);
}
}
+ if (!isExact && (element == compiler.objectClass ||
+ element == compiler.dynamicClass)) {
+ return new HBoundedPotentialPrimitiveType(
+ compiler.objectClass.computeType(compiler),
+ true,
+ canBeNull: canBeNull,
+ isInterfaceType: isInterfaceType);
+ }
return new HBoundedType(
type,
canBeNull: canBeNull,
@@ -117,6 +118,75 @@
isInterfaceType: true);
}
+ factory HType.fromBaseType(BaseType baseType, Compiler compiler) {
+ if (!baseType.isClass()) return HType.UNKNOWN;
+ ClassBaseType classBaseType = baseType;
+ ClassElement cls = classBaseType.element;
+ // Special case the list and map classes that are used as types
+ // for literals in the type inferrer.
+ if (cls == compiler.listClass) {
+ return HType.READABLE_ARRAY;
+ } else if (cls == compiler.mapClass) {
+ // TODO(ngeoffray): get the actual implementation of a map
+ // literal.
+ return new HType.nonNullSubtype(
+ compiler.mapLiteralClass.computeType(compiler), compiler);
+ } else {
+ return new HType.nonNullExactClass(
+ cls.computeType(compiler), compiler);
+ }
+ }
+
+ factory HType.fromInferredType(ConcreteType concreteType, Compiler compiler) {
+ if (concreteType == null) return HType.UNKNOWN;
+ HType ssaType = HType.CONFLICTING;
+ for (BaseType baseType in concreteType.baseTypes) {
+ ssaType = ssaType.union(
+ new HType.fromBaseType(baseType, compiler), compiler);
+ }
+ if (ssaType.isConflicting()) return HType.UNKNOWN;
+ return ssaType;
+ }
+
+ factory HType.inferredForElement(Element element, Compiler compiler) {
+ return new HType.fromInferredType(
+ compiler.typesTask.getGuaranteedTypeOfElement(element),
+ compiler);
+ }
+
+ factory HType.inferredForNode(
+ Element owner, Node node, Compiler compiler) {
+ return new HType.fromInferredType(
+ compiler.typesTask.getGuaranteedTypeOfNode(owner, node),
+ compiler);
+ }
+
+ // [type] is either an instance of [DartType] or special objects
+ // like [native.SpecialType.JsObject], or [native.SpecialType.JsArray].
+ factory HType.fromNativeType(type, Compiler compiler) {
+ if (type == native.SpecialType.JsObject) {
+ return new HType.nonNullExactClass(
+ compiler.objectClass.computeType(compiler), compiler);
+ } else if (type == native.SpecialType.JsArray) {
+ return HType.READABLE_ARRAY;
+ } else {
+ return new HType.nonNullSubclass(type, compiler);
+ }
+ }
+
+ factory HType.fromNativeBehavior(native.NativeBehavior nativeBehavior,
+ Compiler compiler) {
+ if (nativeBehavior.typesInstantiated.isEmpty) return HType.UNKNOWN;
+
+ HType ssaType = HType.CONFLICTING;
+ for (final type in nativeBehavior.typesInstantiated) {
+ ssaType = ssaType.union(
+ new HType.fromNativeType(type, compiler), compiler);
+ }
+ assert(!ssaType.isConflicting());
+ return ssaType;
+ }
+
static const HType CONFLICTING = const HConflictingType();
static const HType UNKNOWN = const HUnknownType();
static const HType BOOLEAN = const HBooleanType();
@@ -169,27 +239,23 @@
/** Alias for isReadableArray. */
bool isArray() => isReadableArray();
- Element lookupSingleTarget(Selector selector, Compiler compiler) {
- if (isInterfaceType()) return null;
- DartType type = computeType(compiler);
- if (type == null) return null;
- ClassElement cls = type.element;
- Element member = cls.lookupSelector(selector);
- if (member == null) return null;
- // [:ClassElement.lookupSelector:] may return an abstract field,
- // and selctors don't work well with them.
- // TODO(ngeoffray): Clean up lookupSelector and selectors to know
- // if it's a getter or a setter that we're interested in.
- if (!member.isFunction()) return null;
- if (!selector.applies(member, compiler)) return null;
- if (!isExact() && !compiler.world.hasNoOverridingMember(member)) {
- return null;
- }
- return member;
- }
-
DartType computeType(Compiler compiler);
+ Selector refine(Selector selector, Compiler compiler) {
+ DartType receiverType = computeType(compiler);
+ if (receiverType != null && !receiverType.isMalformed) {
+ if (isExact()) {
+ return new TypedSelector.exact(receiverType, selector);
+ } else if (isInterfaceType()) {
+ return new TypedSelector.subtype(receiverType, selector);
+ } else {
+ return new TypedSelector.subclass(receiverType, selector);
+ }
+ } else {
+ return selector;
+ }
+ }
+
/**
* The intersection of two types is the intersection of its values. For
* example:
@@ -259,7 +325,7 @@
const HNullType();
bool canBeNull() => true;
bool isNull() => true;
- String toString() => 'null';
+ String toString() => 'null type';
DartType computeType(Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
@@ -702,7 +768,7 @@
DartType computeType(Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
- return backend.jsArrayClass.computeType(compiler);
+ return backend.jsArrayClass.rawType;
}
HType union(HType other, Compiler compiler) {
@@ -1101,31 +1167,3 @@
return super.intersection(other, compiler);
}
}
-
-class HTypeMap {
- // Approximately 85% of methods in the sample "swarm" have less than
- // 32 instructions.
- static const int INITIAL_SIZE = 32;
-
- List<HType> _list = new List<HType>()..length = INITIAL_SIZE;
-
- operator [](HInstruction instruction) {
- HType result;
- if (instruction.id < _list.length) result = _list[instruction.id];
- if (result == null) return instruction.guaranteedType;
- return result;
- }
-
- operator []=(HInstruction instruction, HType value) {
- int length = _list.length;
- int id = instruction.id;
- if (length <= id) {
- if (id + 1 < length * 2) {
- _list.length = length * 2;
- } else {
- _list.length = id + 1;
- }
- }
- _list[id] = value;
- }
-}
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/types_propagation.dart b/sdk/lib/_internal/compiler/implementation/ssa/types_propagation.dart
index 223e017..71a20d6 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/types_propagation.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/types_propagation.dart
@@ -4,62 +4,57 @@
part of ssa;
-class SsaTypePropagator extends HGraphVisitor implements OptimizationPhase {
+abstract class SsaTypePropagator extends HBaseVisitor
+ implements OptimizationPhase {
final Map<int, HInstruction> workmap;
final List<int> worklist;
final Map<HInstruction, Function> pendingOptimizations;
- final HTypeMap types;
final Compiler compiler;
String get name => 'type propagator';
- SsaTypePropagator(this.compiler, this.types)
+ SsaTypePropagator(this.compiler)
: workmap = new Map<int, HInstruction>(),
worklist = new List<int>(),
pendingOptimizations = new Map<HInstruction, Function>();
+ // Compute the (shared) type of the inputs if any. If all inputs
+ // have the same known type return it. If any two inputs have
+ // different known types, we'll return a conflict -- otherwise we'll
+ // simply return an unknown type.
+ HType computeInputsType(HPhi phi, bool ignoreUnknowns) {
+ HType candidateType = HType.CONFLICTING;
+ for (int i = 0, length = phi.inputs.length; i < length; i++) {
+ HType inputType = phi.inputs[i].instructionType;
+ if (ignoreUnknowns && inputType.isUnknown()) continue;
+ // Phis need to combine the incoming types using the union operation.
+ // For example, if one incoming edge has type integer and the other has
+ // type double, then the phi is either an integer or double and thus has
+ // type number.
+ candidateType = candidateType.union(inputType, compiler);
+ if (candidateType.isUnknown()) return HType.UNKNOWN;
+ }
+ return candidateType;
+ }
+
HType computeType(HInstruction instruction) {
- return instruction.computeTypeFromInputTypes(types, compiler);
+ return instruction.accept(this);
}
// Re-compute and update the type of the instruction. Returns
// whether or not the type was changed.
bool updateType(HInstruction instruction) {
- // The [updateType] method is invoked when one of the inputs of
- // the instruction changes its type. That gives us a new
- // opportunity to consider this instruction for optimizations.
- considerForArgumentTypeOptimization(instruction);
// Compute old and new types.
- HType oldType = types[instruction];
+ HType oldType = instruction.instructionType;
HType newType = computeType(instruction);
+ assert(newType != null);
// We unconditionally replace the propagated type with the new type. The
// computeType must make sure that we eventually reach a stable state.
- types[instruction] = newType;
+ instruction.instructionType = newType;
return oldType != newType;
}
- void considerForArgumentTypeOptimization(HInstruction instruction) {
- // Update the pending optimizations map based on the potentially
- // new types of the operands. If the operand types no longer allow
- // us to optimize, we remove the pending optimization.
- if (instruction is !HInvokeDynamicMethod) return;
- HInvokeDynamicMethod invoke = instruction;
- if (instruction.specializer is !BinaryArithmeticSpecializer) return;
- HInstruction left = instruction.inputs[1];
- HInstruction right = instruction.inputs[2];
- if (left.isNumber(types) && !right.isNumber(types)) {
- pendingOptimizations[instruction] = () {
- // This callback function is invoked after we're done
- // propagating types. The types shouldn't have changed.
- assert(left.isNumber(types) && !right.isNumber(types));
- convertInput(instruction, right, HType.NUMBER);
- };
- } else {
- pendingOptimizations.remove(instruction);
- }
- }
-
void visitGraph(HGraph graph) {
visitDominatorTree(graph);
processWorklist();
@@ -75,7 +70,7 @@
// the type of all other incoming edges as "unitialized" and take this
// into account when doing the propagation inside the phis. Just
// setting the propagated type is however easier.
- types[phi] = types[phi.inputs[0]];
+ phi.instructionType = phi.inputs[0].instructionType;
addToWorkList(phi);
});
} else {
@@ -114,16 +109,11 @@
} while (!worklist.isEmpty);
}
- void addDependentInstructionsToWorkList(HInstruction instruction) {
- for (int i = 0, length = instruction.usedBy.length; i < length; i++) {
- // The non-speculative type propagator only propagates types forward. We
- // thus only need to add the users of the [instruction] to the list.
- addToWorkList(instruction.usedBy[i]);
- }
- }
+ void addDependentInstructionsToWorkList(HInstruction instruction) {}
void addToWorkList(HInstruction instruction) {
final int id = instruction.id;
+
if (!workmap.containsKey(id)) {
worklist.add(id);
workmap[id] = instruction;
@@ -135,6 +125,64 @@
pendingOptimizations.clear();
}
+ HType visitInvokeDynamic(HInvokeDynamic instruction) {
+ int receiverIndex = instruction.isInterceptorCall ? 1 : 0;
+ HType receiverType = instruction.inputs[receiverIndex].instructionType;
+ Selector refined = receiverType.refine(instruction.selector, compiler);
+ // TODO(kasperl): Ask the type inferrer about the type of the
+ // selector not the individual elements. This is basically code
+ // lifted out of the inferrer. Not good.
+ HType type = HType.CONFLICTING;
+ DartType functionType = compiler.functionClass.computeType(compiler);
+ for (Element each in compiler.world.allFunctions.filter(refined)) {
+ HType inferred = (refined.isGetter() && each.isFunction())
+ ? new HType.nonNullExactClass(functionType, compiler)
+ : new HType.inferredForElement(each, compiler);
+ type = type.union(inferred, compiler);
+ if (type.isUnknown()) break;
+ }
+ if (type.isUseful()) return type;
+ return instruction.specializer.computeTypeFromInputTypes(
+ instruction, compiler);
+ }
+
+ HType visitBinaryArithmetic(HBinaryArithmetic instruction) {
+ HInstruction left = instruction.left;
+ HInstruction right = instruction.right;
+ if (left.isInteger() && right.isInteger()) return HType.INTEGER;
+ if (left.isDouble()) return HType.DOUBLE;
+ return HType.NUMBER;
+ }
+
+ HType visitNegate(HNegate instruction) {
+ return instruction.operand.instructionType;
+ }
+
+ HType visitInstruction(HInstruction instruction) {
+ assert(instruction.instructionType != null);
+ return instruction.instructionType;
+ }
+
+ HType visitPhi(HPhi phi) {
+ HType inputsType = computeInputsType(phi, false);
+ if (inputsType.isConflicting()) return HType.UNKNOWN;
+ return inputsType;
+ }
+}
+
+class SsaNonSpeculativeTypePropagator extends SsaTypePropagator {
+ final String name = 'non speculative type propagator';
+ DesiredTypeVisitor desiredTypeVisitor;
+ SsaNonSpeculativeTypePropagator(Compiler compiler) : super(compiler);
+
+ void addDependentInstructionsToWorkList(HInstruction instruction) {
+ for (int i = 0, length = instruction.usedBy.length; i < length; i++) {
+ // The non-speculative type propagator only propagates types forward. We
+ // thus only need to add the users of the [instruction] to the list.
+ addToWorkList(instruction.usedBy[i]);
+ }
+ }
+
void convertInput(HInstruction instruction, HInstruction input, HType type) {
HTypeConversion converted =
new HTypeConversion.argumentTypeCheck(type, input);
@@ -145,12 +193,114 @@
addToWorkList(user);
}
}
+
+ HType visitInvokeDynamicMethod(HInvokeDynamicMethod instruction) {
+ // Update the pending optimizations map based on the potentially
+ // new types of the operands. If the operand types no longer allow
+ // us to optimize, we remove the pending optimization.
+ if (instruction.specializer is BinaryArithmeticSpecializer) {
+ HInstruction left = instruction.inputs[1];
+ HInstruction right = instruction.inputs[2];
+ if (left.isNumber() && !right.isNumber()) {
+ pendingOptimizations[instruction] = () {
+ // This callback function is invoked after we're done
+ // propagating types. The types shouldn't have changed.
+ assert(left.isNumber() && !right.isNumber());
+ convertInput(instruction, right, HType.NUMBER);
+ };
+ } else {
+ pendingOptimizations.remove(instruction);
+ }
+ }
+ return super.visitInvokeDynamicMethod(instruction);
+ }
+}
+
+/**
+ * Visitor whose methods return the desired type for the input of an
+ * instruction.
+ */
+class DesiredTypeVisitor extends HBaseVisitor {
+ final Compiler compiler;
+ final SsaTypePropagator propagator;
+ HInstruction input;
+
+ DesiredTypeVisitor(this.compiler, this.propagator);
+
+ HType visitInstruction(HInstruction instruction) {
+ return HType.UNKNOWN;
+ }
+
+ HType visitIntegerCheck(HIntegerCheck instruction) {
+ // If the desired type of the input is already a number, we want
+ // to specialize it to an integer.
+ return input.isNumber() ? HType.INTEGER : HType.UNKNOWN;
+ }
+
+ HType visitInvokeDynamic(HInvokeDynamic instruction) {
+ return instruction.specializer.computeDesiredTypeForInput(
+ instruction, input, compiler);
+ }
+
+ HType visitNot(HNot instruction) {
+ return HType.BOOLEAN;
+ }
+
+ HType visitPhi(HPhi phi) {
+ HType propagatedType = phi.instructionType;
+ // Best case scenario for a phi is, when all inputs have the same type. If
+ // there is no desired outgoing type we therefore try to unify the input
+ // types (which is basically the [likelyType]).
+ if (propagatedType.isUnknown()) return computeLikelyType(phi);
+ // When the desired outgoing type is conflicting we don't need to give any
+ // requirements on the inputs.
+ if (propagatedType.isConflicting()) return HType.UNKNOWN;
+ // Otherwise the input type must match the desired outgoing type.
+ return propagatedType;
+ }
+
+ HType computeLikelyType(HPhi phi) {
+ HType agreedType = propagator.computeInputsType(phi, true);
+ if (agreedType.isConflicting()) return HType.UNKNOWN;
+ // Don't be too restrictive. If the agreed type is integer or double just
+ // say that the likely type is number. If more is expected the type will be
+ // propagated back.
+ if (agreedType.isNumber()) return HType.NUMBER;
+ return agreedType;
+ }
+
+ HType visitInterceptor(HInterceptor instruction) {
+ if (instruction.interceptedClasses.length != 1) return HType.UNKNOWN;
+ // If the only class being intercepted is of type number, we
+ // make this interceptor call say it wants that class as input.
+ Element interceptor = instruction.interceptedClasses.toList()[0];
+ JavaScriptBackend backend = compiler.backend;
+ if (interceptor == backend.jsNumberClass) {
+ return HType.NUMBER;
+ } else if (interceptor == backend.jsIntClass) {
+ return HType.INTEGER;
+ } else if (interceptor == backend.jsDoubleClass) {
+ return HType.DOUBLE;
+ }
+ return HType.UNKNOWN;
+ }
+
+ HType computeDesiredTypeForInput(HInstruction user, HInstruction input) {
+ this.input = input;
+ HType desired = user.accept(this);
+ this.input = null;
+ return desired;
+ }
}
class SsaSpeculativeTypePropagator extends SsaTypePropagator {
final String name = 'speculative type propagator';
- SsaSpeculativeTypePropagator(Compiler compiler, HTypeMap types)
- : super(compiler, types);
+ DesiredTypeVisitor desiredTypeVisitor;
+ final Map<HInstruction, HType> savedTypes;
+ SsaSpeculativeTypePropagator(Compiler compiler, this.savedTypes)
+ : super(compiler) {
+ desiredTypeVisitor = new DesiredTypeVisitor(compiler, this);
+ }
void addDependentInstructionsToWorkList(HInstruction instruction) {
// The speculative type propagator propagates types forward and backward.
@@ -168,16 +318,9 @@
HType computeDesiredType(HInstruction instruction) {
HType desiredType = HType.UNKNOWN;
for (final user in instruction.usedBy) {
- HType userType =
- user.computeDesiredTypeForInput(instruction, types, compiler);
- // Mainly due to the "if (true)" added by hackAroundPossiblyAbortingBody
- // in builder.dart uninitialized variables will propagate a type of null
- // which will result in a conflicting type when combined with a primitive
- // type. Avoid this to improve generated code.
- // TODO(sgjesse): Reconcider this when hackAroundPossiblyAbortingBody
- // has been removed.
- if (desiredType.isPrimitive() && userType == HType.NULL) continue;
- desiredType = desiredType.intersection(userType, compiler);
+ HType userDesiredType = desiredTypeVisitor.computeDesiredTypeForInput(
+ user, instruction);
+ desiredType = desiredType.intersection(userDesiredType, compiler);
// No need to continue if two users disagree on the type.
if (desiredType.isConflicting()) break;
}
@@ -186,24 +329,27 @@
HType computeType(HInstruction instruction) {
// Once we are in a conflicting state don't update the type anymore.
- HType oldType = types[instruction];
+ HType oldType = instruction.instructionType;
if (oldType.isConflicting()) return oldType;
HType newType = super.computeType(instruction);
+ if (oldType != newType && !savedTypes.containsKey(instruction)) {
+ savedTypes[instruction] = oldType;
+ }
// [computeDesiredType] goes to all usedBys and lets them compute their
// desired type. By setting the [newType] here we give them more context to
// work with.
- types[instruction] = newType;
+ instruction.instructionType = newType;
HType desiredType = computeDesiredType(instruction);
// If the desired type is conflicting just return the computed type.
if (desiredType.isConflicting()) return newType;
// TODO(ngeoffray): Allow speculative optimizations on
// non-primitive types?
if (!desiredType.isPrimitive()) return newType;
- return newType.intersection(desiredType, compiler);
+ desiredType = newType.intersection(desiredType, compiler);
+ if (desiredType != newType && !savedTypes.containsKey(instruction)) {
+ savedTypes[instruction] = oldType;
+ }
+ return desiredType;
}
-
- // Do not use speculative argument type optimization for now.
- void considerForArgumentTypeOptimization(HInstruction instruction) { }
-
}
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/value_range_analyzer.dart b/sdk/lib/_internal/compiler/implementation/ssa/value_range_analyzer.dart
index 7ea24ed..e8f3d55 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/value_range_analyzer.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/value_range_analyzer.dart
@@ -552,13 +552,12 @@
final Map<HInstruction, Range> ranges = new Map<HInstruction, Range>();
final ConstantSystem constantSystem;
- final HTypeMap types;
final ValueRangeInfo info;
CodegenWorkItem work;
HGraph graph;
- SsaValueRangeAnalyzer(constantSystem, this.types, this.work)
+ SsaValueRangeAnalyzer(constantSystem, this.work)
: info = new ValueRangeInfo(constantSystem),
this.constantSystem = constantSystem;
@@ -582,7 +581,7 @@
void visit(HInstruction instruction) {
Range range = instruction.accept(this);
- if (instruction.isInteger(types)) {
+ if (instruction.isInteger()) {
assert(range != null);
ranges[instruction] = range;
}
@@ -597,13 +596,13 @@
}
Range visitParameterValue(HParameterValue parameter) {
- if (!parameter.isInteger(types)) return info.newUnboundRange();
+ if (!parameter.isInteger()) return info.newUnboundRange();
Value value = info.newInstructionValue(parameter);
return info.newRange(value, value);
}
Range visitPhi(HPhi phi) {
- if (!phi.isInteger(types)) return info.newUnboundRange();
+ if (!phi.isInteger()) return info.newUnboundRange();
if (phi.block.isLoopHeader()) {
Range range = tryInferLoopPhiRange(phi);
if (range == null) return info.newUnboundRange();
@@ -619,19 +618,19 @@
Range tryInferLoopPhiRange(HPhi phi) {
HInstruction update = phi.inputs[1];
- return update.accept(new LoopUpdateRecognizer(phi, ranges, types, info));
+ return update.accept(new LoopUpdateRecognizer(phi, ranges, info));
}
Range visitConstant(HConstant constant) {
- if (!constant.isInteger(types)) return info.newUnboundRange();
+ if (!constant.isInteger()) return info.newUnboundRange();
IntConstant constantInt = constant.constant;
Value value = info.newIntValue(constantInt.value);
return info.newRange(value, value);
}
Range visitFieldGet(HFieldGet fieldGet) {
- if (!fieldGet.isInteger(types)) return info.newUnboundRange();
- if (!fieldGet.receiver.isIndexablePrimitive(types)) {
+ if (!fieldGet.isInteger()) return info.newUnboundRange();
+ if (!fieldGet.receiver.isIndexablePrimitive()) {
return visitInstruction(fieldGet);
}
LengthValue value = info.newLengthValue(fieldGet);
@@ -647,8 +646,8 @@
HInstruction next = check.next;
Range indexRange = ranges[check.index];
Range lengthRange = ranges[check.length];
- assert(check.index.isInteger(types));
- assert(check.length.isInteger(types));
+ assert(check.index.isInteger());
+ assert(check.length.isInteger());
// Check if the index is strictly below the upper bound of the length
// range.
@@ -703,8 +702,8 @@
Range visitRelational(HRelational relational) {
HInstruction right = relational.right;
HInstruction left = relational.left;
- if (!left.isInteger(types)) return info.newUnboundRange();
- if (!right.isInteger(types)) return info.newUnboundRange();
+ if (!left.isInteger()) return info.newUnboundRange();
+ if (!right.isInteger()) return info.newUnboundRange();
BinaryOperation operation = relational.operation(constantSystem);
Range rightRange = ranges[relational.right];
Range leftRange = ranges[relational.left];
@@ -734,7 +733,7 @@
}
Range handleBinaryOperation(HBinaryArithmetic instruction) {
- if (!instruction.isInteger(types)) return info.newUnboundRange();
+ if (!instruction.isInteger()) return info.newUnboundRange();
return instruction.operation(constantSystem).apply(
ranges[instruction.left], ranges[instruction.right]);
}
@@ -748,10 +747,10 @@
}
Range visitBitAnd(HBitAnd node) {
- if (!node.isInteger(types)) return info.newUnboundRange();
+ if (!node.isInteger()) return info.newUnboundRange();
HInstruction right = node.right;
HInstruction left = node.left;
- if (left.isInteger(types) && right.isInteger(types)) {
+ if (left.isInteger() && right.isInteger()) {
return ranges[left] & ranges[right];
}
@@ -765,9 +764,9 @@
return info.newUnboundRange();
}
- if (left.isInteger(types)) {
+ if (left.isInteger()) {
return tryComputeRange(left);
- } else if (right.isInteger(types)) {
+ } else if (right.isInteger()) {
return tryComputeRange(right);
}
return info.newUnboundRange();
@@ -835,8 +834,8 @@
if (condition is HIdentity) return info.newUnboundRange();
HInstruction right = condition.right;
HInstruction left = condition.left;
- if (!left.isInteger(types)) return info.newUnboundRange();
- if (!right.isInteger(types)) return info.newUnboundRange();
+ if (!left.isInteger()) return info.newUnboundRange();
+ if (!right.isInteger()) return info.newUnboundRange();
Range rightRange = ranges[right];
Range leftRange = ranges[left];
@@ -899,9 +898,8 @@
class LoopUpdateRecognizer extends HBaseVisitor {
final HPhi loopPhi;
final Map<HInstruction, Range> ranges;
- final HTypeMap types;
final ValueRangeInfo info;
- LoopUpdateRecognizer(this.loopPhi, this.ranges, this.types, this.info);
+ LoopUpdateRecognizer(this.loopPhi, this.ranges, this.info);
Range visitAdd(HAdd operation) {
Range range = getRangeForRecognizableOperation(operation);
@@ -955,8 +953,8 @@
* Otherwise returns [null].
*/
Range getRangeForRecognizableOperation(HBinaryArithmetic operation) {
- if (!operation.left.isInteger(types)) return null;
- if (!operation.right.isInteger(types)) return null;
+ if (!operation.left.isInteger()) return null;
+ if (!operation.right.isInteger()) return null;
HInstruction left = unwrap(operation.left);
HInstruction right = unwrap(operation.right);
// We only recognize operations that operate on the loop phi.
diff --git a/sdk/lib/_internal/compiler/implementation/tree/dartstring.dart b/sdk/lib/_internal/compiler/implementation/tree/dartstring.dart
index 5a3494d..70e2d69 100644
--- a/sdk/lib/_internal/compiler/implementation/tree/dartstring.dart
+++ b/sdk/lib/_internal/compiler/implementation/tree/dartstring.dart
@@ -100,7 +100,7 @@
StringBuffer buffer = new StringBuffer();
StringEscapeIterator it = new StringEscapeIterator(source);
while (it.moveNext()) {
- buffer.addCharCode(it.current);
+ buffer.writeCharCode(it.current);
}
toStringCache = buffer.toString();
return toStringCache;
diff --git a/sdk/lib/_internal/compiler/implementation/tree/prettyprint.dart b/sdk/lib/_internal/compiler/implementation/tree/prettyprint.dart
index 129c637..62b6744 100644
--- a/sdk/lib/_internal/compiler/implementation/tree/prettyprint.dart
+++ b/sdk/lib/_internal/compiler/implementation/tree/prettyprint.dart
@@ -54,10 +54,10 @@
void openNode(Node node, String type, [Map params]) {
if (params == null) params = new Map();
addCurrentIndent();
- sb.add("<");
+ sb.write("<");
addBeginAndEndTokensToParams(node, params);
addTypeWithParams(type, params);
- sb.add(">\n");
+ sb.write(">\n");
pushTag(type);
}
@@ -67,10 +67,10 @@
void openAndCloseNode(Node node, String type, [Map params]) {
if (params == null) params = new Map();
addCurrentIndent();
- sb.add("<");
+ sb.write("<");
addBeginAndEndTokensToParams(node, params);
addTypeWithParams(type, params);
- sb.add("/>\n");
+ sb.write("/>\n");
}
/**
@@ -79,14 +79,14 @@
void closeNode() {
String tag = popTag();
addCurrentIndent();
- sb.add("</");
+ sb.write("</");
addTypeWithParams(tag);
- sb.add(">\n");
+ sb.write(">\n");
}
void addTypeWithParams(String type, [Map params]) {
if (params == null) params = new Map();
- sb.add("${type}");
+ sb.write("${type}");
params.forEach((k, v) {
String value;
if (v != null) {
@@ -97,12 +97,12 @@
} else {
value = "[null]";
}
- sb.add(' $k="$value"');
+ sb.write(' $k="$value"');
});
}
void addCurrentIndent() {
- tagStack.forEach((_) { sb.add(INDENT); });
+ tagStack.forEach((_) { sb.write(INDENT); });
}
/**
@@ -318,12 +318,12 @@
visitChildNode(Node node, String fieldName) {
if (node == null) return;
addCurrentIndent();
- sb.add("<$fieldName>\n");
+ sb.write("<$fieldName>\n");
pushTag(fieldName);
node.accept(this);
popTag();
addCurrentIndent();
- sb.add("</$fieldName>\n");
+ sb.write("</$fieldName>\n");
}
openSendNodeWithFields(Send node, String type) {
diff --git a/sdk/lib/_internal/compiler/implementation/tree/unparser.dart b/sdk/lib/_internal/compiler/implementation/tree/unparser.dart
index 9c20f2d..46eb2bf 100644
--- a/sdk/lib/_internal/compiler/implementation/tree/unparser.dart
+++ b/sdk/lib/_internal/compiler/implementation/tree/unparser.dart
@@ -26,7 +26,7 @@
add(token.value);
if (identical(token.kind, KEYWORD_TOKEN)
|| identical(token.kind, IDENTIFIER_TOKEN)) {
- sb.add(' ');
+ sb.write(' ');
}
}
@@ -58,23 +58,23 @@
visit(node.typeParameters);
}
if (node.extendsKeyword != null) {
- sb.add(' ');
+ sb.write(' ');
addToken(node.extendsKeyword);
visit(node.superclass);
}
if (!node.interfaces.isEmpty) {
- sb.add(' ');
+ sb.write(' ');
visit(node.interfaces);
}
if (node.defaultClause != null) {
- sb.add(' default ');
+ sb.write(' default ');
visit(node.defaultClause);
}
- sb.add('{');
+ sb.write('{');
for (final member in members) {
visit(member);
}
- sb.add('}');
+ sb.write('}');
}
visitClassNode(ClassNode node) {
@@ -83,27 +83,27 @@
visitMixinApplication(MixinApplication node) {
visit(node.superclass);
- sb.add(' with ');
+ sb.write(' with ');
visit(node.mixins);
}
visitNamedMixinApplication(NamedMixinApplication node) {
- sb.add('typedef ');
+ sb.write('typedef ');
visit(node.name);
if (node.typeParameters != null) {
visit(node.typeParameters);
}
- sb.add(' = ');
+ sb.write(' = ');
if (!node.modifiers.nodes.isEmpty) {
visit(node.modifiers);
- sb.add(' ');
+ sb.write(' ');
}
visit(node.mixinApplication);
if (node.interfaces != null) {
- sb.add(' implements ');
+ sb.write(' implements ');
visit(node.interfaces);
}
- sb.add(';');
+ sb.write(';');
}
visitConditional(Conditional node) {
@@ -121,12 +121,12 @@
visitFor(For node) {
add(node.forToken.value);
- sb.add('(');
+ sb.write('(');
visit(node.initializer);
- sb.add(';');
+ sb.write(';');
visit(node.conditionStatement);
visit(node.update);
- sb.add(')');
+ sb.write(')');
visit(node.body);
}
@@ -145,15 +145,15 @@
if (!send.isOperator) {
// Looks like a factory method.
visit(send.receiver);
- sb.add('.');
+ sb.write('.');
} else {
visit(send.receiver);
Identifier identifier = send.selector.asIdentifier();
if (identical(identifier.token.kind, KEYWORD_TOKEN)) {
- sb.add(' ');
+ sb.write(' ');
} else if (identifier.source == const SourceString('negate')) {
// TODO(ahe): Remove special case for negate.
- sb.add(' ');
+ sb.write(' ');
}
}
visit(send.selector);
@@ -165,15 +165,15 @@
visitFunctionExpression(FunctionExpression node) {
if (!node.modifiers.nodes.isEmpty) {
visit(node.modifiers);
- sb.add(' ');
+ sb.write(' ');
}
if (node.returnType != null) {
visit(node.returnType);
- sb.add(' ');
+ sb.write(' ');
}
if (node.getOrSet != null) {
add(node.getOrSet.value);
- sb.add(' ');
+ sb.write(' ');
}
unparseFunctionName(node.name);
visit(node.parameters);
@@ -191,7 +191,7 @@
visit(node.thenPart);
if (node.hasElsePart) {
add(node.elseToken.value);
- if (node.elsePart is !Block) sb.add(' ');
+ if (node.elsePart is !Block) sb.write(' ');
visit(node.elsePart);
}
}
@@ -218,7 +218,7 @@
visitStringJuxtaposition(StringJuxtaposition node) {
visit(node.first);
- sb.add(" ");
+ sb.write(" ");
visit(node.second);
}
@@ -236,7 +236,7 @@
visit(node.typeArguments);
visit(node.elements);
// If list is empty, emit space after [] to disambiguate cases like []==[].
- if (node.elements.isEmpty) sb.add(' ');
+ if (node.elements.isEmpty) sb.write(' ');
}
visitModifiers(Modifiers node) => node.visitChildren(this);
@@ -249,7 +249,7 @@
String delimiter = (node.delimiter == null) ? "" : "${node.delimiter}";
visit(from.head);
for (Link link = from.tail; !link.isEmpty; link = link.tail) {
- sb.add(delimiter);
+ sb.write(delimiter);
visit(link.head);
}
}
@@ -268,11 +268,11 @@
visitReturn(Return node) {
if (node.isRedirectingFactoryBody) {
- sb.add(' ');
+ sb.write(' ');
}
add(node.beginToken.value);
if (node.hasExpression && node.beginToken.stringValue != '=>') {
- sb.add(' ');
+ sb.write(' ');
}
visit(node.expression);
if (node.endToken != null) add(node.endToken.value);
@@ -285,9 +285,9 @@
if (asCascadeReceiver != null) {
add(asCascadeReceiver.cascadeOperator.value);
} else if (node.selector.asOperator() == null) {
- sb.add('.');
+ sb.write('.');
} else if (spacesNeeded) {
- sb.add(' ');
+ sb.write(' ');
}
}
@@ -299,14 +299,14 @@
if (node.isPrefix) visit(node.selector);
unparseSendReceiver(node, spacesNeeded: spacesNeeded);
if (!node.isPrefix && !node.isIndex) visit(node.selector);
- if (spacesNeeded) sb.add(' ');
+ if (spacesNeeded) sb.write(' ');
// Also add a space for sequences like x + +1 and y - -y.
// TODO(ahe): remove case for '+' when we drop the support for it.
if (node.argumentsNode != null && (identical(opString, '-')
|| identical(opString, '+'))) {
Token beginToken = node.argumentsNode.getBeginToken();
if (beginToken != null && identical(beginToken.stringValue, opString)) {
- sb.add(' ');
+ sb.write(' ');
}
}
visit(node.argumentsNode);
@@ -314,21 +314,21 @@
visitSendSet(SendSet node) {
if (node.isPrefix) {
- sb.add(' ');
+ sb.write(' ');
visit(node.assignmentOperator);
}
unparseSendReceiver(node);
if (node.isIndex) {
- sb.add('[');
+ sb.write('[');
visit(node.arguments.head);
- sb.add(']');
+ sb.write(']');
if (!node.isPrefix) visit(node.assignmentOperator);
unparseNodeListFrom(node.argumentsNode, node.argumentsNode.nodes.tail);
} else {
visit(node.selector);
if (!node.isPrefix) {
visit(node.assignmentOperator);
- if (node.assignmentOperator.source.slowToString() != '=') sb.add(' ');
+ if (node.assignmentOperator.source.slowToString() != '=') sb.write(' ');
}
visit(node.argumentsNode);
}
@@ -337,7 +337,7 @@
visitThrow(Throw node) {
add(node.throwToken.value);
if (node.expression != null) {
- sb.add(' ');
+ sb.write(' ');
visit(node.expression);
}
node.endToken.value.printOn(sb);
@@ -351,7 +351,7 @@
visitTypeVariable(TypeVariable node) {
visit(node.name);
if (node.bound != null) {
- sb.add(' extends ');
+ sb.write(' extends ');
visit(node.bound);
}
}
@@ -359,22 +359,22 @@
visitVariableDefinitions(VariableDefinitions node) {
visit(node.modifiers);
if (!node.modifiers.nodes.isEmpty) {
- sb.add(' ');
+ sb.write(' ');
}
if (node.type != null) {
visit(node.type);
- sb.add(' ');
+ sb.write(' ');
}
visit(node.definitions);
}
visitDoWhile(DoWhile node) {
add(node.doKeyword.value);
- if (node.body is !Block) sb.add(' ');
+ if (node.body is !Block) sb.write(' ');
visit(node.body);
add(node.whileKeyword.value);
visit(node.condition);
- sb.add(node.endToken.value);
+ sb.write(node.endToken.value);
}
visitWhile(While node) {
@@ -395,9 +395,9 @@
}
visitStringInterpolationPart(StringInterpolationPart node) {
- sb.add('\${'); // TODO(ahe): Preserve the real tokens.
+ sb.write('\${'); // TODO(ahe): Preserve the real tokens.
visit(node.expression);
- sb.add('}');
+ sb.write('}');
visit(node.string);
}
@@ -408,7 +408,7 @@
visitGotoStatement(GotoStatement node) {
add(node.keywordToken.value);
if (node.target != null) {
- sb.add(' ');
+ sb.write(' ');
visit(node.target);
}
add(node.semicolonToken.value);
@@ -424,12 +424,12 @@
visitForIn(ForIn node) {
add(node.forToken.value);
- sb.add('(');
+ sb.write('(');
visit(node.declaredIdentifier);
- sb.add(' ');
+ sb.write(' ');
addToken(node.inToken);
visit(node.expression);
- sb.add(')');
+ sb.write(')');
visit(node.body);
}
@@ -470,27 +470,27 @@
visitSwitchCase(SwitchCase node) {
visit(node.labelsAndCases);
if (node.isDefaultCase) {
- sb.add('default:');
+ sb.write('default:');
}
visit(node.statements);
}
unparseImportTag(String uri, [String prefix]) {
final suffix = prefix == null ? '' : ' as $prefix';
- sb.add('import "$uri"$suffix;');
+ sb.write('import "$uri"$suffix;');
}
visitScriptTag(ScriptTag node) {
add(node.beginToken.value);
visit(node.tag);
- sb.add('(');
+ sb.write('(');
visit(node.argument);
if (node.prefixIdentifier != null) {
visit(node.prefixIdentifier);
- sb.add(':');
+ sb.write(':');
visit(node.prefix);
}
- sb.add(')');
+ sb.write(')');
add(node.endToken.value);
}
@@ -506,7 +506,7 @@
visitCaseMatch(CaseMatch node) {
add(node.caseKeyword.value);
- sb.add(" ");
+ sb.write(" ");
visit(node.expression);
add(node.colonToken.value);
}
@@ -515,7 +515,7 @@
addToken(node.onKeyword);
if (node.type != null) {
visit(node.type);
- sb.add(' ');
+ sb.write(' ');
}
addToken(node.catchKeyword);
visit(node.formals);
@@ -526,7 +526,7 @@
addToken(node.typedefKeyword);
if (node.returnType != null) {
visit(node.returnType);
- sb.add(' ');
+ sb.write(' ');
}
visit(node.name);
if (node.typeParameters != null) {
@@ -546,12 +546,12 @@
addToken(node.importKeyword);
visit(node.uri);
if (node.prefix != null) {
- sb.add(' ');
+ sb.write(' ');
addToken(node.asKeyword);
visit(node.prefix);
}
if (node.combinators != null) {
- sb.add(' ');
+ sb.write(' ');
visit(node.combinators);
}
add(node.getEndToken().value);
@@ -561,7 +561,7 @@
addToken(node.exportKeyword);
visit(node.uri);
if (node.combinators != null) {
- sb.add(' ');
+ sb.write(' ');
visit(node.combinators);
}
add(node.getEndToken().value);
diff --git a/sdk/lib/_internal/compiler/implementation/typechecker.dart b/sdk/lib/_internal/compiler/implementation/typechecker.dart
index 76f0c1b..6e79532 100644
--- a/sdk/lib/_internal/compiler/implementation/typechecker.dart
+++ b/sdk/lib/_internal/compiler/implementation/typechecker.dart
@@ -33,6 +33,80 @@
CancelTypeCheckException(this.node, this.reason);
}
+/**
+ * [ElementAccess] represents the access of [element], either as a property
+ * access or invocation.
+ */
+abstract class ElementAccess {
+ Element get element;
+
+ DartType computeType(Compiler compiler);
+
+ /// Returns [: true :] if the element can be access as an invocation.
+ bool isCallable(Compiler compiler) {
+ if (element.isAbstractField()) {
+ AbstractFieldElement abstractFieldElement = element;
+ if (abstractFieldElement.getter == null) {
+ // Setters cannot be invoked as function invocations.
+ return false;
+ }
+ }
+ return compiler.types.isAssignable(
+ computeType(compiler), compiler.functionClass.computeType(compiler));
+ }
+}
+
+/// An access of a instance member.
+class MemberAccess extends ElementAccess {
+ final Member member;
+
+ MemberAccess(Member this.member);
+
+ Element get element => member.element;
+
+ DartType computeType(Compiler compiler) => member.computeType(compiler);
+}
+
+/// An access of an unresolved element.
+class DynamicAccess implements ElementAccess {
+ const DynamicAccess();
+
+ Element get element => null;
+
+ DartType computeType(Compiler compiler) => compiler.types.dynamicType;
+
+ bool isCallable(Compiler compiler) => true;
+}
+
+/**
+ * An access of a resolved top-level or static property or function, or an
+ * access of a resolved element through [:this:].
+ */
+class ResolvedAccess extends ElementAccess {
+ final Element element;
+
+ ResolvedAccess(Element this.element) {
+ assert(element != null);
+ }
+
+ DartType computeType(Compiler compiler) => element.computeType(compiler);
+}
+
+/**
+ * An access of a resolved top-level or static property or function, or an
+ * access of a resolved element through [:this:].
+ */
+class TypeAccess extends ElementAccess {
+ final DartType type;
+ TypeAccess(DartType this.type) {
+ assert(type != null);
+ }
+
+ Element get element => type.element;
+
+ DartType computeType(Compiler compiler) => type;
+}
+
class TypeCheckerVisitor implements Visitor<DartType> {
final Compiler compiler;
final TreeElements elements;
@@ -238,37 +312,26 @@
return unhandledStatement();
}
- DartType lookupMethodType(Node node, ClassElement classElement,
- SourceString name) {
- Element member = classElement.lookupLocalMember(name);
- if (member == null) {
- classElement.ensureResolved(compiler);
- for (Link<DartType> supertypes = classElement.allSupertypes;
- !supertypes.isEmpty && member == null;
- supertypes = supertypes.tail) {
- ClassElement lookupTarget = supertypes.head.element;
- member = lookupTarget.lookupLocalMember(name);
- }
+ ElementAccess lookupMethod(Node node, DartType type, SourceString name) {
+ if (identical(type, types.dynamicType)) {
+ return const DynamicAccess();
}
- if (member != null && member.kind == ElementKind.FUNCTION) {
- return computeType(member);
+ Member member = type.lookupMember(name);
+ if (member != null) {
+ return new MemberAccess(member);
}
reportTypeWarning(node, MessageKind.METHOD_NOT_FOUND,
- {'className': classElement.name, 'memberName': name});
- return types.dynamicType;
+ {'className': type.name, 'memberName': name});
+ return const DynamicAccess();
}
// TODO(johnniwinther): Provide the element from which the type came in order
// to give better error messages.
void analyzeArguments(Send send, DartType type) {
Link<Node> arguments = send.arguments;
- if (type == null || identical(type, types.dynamicType)) {
- while(!arguments.isEmpty) {
- analyze(arguments.head);
- arguments = arguments.tail;
- }
- } else {
- FunctionType funType = type;
+ DartType unaliasedType = type.unalias(compiler);
+ if (identical(unaliasedType.kind, TypeKind.FUNCTION)) {
+ FunctionType funType = unaliasedType;
Link<DartType> parameterTypes = funType.parameterTypes;
Link<DartType> optionalParameterTypes = funType.optionalParameterTypes;
while (!arguments.isEmpty) {
@@ -315,6 +378,28 @@
reportTypeWarning(send, MessageKind.MISSING_ARGUMENT,
{'argumentType': parameterTypes.head});
}
+ } else {
+ while(!arguments.isEmpty) {
+ analyze(arguments.head);
+ arguments = arguments.tail;
+ }
+ }
+ }
+
+ DartType analyzeInvocation(Send node, ElementAccess elementAccess) {
+ DartType type = elementAccess.computeType(compiler);
+ if (elementAccess.isCallable(compiler)) {
+ analyzeArguments(node, type);
+ } else {
+ reportTypeWarning(node, MessageKind.NOT_CALLABLE,
+ {'elementName': elementAccess.element.name});
+ analyzeArguments(node, types.dynamicType);
+ }
+ if (identical(type.kind, TypeKind.FUNCTION)) {
+ FunctionType funType = type;
+ return funType.returnType;
+ } else {
+ return types.dynamicType;
}
}
@@ -322,8 +407,14 @@
Element element = elements[node];
if (Elements.isClosureSend(node, element)) {
- // TODO(karlklose): Finish implementation.
- return types.dynamicType;
+ if (element != null) {
+ // foo() where foo is a local or a parameter.
+ return analyzeInvocation(node, new ResolvedAccess(element));
+ } else {
+ // exp() where exp is some complex expression like (o) or foo().
+ DartType type = analyze(node.selector);
+ return analyzeInvocation(node, new TypeAccess(type));
+ }
}
Identifier selector = node.selector.asIdentifier();
@@ -340,17 +431,22 @@
final DartType secondArgumentType =
analyzeWithDefault(secondArgument, null);
- if (identical(name, '+') || identical(name, '=') || identical(name, '-')
- || identical(name, '*') || identical(name, '/') || identical(name, '%')
- || identical(name, '~/') || identical(name, '|') || identical(name, '&')
- || identical(name, '^') || identical(name, '~')|| identical(name, '<<')
- || identical(name, '>>') || identical(name, '[]')) {
+ if (identical(name, '+') || identical(name, '=') ||
+ identical(name, '-') || identical(name, '*') ||
+ identical(name, '/') || identical(name, '%') ||
+ identical(name, '~/') || identical(name, '|') ||
+ identical(name, '&') || identical(name, '^') ||
+ identical(name, '~')|| identical(name, '<<') ||
+ identical(name, '>>') || identical(name, '[]')) {
return types.dynamicType;
- } else if (identical(name, '<') || identical(name, '>') || identical(name, '<=')
- || identical(name, '>=') || identical(name, '==') || identical(name, '!=')
- || identical(name, '===') || identical(name, '!==')) {
+ } else if (identical(name, '<') || identical(name, '>') ||
+ identical(name, '<=') || identical(name, '>=') ||
+ identical(name, '==') || identical(name, '!=') ||
+ identical(name, '===') || identical(name, '!==')) {
return boolType;
- } else if (identical(name, '||') || identical(name, '&&') || identical(name, '!')) {
+ } else if (identical(name, '||') ||
+ identical(name, '&&') ||
+ identical(name, '!')) {
checkAssignable(firstArgument, boolType, firstArgumentType);
if (!arguments.isEmpty) {
// TODO(karlklose): check number of arguments in validator.
@@ -370,56 +466,46 @@
} else if (node.isFunctionObjectInvocation) {
fail(node.receiver, 'function object invocation unimplemented');
-
} else {
- FunctionType computeFunType() {
+ ElementAccess computeMethod() {
if (node.receiver != null) {
+ // e.foo() for some expression e.
DartType receiverType = analyze(node.receiver);
- if (receiverType.element == compiler.dynamicClass) return null;
+ if (receiverType.element == compiler.dynamicClass) {
+ return const DynamicAccess();
+ }
if (receiverType == null) {
- fail(node.receiver, 'receivertype is null');
+ fail(node.receiver, 'receiverType is null');
}
- if (identical(receiverType.element.kind, ElementKind.GETTER)) {
- FunctionType getterType = receiverType;
- receiverType = getterType.returnType;
- }
- ElementKind receiverKind = receiverType.element.kind;
- if (identical(receiverKind, ElementKind.TYPEDEF)) {
+ TypeKind receiverKind = receiverType.kind;
+ if (identical(receiverKind, TypeKind.TYPEDEF)) {
// TODO(karlklose): handle typedefs.
- return null;
+ return const DynamicAccess();
}
- if (identical(receiverKind, ElementKind.TYPE_VARIABLE)) {
+ if (identical(receiverKind, TypeKind.TYPE_VARIABLE)) {
// TODO(karlklose): handle type variables.
- return null;
+ return const DynamicAccess();
}
- if (!identical(receiverKind, ElementKind.CLASS)) {
+ if (!identical(receiverKind, TypeKind.INTERFACE)) {
fail(node.receiver, 'unexpected receiver kind: ${receiverKind}');
}
- ClassElement classElement = receiverType.element;
- // TODO(karlklose): substitute type arguments.
- DartType memberType =
- lookupMethodType(selector, classElement, selector.source);
- if (identical(memberType.element, compiler.dynamicClass)) return null;
- return memberType;
+ return lookupMethod(selector, receiverType, selector.source);
} else {
if (Elements.isUnresolved(element)) {
- fail(node, 'unresolved ${node.selector}');
- } else if (identical(element.kind, ElementKind.FUNCTION)) {
- return computeType(element);
- } else if (element.isForeign(compiler)) {
- return null;
- } else if (identical(element.kind, ElementKind.VARIABLE)
- || identical(element.kind, ElementKind.FIELD)) {
- // TODO(karlklose): handle object invocations.
- return null;
+ // foo() where foo is unresolved.
+ return const DynamicAccess();
+ } else if (element.isFunction()) {
+ // foo() where foo is a method in the same class.
+ return new ResolvedAccess(element);
+ } else if (element.isVariable() || element.isField()) {
+ // foo() where foo is a field in the same class.
+ return new ResolvedAccess(element);
} else {
fail(node, 'unexpected element kind ${element.kind}');
}
}
}
- FunctionType funType = computeFunType();
- analyzeArguments(node, funType);
- return (funType != null) ? funType.returnType : types.dynamicType;
+ return analyzeInvocation(node, computeMethod());
}
}
diff --git a/sdk/lib/_internal/compiler/implementation/types/concrete_types_inferrer.dart b/sdk/lib/_internal/compiler/implementation/types/concrete_types_inferrer.dart
index f268488..8fde89a 100644
--- a/sdk/lib/_internal/compiler/implementation/types/concrete_types_inferrer.dart
+++ b/sdk/lib/_internal/compiler/implementation/types/concrete_types_inferrer.dart
@@ -39,7 +39,9 @@
return element == other.element;
}
int get hashCode => element.hashCode;
- String toString() => element.name.slowToString();
+ String toString() {
+ return element == null ? 'toplevel' : element.name.slowToString();
+ }
bool isClass() => true;
bool isUnknown() => false;
bool isNull() => false;
@@ -287,20 +289,13 @@
final ClassBaseType objectBaseType;
final ClassBaseType typeBaseType;
- static _getNativeListClass(Compiler compiler) {
- // TODO(polux): switch to other implementations on other backends
- JavaScriptBackend backend = compiler.backend;
- return backend.jsArrayClass;
- }
-
BaseTypes(Compiler compiler) :
intBaseType = new ClassBaseType(compiler.intClass),
doubleBaseType = new ClassBaseType(compiler.doubleClass),
numBaseType = new ClassBaseType(compiler.numClass),
boolBaseType = new ClassBaseType(compiler.boolClass),
stringBaseType = new ClassBaseType(compiler.stringClass),
- // in the Javascript backend, lists are implemented by JsArray
- listBaseType = new ClassBaseType(_getNativeListClass(compiler)),
+ listBaseType = new ClassBaseType(compiler.listClass),
mapBaseType = new ClassBaseType(compiler.mapClass),
objectBaseType = new ClassBaseType(compiler.objectClass),
typeBaseType = new ClassBaseType(compiler.typeClass);
@@ -454,6 +449,15 @@
/** [: readers[field] :] is the list of [: field :]'s possible readers. */
final Map<Element, Set<FunctionElement>> readers;
+ /// The set of classes encountered so far.
+ final Set<ClassElement> seenClasses;
+
+ /**
+ * A map from method names to callers of methods with this name on objects
+ * of unknown inferred type.
+ */
+ final Map<SourceString, Set<FunctionElement>> dynamicCallers;
+
/** The inferred type of elements stored in Lists. */
ConcreteType listElementType;
@@ -473,7 +477,9 @@
workQueue = new Queue<InferenceWorkItem>(),
callers = new Map<FunctionElement, Set<FunctionElement>>(),
readers = new Map<Element, Set<FunctionElement>>(),
- listElementType = new ConcreteType.empty() {
+ listElementType = new ConcreteType.empty(),
+ seenClasses = new Set<ClassElement>(),
+ dynamicCallers = new Map<SourceString, Set<FunctionElement>>() {
unknownConcreteType = new ConcreteType.unknown();
emptyConcreteType = new ConcreteType.empty();
}
@@ -509,7 +515,8 @@
// resolve num for some reason.
receiverType.element.ensureResolved(compiler);
FunctionElement methodElement =
- receiverType.element.lookupMember(new SourceString(method));
+ receiverType.element.lookupMember(new SourceString(method))
+ .implementation;
ConcreteTypesEnvironment environment =
makeEnvironment(receiverType, methodElement, argumentType);
Map<ConcreteTypesEnvironment, ConcreteType> map =
@@ -563,10 +570,10 @@
List<Element> getMembersByName(SourceString methodName) {
// TODO(polux): memoize?
var result = new List<Element>();
- for (ClassElement cls in compiler.enqueuer.resolution.seenClasses) {
+ for (ClassElement cls in seenClasses) {
Element elem = cls.lookupLocalMember(methodName);
if (elem != null) {
- result.add(elem);
+ result.add(elem.implementation);
}
}
return result;
@@ -635,6 +642,20 @@
(oldType == null) ? type : union(oldType, type);
}
+ /// Augments the set of classes encountered so far.
+ void augmentSeenClasses(ClassElement cls) {
+ if (!seenClasses.contains(cls)) {
+ seenClasses.add(cls);
+ cls.forEachLocalMember((Element member) {
+ Set<FunctionElement> functions = dynamicCallers[member.name];
+ if (functions == null) return;
+ for (FunctionElement function in functions) {
+ invalidate(function);
+ }
+ });
+ }
+ }
+
/**
* Add [caller] to the set of [callee]'s callers.
*/
@@ -650,6 +671,20 @@
}
/**
+ * Add [caller] to the set of [callee]'s dynamic callers.
+ */
+ void addDynamicCaller(SourceString callee, FunctionElement caller) {
+ Set<FunctionElement> current = dynamicCallers[callee];
+ if (current != null) {
+ current.add(caller);
+ } else {
+ Set<FunctionElement> newSet = new Set<FunctionElement>();
+ newSet.add(caller);
+ dynamicCallers[callee] = newSet;
+ }
+ }
+
+ /**
* Add [reader] to the set of [field]'s readers.
*/
void addReader(Element field, FunctionElement reader) {
@@ -670,14 +705,20 @@
Set<FunctionElement> methodCallers = callers[function];
if (methodCallers == null) return;
for (FunctionElement caller in methodCallers) {
- Map<ConcreteTypesEnvironment, ConcreteType> callerInstances =
- cache[caller];
- if (callerInstances != null) {
- callerInstances.forEach((environment, _) {
- workQueue.addLast(
- new InferenceWorkItem(caller, environment));
- });
- }
+ invalidate(caller);
+ }
+ }
+
+ /**
+ * Add all instances of method to the workqueue.
+ */
+ void invalidate(FunctionElement method) {
+ Map<ConcreteTypesEnvironment, ConcreteType> instances = cache[method];
+ if (instances != null) {
+ instances.forEach((environment, _) {
+ workQueue.addLast(
+ new InferenceWorkItem(method, environment));
+ });
}
}
@@ -844,7 +885,10 @@
*/
ConcreteType getSpecialCaseReturnType(FunctionElement function,
ConcreteTypesEnvironment environment) {
- if (function == listIndex) {
+ if (function.name == const SourceString('JS')) {
+ // TODO(polux): trust the type once we handle generic types
+ return unknownConcreteType;
+ } else if (function == listIndex) {
ConcreteType indexType = environment.lookupType(
listIndex.functionSignature.requiredParameters.head);
if (!indexType.baseTypes.contains(baseTypes.intBaseType)) {
@@ -875,7 +919,7 @@
ConcreteType analyzeMethod(FunctionElement element,
ConcreteTypesEnvironment environment) {
TreeElements elements =
- compiler.enqueuer.resolution.resolvedElements[element];
+ compiler.enqueuer.resolution.resolvedElements[element.declaration];
ConcreteType specialResult = handleSpecialMethod(element, environment);
if (specialResult != null) return specialResult;
FunctionExpression tree = element.parseNode(compiler);
@@ -884,8 +928,8 @@
new TypeInferrerVisitor(elements, element, this, environment);
return tree.accept(visitor);
} else {
- // TODO(polux): implement visitForeingCall and always use the
- // implementation element instead of this hack
+ // TODO(polux): handle num#<, num#>, etc. in order to get rid of this
+ // else branch
return new ConcreteType.unknown();
}
}
@@ -893,6 +937,7 @@
ConcreteType analyzeConstructor(FunctionElement element,
ConcreteTypesEnvironment environment) {
ClassElement enclosingClass = element.enclosingElement;
+ augmentSeenClasses(enclosingClass);
FunctionExpression tree = compiler.parser.parse(element);
TreeElements elements =
compiler.enqueuer.resolution.resolvedElements[element];
@@ -926,7 +971,8 @@
ClassElement superClass = enclosingClass.superclass;
if (enclosingClass != compiler.objectClass) {
FunctionElement target = superClass.lookupConstructor(
- new Selector.callDefaultConstructor(enclosingClass.getLibrary()));
+ new Selector.callDefaultConstructor(enclosingClass.getLibrary()))
+ .implementation;
final superClassConcreteType = singletonConcreteType(
new ClassBaseType(enclosingClass));
getSendReturnType(target, enclosingClass,
@@ -1005,11 +1051,15 @@
* Dumps debugging information on the standard output.
*/
void debug() {
- print("callers :");
+ print("seen classes:");
+ for (ClassElement cls in seenClasses) {
+ print(" ${cls.name.slowToString()}");
+ }
+ print("callers:");
callers.forEach((k,v) {
print(" $k: $v");
});
- print("readers :");
+ print("readers:");
readers.forEach((k,v) {
print(" $k: $v");
});
@@ -1025,7 +1075,7 @@
cache.forEach((k,v) {
print(" $k: $v");
});
- print("inferred expression types: ");
+ print("inferred expression types:");
inferredTypes.forEach((k,v) {
print(" $k: $v");
});
@@ -1225,6 +1275,7 @@
}
if (receiverType.isUnkown()) {
+ inferrer.addDynamicCaller(name, currentMethod);
for (Element member in inferrer.getMembersByName(name)) {
if (!(member.isField() || member.isAbstractField())) continue;
Element cls = member.getEnclosingClass();
@@ -1304,14 +1355,19 @@
}
ConcreteType visitLiteralInt(LiteralInt node) {
+ inferrer.augmentSeenClasses(inferrer.compiler.intClass);
+ inferrer.augmentSeenClasses(inferrer.compiler.numClass);
return inferrer.singletonConcreteType(inferrer.baseTypes.intBaseType);
}
ConcreteType visitLiteralDouble(LiteralDouble node) {
+ inferrer.augmentSeenClasses(inferrer.compiler.doubleClass);
+ inferrer.augmentSeenClasses(inferrer.compiler.numClass);
return inferrer.singletonConcreteType(inferrer.baseTypes.doubleBaseType);
}
ConcreteType visitLiteralBool(LiteralBool node) {
+ inferrer.augmentSeenClasses(inferrer.compiler.boolClass);
return inferrer.singletonConcreteType(inferrer.baseTypes.boolBaseType);
}
@@ -1322,6 +1378,7 @@
&& node.dartString.slowToString() == "__dynamic_for_test") {
return inferrer.unknownConcreteType;
}
+ inferrer.augmentSeenClasses(inferrer.compiler.stringClass);
return inferrer.singletonConcreteType(inferrer.baseTypes.stringBaseType);
}
@@ -1352,6 +1409,7 @@
elementsType = inferrer.union(elementsType, analyze(link.head));
}
inferrer.augmentListElementType(elementsType);
+ inferrer.augmentSeenClasses(inferrer.compiler.listClass);
return inferrer.singletonConcreteType(inferrer.baseTypes.listBaseType);
}
@@ -1427,11 +1485,13 @@
ConcreteType visitStringInterpolation(StringInterpolation node) {
node.visitChildren(this);
+ inferrer.augmentSeenClasses(inferrer.compiler.stringClass);
return inferrer.singletonConcreteType(inferrer.baseTypes.stringBaseType);
}
ConcreteType visitStringInterpolationPart(StringInterpolationPart node) {
node.visitChildren(this);
+ inferrer.augmentSeenClasses(inferrer.compiler.stringClass);
return inferrer.singletonConcreteType(inferrer.baseTypes.stringBaseType);
}
@@ -1462,6 +1522,7 @@
ConcreteType visitLiteralMap(LiteralMap node) {
visitNodeList(node.entries);
+ inferrer.augmentSeenClasses(inferrer.compiler.mapClass);
return inferrer.singletonConcreteType(inferrer.baseTypes.mapBaseType);
}
@@ -1566,8 +1627,9 @@
ConcreteType receiverType = analyze(node.receiver);
if (receiverType.isUnkown()) {
- List<Element> members =
- inferrer.getMembersByName(node.selector.asIdentifier().source);
+ SourceString name = node.selector.asIdentifier().source;
+ inferrer.addDynamicCaller(name, currentMethod);
+ List<Element> members = inferrer.getMembersByName(name);
for (Element member in members) {
if (!(member.isField() || member.isAbstractField())) continue;
Element cls = member.getEnclosingClass();
@@ -1581,7 +1643,7 @@
Element getterOrField =
cls.lookupMember(node.selector.asIdentifier().source);
if (getterOrField != null) {
- augmentResult(cls, getterOrField);
+ augmentResult(cls, getterOrField.implementation);
}
}
}
@@ -1600,6 +1662,7 @@
ConcreteType result = inferrer.emptyConcreteType;
if (receiverType.isUnkown()) {
+ inferrer.addDynamicCaller(canonicalizedMethodName, currentMethod);
List<Element> methods =
inferrer.getMembersByName(canonicalizedMethodName);
for (Element element in methods) {
@@ -1621,6 +1684,7 @@
ClassElement cls = classBaseReceiverType.element;
FunctionElement method = cls.lookupMember(canonicalizedMethodName);
if (method != null) {
+ method = method.implementation;
inferrer.addCaller(method, currentMethod);
result = inferrer.union(
result,
@@ -1661,11 +1725,11 @@
}
ConcreteType visitForeignSend(Send node) {
- inferrer.fail(node, 'not implemented');
+ return inferrer.unknownConcreteType;
}
ConcreteType visitStaticSend(Send node) {
- Element element = elements[node];
+ Element element = elements[node].implementation;
inferrer.addCaller(element, currentMethod);
return inferrer.getSendReturnType(element, null,
analyzeArguments(node.arguments));
diff --git a/sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart b/sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart
index e4a16b0..114e8e9 100644
--- a/sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart
+++ b/sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart
@@ -35,6 +35,53 @@
bool get isEmpty => queue.isEmpty;
}
+/**
+ * Placeholder for type information of final fields of classes.
+ */
+class ClassInfoForFinalFields {
+ /**
+ * Maps a final field to a map from generative constructor to the
+ * inferred type of the field in that generative constructor.
+ */
+ final Map<Element, Map<Element, Element>> typesOfFinalFields =
+ new Map<Element, Map<Element, Element>>();
+
+ /**
+ * The number of generative constructors that need to be visited
+ * before we can take any decision on the type of the fields.
+ * Given that all generative constructors must be analyzed before
+ * re-analyzing one, we know that once [constructorsToVisitCount]
+ * reaches to 0, all generative constructors have been analyzed.
+ */
+ int constructorsToVisitCount;
+
+ ClassInfoForFinalFields(this.constructorsToVisitCount);
+
+ /**
+ * Records that the generative [constructor] has inferred [type]
+ * for the final [field].
+ */
+ void recordFinalFieldType(Element constructor, Element field, Element type) {
+ Map<Element, Element> typesFor = typesOfFinalFields.putIfAbsent(
+ field, () => new Map<Element, Element>());
+ typesFor[constructor] = type;
+ }
+
+ /**
+ * Records that [constructor] has been analyzed. If not at 0,
+ * decrement [constructorsToVisitCount].
+ */
+ void doneAnalyzingGenerativeConstructor(Element constructor) {
+ if (constructorsToVisitCount != 0) constructorsToVisitCount--;
+ }
+
+ /**
+ * Returns whether all generative constructors of the class have
+ * been analyzed.
+ */
+ bool get isDone => constructorsToVisitCount == 0;
+}
+
class SimpleTypesInferrer extends TypesInferrer {
/**
* Maps an element to its callers.
@@ -49,18 +96,19 @@
new Map<Element, Element>();
/**
- * Maps a name to elements in the universe that have that name.
- */
- final Map<SourceString, Set<Element>> methodCache =
- new Map<SourceString, Set<Element>>();
-
- /**
* Maps an element to the number of times this type inferrer
* analyzed it.
*/
final Map<Element, int> analyzeCount = new Map<Element, int>();
/**
+ * Maps a class to a [ClassInfoForFinalFields] to help collect type
+ * information of final fields.
+ */
+ final Map<ClassElement, ClassInfoForFinalFields> classInfoForFinalFields =
+ new Map<ClassElement, ClassInfoForFinalFields>();
+
+ /**
* The work list of the inferrer.
*/
final WorkSet<Element> workSet = new WorkSet<Element>();
@@ -78,8 +126,6 @@
final Compiler compiler;
- // Times the computation of the call graph.
- final Stopwatch memberWatch = new Stopwatch();
// Times the computation of re-analysis of methods.
final Stopwatch recomputeWatch = new Stopwatch();
// Number of re-analysis.
@@ -153,6 +199,9 @@
*/
getConcreteTypeOfNode(Element owner, Node node) {
var elements = compiler.enqueuer.resolution.resolvedElements[owner];
+ // TODO(ngeoffray): Not sure why the resolver would put a null
+ // mapping.
+ if (elements == null) return null;
Selector selector = elements.getSelector(node);
// TODO(ngeoffray): Should the builder call this method with a
// SendSet?
@@ -202,6 +251,21 @@
set.forEach((e) { workSet.add(e); });
}
}
+
+ // Build the [classInfoForFinalFields] map by iterating over all
+ // seen classes and counting the number of their generative
+ // constructors.
+ // We iterate over the seen classes and not the instantiated ones,
+ // because we also need to analyze the final fields of super
+ // classes that are not instantiated.
+ compiler.enqueuer.resolution.seenClasses.forEach((ClassElement cls) {
+ int constructorCount = 0;
+ cls.forEachMember((_, member) {
+ if (member.isGenerativeConstructor()) constructorCount++;
+ });
+ classInfoForFinalFields[cls.implementation] =
+ new ClassInfoForFinalFields(constructorCount);
+ });
}
dump() {
@@ -214,8 +278,6 @@
interestingTypes++;
}
});
- compiler.log('Type inferrer spent ${memberWatch.elapsedMilliseconds} ms '
- 'computing a call graph.');
compiler.log('Type inferrer re-analyzed methods $recompiles times '
'in ${recomputeWatch.elapsedMilliseconds} ms.');
compiler.log('Type inferrer found $interestingTypes interesting '
@@ -228,16 +290,33 @@
void clear() {
callersOf.clear();
analyzeCount.clear();
+ classInfoForFinalFields.clear();
}
bool analyze(Element element) {
- if (element.isField()) {
- // TODO(ngeoffray): Analyze its initializer.
+ SimpleTypeInferrerVisitor visitor =
+ new SimpleTypeInferrerVisitor(element, compiler, this);
+ Element returnType = visitor.run();
+ if (analyzeCount.containsKey(element)) {
+ analyzeCount[element]++;
+ } else {
+ analyzeCount[element] = 1;
+ }
+ if (element.isGenerativeConstructor()) {
+ // We always know the return type of a generative constructor.
+ return false;
+ } else if (element.isField()) {
+ if (element.modifiers.isFinal() || element.modifiers.isConst()) {
+ if (element.parseNode(compiler).asSendSet() != null) {
+ // If [element] is final and has an initializer, we record
+ // the inferred type.
+ return recordReturnType(element, returnType);
+ }
+ }
+ // We don't record anything for non-final fields.
return false;
} else {
- SimpleTypeInferrerVisitor visitor =
- new SimpleTypeInferrerVisitor(element, compiler, this);
- return visitor.run();
+ return recordReturnType(element, returnType);
}
}
@@ -249,30 +328,14 @@
bool recordReturnType(analyzedElement, returnType) {
assert(returnType != null);
Element existing = returnTypeOf[analyzedElement];
- if (existing == null) {
- // First time we analyzed [analyzedElement]. Initialize the
- // return type.
- assert(!analyzeCount.containsKey(analyzedElement));
- returnTypeOf[analyzedElement] = returnType;
- // If the return type is useful, say it has changed.
- return returnType != compiler.dynamicClass
- && returnType != compiler.nullClass;
- } else if (existing == compiler.dynamicClass) {
- // Previous analysis did not find any type.
- returnTypeOf[analyzedElement] = returnType;
- // If the return type is useful, say it has changed.
- return returnType != compiler.dynamicClass
- && returnType != compiler.nullClass;
- } else if (existing == giveUpType) {
- // If we already gave up on the return type, we don't change it.
- return false;
- } else if (existing != returnType) {
- // The method is returning two different types. Give up for now.
- // TODO(ngeoffray): Compute LUB.
- returnTypeOf[analyzedElement] = giveUpType;
- return true;
- }
- return false;
+ Element newType = existing == compiler.dynamicClass
+ ? returnType // Previous analysis did not find any type.
+ : computeLUB(existing, returnType);
+ returnTypeOf[analyzedElement] = newType;
+ // If the return type is useful, say it has changed.
+ return existing != newType
+ && newType != compiler.dynamicClass
+ && newType != compiler.nullClass;
}
/**
@@ -286,6 +349,7 @@
if (returnType == null || returnType == giveUpType) {
return compiler.dynamicClass;
}
+ assert(returnType != null);
return returnType;
}
@@ -314,6 +378,9 @@
return true;
}
});
+ if (result == null) {
+ result = compiler.dynamicClass;
+ }
return result;
}
@@ -326,8 +393,8 @@
ArgumentsTypes arguments) {
if (analyzeCount.containsKey(caller)) return;
callee = callee.implementation;
- Set<FunctionElement> callers = callersOf.putIfAbsent(
- callee, () => new Set<FunctionElement>());
+ Set<Element> callers = callersOf.putIfAbsent(
+ callee, () => new Set<Element>());
callers.add(caller);
}
@@ -339,8 +406,8 @@
Element callee) {
if (analyzeCount.containsKey(caller)) return;
callee = callee.implementation;
- Set<FunctionElement> callers = callersOf.putIfAbsent(
- callee, () => new Set<FunctionElement>());
+ Set<Element> callers = callersOf.putIfAbsent(
+ callee, () => new Set<Element>());
callers.add(caller);
}
@@ -354,8 +421,8 @@
if (analyzeCount.containsKey(caller)) return;
iterateOverElements(selector, (Element element) {
assert(element.isImplementation);
- Set<FunctionElement> callers = callersOf.putIfAbsent(
- element, () => new Set<FunctionElement>());
+ Set<Element> callers = callersOf.putIfAbsent(
+ element, () => new Set<Element>());
callers.add(caller);
return true;
});
@@ -369,8 +436,8 @@
if (analyzeCount.containsKey(caller)) return;
iterateOverElements(selector, (Element element) {
assert(element.isImplementation);
- Set<FunctionElement> callers = callersOf.putIfAbsent(
- element, () => new Set<FunctionElement>());
+ Set<Element> callers = callersOf.putIfAbsent(
+ element, () => new Set<Element>());
callers.add(caller);
return true;
});
@@ -392,39 +459,76 @@
* [selector]. If [f] returns false, aborts the iteration.
*/
void iterateOverElements(Selector selector, bool f(Element element)) {
- SourceString name = selector.name;
-
- // The following is already computed by the resolver, but it does
- // not save it yet.
- Set<Element> methods = methodCache[name];
- if (methods == null) {
- memberWatch.start();
- methods = new Set<Element>();
- void add(element) {
- if (!element.isInstanceMember()) return;
- if (element.isAbstract(compiler)) return;
- if (!compiler.enqueuer.resolution.isProcessed(element)) return;
- methods.add(element.implementation);
- }
- for (ClassElement cls in compiler.enqueuer.resolution.seenClasses) {
- var element = cls.lookupLocalMember(name);
- if (element != null) {
- if (element.isAbstractField()) {
- if (element.getter != null) add(element.getter);
- if (element.setter != null) add(element.setter);
- } else {
- add(element);
- }
- }
- }
- methodCache[name] = methods;
- memberWatch.stop();
+ Iterable<Element> elements = compiler.world.allFunctions.filter(selector);
+ for (Element e in elements) {
+ if (!f(e.implementation)) return;
}
+ }
- for (Element element in methods) {
- if (selector.appliesUnnamed(element, compiler)) {
- if (!f(element)) return;
- }
+ /**
+ * Records in [classInfoForFinalFields] that [constructor] has
+ * inferred [type] for the final [field].
+ */
+ void recordFinalFieldType(Element constructor, Element field, Element type) {
+ // If the field is being set at its declaration site, it is not
+ // being tracked in the [classInfoForFinalFields] map.
+ if (constructor == field) return;
+ assert(field.modifiers.isFinal() || field.modifiers.isConst());
+ ClassElement cls = constructor.getEnclosingClass();
+ ClassInfoForFinalFields info = classInfoForFinalFields[cls.implementation];
+ info.recordFinalFieldType(constructor, field, type);
+ }
+
+ /**
+ * Records that we are done analyzing [constructor]. If all
+ * generative constructors of its enclosing class have already been
+ * analyzed, this method updates the types of final fields.
+ */
+ void doneAnalyzingGenerativeConstructor(Element constructor) {
+ ClassElement cls = constructor.getEnclosingClass();
+ ClassInfoForFinalFields info = classInfoForFinalFields[cls.implementation];
+ info.doneAnalyzingGenerativeConstructor(constructor);
+ if (info.isDone) {
+ updateFieldTypes(info);
+ }
+ }
+
+ /**
+ * Updates types of final fields listed in [info].
+ */
+ void updateFieldTypes(ClassInfoForFinalFields info) {
+ assert(info.isDone);
+ info.typesOfFinalFields.forEach((Element field,
+ Map<Element, Element> types) {
+ assert(field.modifiers.isFinal());
+ Element fieldType;
+ types.forEach((_, type) {
+ fieldType = computeLUB(fieldType, type);
+ });
+ returnTypeOf[field] = fieldType;
+ });
+ }
+
+ /**
+ * Returns the least upper bound between [firstType] and
+ * [secondType].
+ */
+ Element computeLUB(Element firstType, Element secondType) {
+ assert(secondType != null);
+ if (firstType == null) {
+ return secondType;
+ } else if (firstType == giveUpType) {
+ return firstType;
+ } else if (secondType == compiler.dynamicClass) {
+ return secondType;
+ } else if (firstType == compiler.dynamicClass) {
+ return firstType;
+ } else if (firstType != secondType) {
+ // TODO(ngeoffray): Actually compute the least upper bound.
+ return giveUpType;
+ } else {
+ assert(firstType == secondType);
+ return firstType;
}
}
}
@@ -441,12 +545,12 @@
}
class SimpleTypeInferrerVisitor extends ResolvedVisitor {
- final FunctionElement analyzedElement;
+ final Element analyzedElement;
final SimpleTypesInferrer inferrer;
final Compiler compiler;
Element returnType;
- SimpleTypeInferrerVisitor(FunctionElement element,
+ SimpleTypeInferrerVisitor(Element element,
Compiler compiler,
this.inferrer)
: super(compiler.enqueuer.resolution.resolvedElements[element.declaration]),
@@ -455,49 +559,42 @@
assert(elements != null);
}
- bool run() {
- FunctionExpression node =
- analyzedElement.implementation.parseNode(compiler);
- bool changed;
- if (analyzedElement.isGenerativeConstructor()) {
- FunctionSignature signature = analyzedElement.computeSignature(compiler);
- // TODO(ngeoffray): handle initializing formals.
- // TODO(ngeoffray): handle initializers.
- node.body.accept(this);
- // We always know the return type of a generative constructor.
- changed = false;
+ Element run() {
+ var node = analyzedElement.implementation.parseNode(compiler);
+ if (analyzedElement.isField()) {
+ returnType = visit(node);
+ } else if (analyzedElement.isGenerativeConstructor()) {
+ FunctionElement function = analyzedElement;
+ FunctionSignature signature = function.computeSignature(compiler);
+ signature.forEachParameter((element) {
+ if (element.kind == ElementKind.FIELD_PARAMETER
+ && element.modifiers.isFinal()) {
+ // We don't track argument types yet, so just set the field
+ // as dynamic.
+ inferrer.recordFinalFieldType(
+ analyzedElement, element.fieldElement, compiler.dynamicClass);
+ }
+ });
+ visit(node.initializers);
+ visit(node.body);
+ inferrer.doneAnalyzingGenerativeConstructor(analyzedElement);
+ returnType = analyzedElement.getEnclosingClass();
} else if (analyzedElement.isNative()) {
// Native methods do not have a body, and we currently just say
// they return dynamic.
- inferrer.recordReturnType(analyzedElement, compiler.dynamicClass);
- changed = false;
+ returnType = compiler.dynamicClass;
} else {
- node.body.accept(this);
+ visit(node.body);
if (returnType == null) {
// No return in the body.
returnType = compiler.nullClass;
}
- changed = inferrer.recordReturnType(analyzedElement, returnType);
}
- if (inferrer.analyzeCount.containsKey(analyzedElement)) {
- inferrer.analyzeCount[analyzedElement]++;
- } else {
- inferrer.analyzeCount[analyzedElement] = 1;
- }
- return changed;
+ return returnType;
}
recordReturnType(ClassElement cls) {
- if (returnType == null) {
- returnType = cls;
- } else if (returnType != inferrer.giveUpType
- && cls == compiler.dynamicClass) {
- returnType = cls;
- } else if (returnType == compiler.dynamicClass) {
- // Nothing to do. Stay dynamic.
- } else if (leastUpperBound(cls, returnType) == compiler.dynamicClass) {
- returnType = inferrer.giveUpType;
- }
+ returnType = inferrer.computeLUB(returnType, cls);
}
visitNode(Node node) {
@@ -509,6 +606,10 @@
return node.send.accept(this);
}
+ visit(Node node) {
+ return node == null ? compiler.dynamicClass : node.accept(this);
+ }
+
visitFunctionExpression(FunctionExpression node) {
// We don't put the closure in the work queue of the
// inferrer, because it will share information with its enclosing
@@ -560,9 +661,52 @@
}
visitSendSet(SendSet node) {
- // TODO(ngeoffray): return the right hand side's type.
- node.visitChildren(this);
- return compiler.dynamicClass;
+ Element element = elements[node];
+ if (!Elements.isUnresolved(element) && element.impliesType()) {
+ node.visitChildren(this);
+ return compiler.dynamicClass;
+ }
+
+ Operator op = node.assignmentOperator;
+ if (node.isSuperCall) {
+ // [: super.foo = 42 :] or [: super.foo++ :] or [: super.foo += 1 :].
+ node.visitChildren(this);
+ return compiler.dynamicClass;
+ } else if (node.isIndex) {
+ if (const SourceString("=") == op.source) {
+ // [: foo[0] = 42 :]
+ visit(node.receiver);
+ Element returnType;
+ for (Node argument in node.arguments) {
+ returnType = argument.accept(this);
+ }
+ return returnType;
+ } else {
+ // [: foo[0] += 42 :] or [: foo[0]++ :].
+ node.visitChildren(this);
+ return compiler.dynamicClass;
+ }
+ } else if (const SourceString("=") == op.source) {
+ // [: foo = 42 :]
+ visit(node.receiver);
+ Link<Node> link = node.arguments;
+ assert(!link.isEmpty && link.tail.isEmpty);
+ Element type = link.head.accept(this);
+
+ if (!Elements.isUnresolved(element)
+ && element.isField()
+ && element.modifiers.isFinal()) {
+ inferrer.recordFinalFieldType(analyzedElement, element, type);
+ }
+ return type;
+ } else {
+ // [: foo++ :] or [: foo += 1 :].
+ assert(const SourceString("++") == op.source ||
+ const SourceString("--") == op.source ||
+ node.assignmentOperator.source.stringValue.endsWith("="));
+ node.visitChildren(this);
+ return compiler.dynamicClass;
+ }
}
visitIdentifier(Identifier node) {
@@ -709,14 +853,8 @@
visitGetterSend(Send node) {
Element element = elements[node];
if (Elements.isStaticOrTopLevelField(element)) {
- if (element.isGetter()) {
- inferrer.registerGetterOnElement(analyzedElement, element);
- return inferrer.returnTypeOfElement(element);
- } else {
- // Nothing yet.
- // TODO: Analyze initializer of element.
- return compiler.dynamicClass;
- }
+ inferrer.registerGetterOnElement(analyzedElement, element);
+ return inferrer.returnTypeOfElement(element);
} else if (Elements.isInstanceSend(node, elements)) {
ClassElement receiverType;
if (node.receiver == null) {
@@ -765,12 +903,9 @@
node.condition.accept(this);
Element firstType = node.thenExpression.accept(this);
Element secondType = node.elseExpression.accept(this);
- return leastUpperBound(firstType, secondType);
- }
-
- leastUpperBound(Element firstType, Element secondType) {
- if (firstType == secondType) return firstType;
- return compiler.dynamicClass;
+ Element type = inferrer.computeLUB(firstType, secondType);
+ if (type == inferrer.giveUpType) type = compiler.dynamicClass;
+ return type;
}
internalError(String reason, {Node node}) {
diff --git a/sdk/lib/_internal/compiler/implementation/universe/full_function_set.dart b/sdk/lib/_internal/compiler/implementation/universe/full_function_set.dart
new file mode 100644
index 0000000..1f970f2
--- /dev/null
+++ b/sdk/lib/_internal/compiler/implementation/universe/full_function_set.dart
@@ -0,0 +1,120 @@
+// 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.
+
+part of universe;
+
+class FullFunctionSet extends FunctionSet {
+ FullFunctionSet(Compiler compiler) : super(compiler);
+
+ FunctionSetNode newNode(SourceString name)
+ => new FullFunctionSetNode(name);
+
+ // TODO(kasperl): This interface is a little bit weird and mostly
+ // put here to illustrate how we can refine types. Returning a
+ // selector seems weird because we're only really interested in the
+ // receiver type information and the type kind.
+ Selector refine(Selector selector) {
+ SourceString name = selector.name;
+ FunctionSetNode node = nodes[name];
+ if (node == null) return null;
+ FullFunctionSetQuery query = node.query(selector, compiler);
+
+ // If the selector is already exact, then we cannot refine it
+ // further. If the query isn't exact, it means that the exact
+ // class didn't contain any mathing element.
+ if (selector.typeKind == TypedSelectorKind.EXACT) {
+ if (!query.isExact) return null;
+ assert(selector.receiverType.element == query.classes[0]);
+ return selector;
+ }
+
+ // If the query yields an exact class, we refine the selector.
+ if (query.isExact) {
+ ClassElement refinement = query.classes[0];
+ DartType type = refinement.computeType(compiler);
+ return new TypedSelector.exact(type, selector);
+ }
+
+ // Get the list of classes from the query. If the type information
+ // cannot be represented in the selector, we avoid refining it.
+ List<ClassElement> classes = query.classes;
+ if (classes == null || classes.length != 1) return selector;
+ if (classes.isEmpty) return null;
+
+ // We found one non-exact class, so we try to refine the selector
+ // to be of subclass kind. We take care to reuse the existing
+ // selector if matching class.
+ assert(classes.length == 1);
+ ClassElement refinement = query.classes[0];
+ if (selector.typeKind == TypedSelectorKind.SUBCLASS) {
+ ClassElement existing = selector.receiverType.element;
+ if (refinement == existing) return selector;
+ assert(refinement.isSubclassOf(existing));
+ }
+ DartType type = refinement.computeType(compiler);
+ return new TypedSelector.subclass(type, selector);
+ }
+}
+
+class FullFunctionSetNode extends FunctionSetNode {
+ // To cut down on the time we spend on computing type information
+ // about the function holders, we limit the number of classes and
+ // the number of steps it takes to compute them.
+ static const int MAX_CLASSES = 4;
+ static const int MAX_CLASSES_STEPS = 32;
+
+ FullFunctionSetNode(SourceString name) : super(name);
+
+ FunctionSetQuery newQuery(List<Element> functions,
+ Selector selector,
+ Compiler compiler) {
+ List<ClassElement> classes = computeClasses(functions, compiler);
+ bool isExact = (selector.typeKind == TypedSelectorKind.EXACT)
+ || isExactClass(classes, compiler);
+ return new FullFunctionSetQuery(functions, classes, isExact);
+ }
+
+ static List<ClassElement> computeClasses(List<Element> functions,
+ Compiler compiler) {
+ // TODO(kasperl): Check if any of the found classes may have a
+ // non-throwing noSuchMethod implementation in a subclass instead
+ // of always disabling the class list computation.
+ if (compiler.enabledNoSuchMethod) return null;
+ List<ClassElement> classes = <ClassElement>[];
+ int budget = MAX_CLASSES_STEPS;
+ L: for (Element element in functions) {
+ ClassElement enclosing = element.getEnclosingClass();
+ for (int i = 0; i < classes.length; i++) {
+ if (--budget <= 0) {
+ return null;
+ } else if (enclosing.isSubclassOf(classes[i])) {
+ continue L;
+ } else if (classes[i].isSubclassOf(enclosing)) {
+ classes[i] = enclosing;
+ continue L;
+ }
+ }
+ if (classes.length >= MAX_CLASSES) return null;
+ classes.add(enclosing);
+ }
+ return classes;
+ }
+
+ static bool isExactClass(List<ClassElement> classes, Compiler compiler) {
+ if (classes == null || classes.length != 1) return false;
+ ClassElement single = classes[0];
+ // Return true if the single class in our list does not have a
+ // single instantiated subclass.
+ Set<ClassElement> subtypes = compiler.world.subtypes[single];
+ return subtypes == null
+ || subtypes.every((ClassElement each) => !each.isSubclassOf(single));
+ }
+}
+
+class FullFunctionSetQuery extends FunctionSetQuery {
+ final List<ClassElement> classes;
+ final bool isExact;
+ FullFunctionSetQuery(List<Element> functions, this.classes, this.isExact)
+ : super(functions);
+}
diff --git a/sdk/lib/_internal/compiler/implementation/universe/function_set.dart b/sdk/lib/_internal/compiler/implementation/universe/function_set.dart
index b6c6a53..dc30ef1 100644
--- a/sdk/lib/_internal/compiler/implementation/universe/function_set.dart
+++ b/sdk/lib/_internal/compiler/implementation/universe/function_set.dart
@@ -13,16 +13,20 @@
new Map<SourceString, FunctionSetNode>();
FunctionSet(this.compiler);
+ FunctionSetNode newNode(SourceString name)
+ => new FunctionSetNode(name);
+
void add(Element element) {
- assert(element.isMember());
+ assert(element.isInstanceMember());
+ assert(!element.isAbstract(compiler));
SourceString name = element.name;
- FunctionSetNode node = nodes.putIfAbsent(
- name, () => new FunctionSetNode(name));
+ FunctionSetNode node = nodes.putIfAbsent(name, () => newNode(name));
node.add(element);
}
void remove(Element element) {
- assert(element.isMember());
+ assert(element.isInstanceMember());
+ assert(!element.isAbstract(compiler));
SourceString name = element.name;
FunctionSetNode node = nodes[name];
if (node != null) {
@@ -31,7 +35,8 @@
}
bool contains(Element element) {
- assert(element.isMember());
+ assert(element.isInstanceMember());
+ assert(!element.isAbstract(compiler));
SourceString name = element.name;
FunctionSetNode node = nodes[name];
return (node != null)
@@ -40,24 +45,17 @@
}
/**
- * Returns all elements that may be invoked with the given [selector].
+ * Returns an object that allows iterating over all the functions
+ * that may be invoked with the given [selector].
*/
- Iterable<Element> filterBySelector(Selector selector) {
+ Iterable<Element> filter(Selector selector) {
SourceString name = selector.name;
FunctionSetNode node = nodes[name];
return (node != null)
- ? node.filter(selector, compiler)
+ ? node.query(selector, compiler).functions
: const <Element>[];
}
- /**
- * Returns whether the set has any element matching the given
- * [selector].
- */
- bool hasAnyElementMatchingSelector(Selector selector) {
- return !filterBySelector(selector).isEmpty;
- }
-
void forEach(Function action) {
nodes.forEach((SourceString name, FunctionSetNode node) {
node.forEach(action);
@@ -65,9 +63,11 @@
}
}
+
class FunctionSetNode {
final SourceString name;
- final Map<Selector, List<Element>> cache = new Map<Selector, List<Element>>();
+ final Map<Selector, FunctionSetQuery> cache =
+ new Map<Selector, FunctionSetQuery>();
// Initially, we keep the elements in a list because it is more
// compact than a hash set. Once we get enough elements, we change
@@ -124,23 +124,36 @@
elements.forEach(action);
}
- Iterable<Element> filter(Selector selector, Compiler compiler) {
+ FunctionSetQuery query(Selector selector, Compiler compiler) {
assert(selector.name == name);
- List<Element> result = cache[selector];
+ FunctionSetQuery result = cache[selector];
if (result != null) return result;
+ List<Element> functions;
for (Element element in elements) {
if (selector.appliesUnnamed(element, compiler)) {
- if (result == null) {
- // Defer the allocation of the resulting list until we are
+ if (functions == null) {
+ // Defer the allocation of the functions list until we are
// sure we need it. This allows us to return immutable empty
// lists when the filtering produced no results.
- result = <Element>[];
+ functions = <Element>[];
}
- result.add(element);
+ functions.add(element);
}
}
- if (result == null) result = const <Element>[];
- cache[selector] = result;
+ cache[selector] = result = (functions != null)
+ ? newQuery(functions, selector, compiler)
+ : const FunctionSetQuery(const <Element>[]);
return result;
}
+
+ FunctionSetQuery newQuery(List<Element> functions,
+ Selector selector,
+ Compiler compiler) {
+ return new FunctionSetQuery(functions);
+ }
+}
+
+class FunctionSetQuery {
+ final List<Element> functions;
+ const FunctionSetQuery(this.functions);
}
diff --git a/sdk/lib/_internal/compiler/implementation/universe/universe.dart b/sdk/lib/_internal/compiler/implementation/universe/universe.dart
index 8f57035..4fb6cfb 100644
--- a/sdk/lib/_internal/compiler/implementation/universe/universe.dart
+++ b/sdk/lib/_internal/compiler/implementation/universe/universe.dart
@@ -13,6 +13,7 @@
import '../js/js.dart' as js;
part 'function_set.dart';
+part 'full_function_set.dart';
part 'selector_map.dart';
class Universe {
@@ -414,8 +415,8 @@
if (namedArgumentCount > 0) {
StringBuffer result = new StringBuffer();
for (int i = 0; i < namedArgumentCount; i++) {
- if (i != 0) result.add(', ');
- result.add(namedArguments[i].slowToString());
+ if (i != 0) result.write(', ');
+ result.write(namedArguments[i].slowToString());
}
return "[$result]";
}
@@ -514,12 +515,15 @@
if (typeKind == TypedSelectorKind.EXACT) {
return hasElementIn(self, element) && appliesUntyped(element, compiler);
} else if (typeKind == TypedSelectorKind.SUBCLASS) {
- return (hasElementIn(self, element) || other.isSubclassOf(self))
+ return (hasElementIn(self, element)
+ || other.isSubclassOf(self)
+ || compiler.world.hasAnySubclassThatMixes(self, other))
&& appliesUntyped(element, compiler);
} else {
assert(typeKind == TypedSelectorKind.INTERFACE);
if (other.implementsInterface(self)
|| other.isSubclassOf(self)
+ || compiler.world.hasAnySubclassThatMixes(self, other)
|| compiler.world.hasAnySubclassThatImplements(other, receiverType)) {
return appliesUntyped(element, compiler);
}
diff --git a/sdk/lib/_internal/compiler/implementation/util/link_implementation.dart b/sdk/lib/_internal/compiler/implementation/util/link_implementation.dart
index d00ec86..2f9ea84 100644
--- a/sdk/lib/_internal/compiler/implementation/util/link_implementation.dart
+++ b/sdk/lib/_internal/compiler/implementation/util/link_implementation.dart
@@ -36,19 +36,19 @@
}
void printOn(StringBuffer buffer, [separatedBy]) {
- buffer.add(head);
+ buffer.write(head);
if (separatedBy == null) separatedBy = '';
for (Link link = tail; !link.isEmpty; link = link.tail) {
- buffer.add(separatedBy);
- buffer.add(link.head);
+ buffer.write(separatedBy);
+ buffer.write(link.head);
}
}
String toString() {
StringBuffer buffer = new StringBuffer();
- buffer.add('[ ');
+ buffer.write('[ ');
printOn(buffer, ', ');
- buffer.add(' ]');
+ buffer.write(' ]');
return buffer.toString();
}
diff --git a/sdk/lib/_internal/compiler/implementation/util/uri_extras.dart b/sdk/lib/_internal/compiler/implementation/util/uri_extras.dart
index fed5f15..f43e826 100644
--- a/sdk/lib/_internal/compiler/implementation/util/uri_extras.dart
+++ b/sdk/lib/_internal/compiler/implementation/util/uri_extras.dart
@@ -53,12 +53,12 @@
}
StringBuffer sb = new StringBuffer();
for (int i = common + 1; i < baseParts.length; i++) {
- sb.add('../');
+ sb.write('../');
}
for (int i = common; i < uriParts.length - 1; i++) {
- sb.add('${uriParts[i]}/');
+ sb.write('${uriParts[i]}/');
}
- sb.add('${uriParts.last}');
+ sb.write('${uriParts.last}');
return sb.toString();
}
return uri.toString();
diff --git a/sdk/lib/_internal/compiler/implementation/warnings.dart b/sdk/lib/_internal/compiler/implementation/warnings.dart
index 82fdd8d..39f53b6 100644
--- a/sdk/lib/_internal/compiler/implementation/warnings.dart
+++ b/sdk/lib/_internal/compiler/implementation/warnings.dart
@@ -28,6 +28,8 @@
"no named argument '#{argumentName}' found on method");
static const METHOD_NOT_FOUND = const MessageKind(
'no method named #{memberName} in class #{className}');
+ static const NOT_CALLABLE = const MessageKind(
+ "'#{elementName}' is not callable");
static const MEMBER_NOT_STATIC = const MessageKind(
'#{className}.#{memberName} is not static');
static const NO_INSTANCE_AVAILABLE = const MessageKind(
@@ -378,6 +380,9 @@
static const MISSING_FACTORY_KEYWORD = const MessageKind(
"Did you forget a factory keyword here?");
+ static const DEFERRED_LIBRARY_NAME_MISMATCH = const MessageKind(
+ 'Error: Library name mismatch "#{expectedName}" != "#{actualName}".');
+
static const COMPILER_CRASHED = const MessageKind(
"Error: The compiler crashed when compiling this element.");
diff --git a/sdk/lib/_internal/compiler/implementation/world.dart b/sdk/lib/_internal/compiler/implementation/world.dart
index 4230708..f3142f6 100644
--- a/sdk/lib/_internal/compiler/implementation/world.dart
+++ b/sdk/lib/_internal/compiler/implementation/world.dart
@@ -11,18 +11,16 @@
final Map<ClassElement, Set<ClassElement>> typesImplementedBySubclasses;
final Set<ClassElement> classesNeedingRti;
final Map<ClassElement, Set<ClassElement>> rtiDependencies;
- final FunctionSet userDefinedGetters;
- final FunctionSet userDefinedSetters;
+ final FullFunctionSet allFunctions;
World(Compiler compiler)
: subtypes = new Map<ClassElement, Set<ClassElement>>(),
mixinUses = new Map<ClassElement, Set<MixinApplicationElement>>(),
typesImplementedBySubclasses =
new Map<ClassElement, Set<ClassElement>>(),
- userDefinedGetters = new FunctionSet(compiler),
- userDefinedSetters = new FunctionSet(compiler),
classesNeedingRti = new Set<ClassElement>(),
rtiDependencies = new Map<ClassElement, Set<ClassElement>>(),
+ allFunctions = new FullFunctionSet(compiler),
this.compiler = compiler;
void populate() {
@@ -117,122 +115,67 @@
return classesNeedingRti.contains(cls) || compiler.enabledRuntimeType;
}
- void recordUserDefinedGetter(Element element) {
- assert(element.isGetter());
- userDefinedGetters.add(element);
- }
-
- void recordUserDefinedSetter(Element element) {
- assert(element.isSetter());
- userDefinedSetters.add(element);
- }
-
bool hasAnyUserDefinedGetter(Selector selector) {
- return userDefinedGetters.hasAnyElementMatchingSelector(selector);
+ return allFunctions.filter(selector).any((each) => each.isGetter());
}
bool hasAnyUserDefinedSetter(Selector selector) {
- return userDefinedSetters.hasAnyElementMatchingSelector(selector);
+ return allFunctions.filter(selector).any((each) => each.isSetter());
}
// Returns whether a subclass of [superclass] implements [type].
bool hasAnySubclassThatImplements(ClassElement superclass, DartType type) {
- Set<ClassElement> subclasses= typesImplementedBySubclasses[superclass];
+ Set<ClassElement> subclasses = typesImplementedBySubclasses[superclass];
if (subclasses == null) return false;
return subclasses.contains(type.element);
}
- bool hasNoOverridingMember(Element element) {
- ClassElement cls = element.getEnclosingClass();
- Set<ClassElement> subclasses = compiler.world.subtypes[cls];
- // TODO(ngeoffray): Implement the full thing.
- return subclasses == null || subclasses.isEmpty;
+ // Returns whether a subclass of [superclass] mixes in [other].
+ bool hasAnySubclassThatMixes(ClassElement superclass, ClassElement other) {
+ Set<MixinApplicationElement> uses = mixinUses[other];
+ return (uses != null)
+ ? uses.any((each) => each.isSubclassOf(superclass))
+ : false;
}
void registerUsedElement(Element element) {
- if (element.isMember()) {
- if (element.isGetter()) {
- // We're collecting user-defined getters to let the codegen know which
- // field accesses might have side effects.
- recordUserDefinedGetter(element);
- } else if (element.isSetter()) {
- recordUserDefinedSetter(element);
- }
+ if (element.isInstanceMember() && !element.isAbstract(compiler)) {
+ allFunctions.add(element);
}
}
- /**
- * Returns a [MemberSet] that contains the possible targets of the given
- * [selector] on a receiver with the given [type]. This includes all sub
- * types.
- */
- MemberSet _memberSetFor(DartType type, Selector selector) {
- assert(compiler != null);
- ClassElement cls = type.element;
- SourceString name = selector.name;
- LibraryElement library = selector.library;
- MemberSet result = new MemberSet(name);
- Element element = cls.implementation.lookupSelector(selector);
- if (element != null) result.add(element);
-
- bool isPrivate = name.isPrivate();
- Set<ClassElement> subtypesOfCls = subtypes[cls];
- if (subtypesOfCls != null) {
- for (ClassElement sub in subtypesOfCls) {
- // Private members from a different library are not visible.
- if (isPrivate && sub.getLibrary() != library) continue;
- element = sub.implementation.lookupLocalMember(name);
- if (element != null) result.add(element);
- }
- }
- return result;
+ VariableElement locateSingleField(Selector selector) {
+ Element result = locateSingleElement(selector);
+ return (result != null && result.isField()) ? result : null;
}
- /**
- * Returns the field in [type] described by the given [selector].
- * If no such field exists, or a subclass overrides the field
- * returns [:null:].
- */
- VariableElement locateSingleField(DartType type, Selector selector) {
- MemberSet memberSet = _memberSetFor(type, selector);
- ClassElement cls = type.element;
- Element result = cls.implementation.lookupSelector(selector);
- if (result == null) return null;
- if (!result.isField()) return null;
-
- // Verify that no subclass overrides the field.
- if (memberSet.elements.length != 1) return null;
- assert(memberSet.elements.contains(result));
- return result;
+ Element locateSingleElement(Selector selector) {
+ Iterable<Element> targets = allFunctions.filter(selector);
+ if (targets.length != 1) return null;
+ Element result = targets.first;
+ ClassElement enclosing = result.getEnclosingClass();
+ DartType receiverType = selector.receiverType;
+ ClassElement receiverTypeElement = (receiverType == null)
+ ? compiler.objectClass
+ : receiverType.element;
+ // We only return the found element if it is guaranteed to be
+ // implemented on the exact receiver type. It could be found in a
+ // subclass or in an inheritance-wise unrelated class in case of
+ // subtype selectors.
+ return (receiverTypeElement.isSubclassOf(enclosing)) ? result : null;
}
- Set<ClassElement> findNoSuchMethodHolders(DartType type) {
- Set<ClassElement> result = new Set<ClassElement>();
+ Iterable<ClassElement> locateNoSuchMethodHolders(Selector selector) {
Selector noSuchMethodSelector = new Selector.noSuchMethod();
- MemberSet memberSet = _memberSetFor(type, noSuchMethodSelector);
- for (Element element in memberSet.elements) {
- ClassElement holder = element.getEnclosingClass();
- if (!identical(holder, compiler.objectClass) &&
- noSuchMethodSelector.applies(element, compiler)) {
- result.add(holder);
- }
+ DartType receiverType = selector.receiverType;
+ if (receiverType != null) {
+ noSuchMethodSelector = new TypedSelector(
+ receiverType, selector.typeKind, noSuchMethodSelector);
}
- return result;
+ ClassElement objectClass = compiler.objectClass;
+ return allFunctions
+ .filter(noSuchMethodSelector)
+ .map((Element member) => member.getEnclosingClass())
+ .where((ClassElement holder) => !identical(holder, objectClass));
}
}
-
-/**
- * A [MemberSet] contains all the possible targets for a selector.
- */
-class MemberSet {
- final Set<Element> elements;
- final SourceString name;
-
- MemberSet(SourceString this.name) : elements = new Set<Element>();
-
- void add(Element element) {
- elements.add(element);
- }
-
- bool get isEmpty => elements.isEmpty;
-}
diff --git a/sdk/lib/_internal/dartdoc/lib/dartdoc.dart b/sdk/lib/_internal/dartdoc/lib/dartdoc.dart
index e0305ab..a6f0179d 100644
--- a/sdk/lib/_internal/dartdoc/lib/dartdoc.dart
+++ b/sdk/lib/_internal/dartdoc/lib/dartdoc.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
@@ -107,18 +107,19 @@
Future copyDirectory(Path from, Path to) {
final completer = new Completer();
final fromDir = new Directory.fromPath(from);
- final lister = fromDir.list(recursive: false);
+ fromDir.list(recursive: false).listen(
+ (FileSystemEntity entity) {
+ if (entity is File) {
+ final name = new Path(entity.name).filename;
+ // TODO(rnystrom): Hackish. Ignore 'hidden' files like .DS_Store.
+ if (name.startsWith('.')) return;
- lister.onFile = (String path) {
- final name = new Path(path).filename;
- // TODO(rnystrom): Hackish. Ignore 'hidden' files like .DS_Store.
- if (name.startsWith('.')) return;
-
- File fromFile = new File(path);
- File toFile = new File.fromPath(to.append(name));
- fromFile.openInputStream().pipe(toFile.openOutputStream());
- };
- lister.onDone = (done) => completer.complete(true);
+ File fromFile = entity;
+ File toFile = new File.fromPath(to.append(name));
+ fromFile.openRead().pipe(toFile.openWrite());
+ }
+ },
+ onDone: () => completer.complete(true));
return completer.future;
}
@@ -175,7 +176,7 @@
* Path to the directory containing data files for each library.
*
* Currently this is the serialized json version of the LibraryElement for
- * the library.
+ * the library.
*/
String location;
/**
@@ -1902,15 +1903,18 @@
write("NETWORK:\n*\n\n");
write("CACHE:\n");
var toCache = new Directory.fromPath(outputDir);
- var toCacheLister = toCache.list(recursive: true);
- toCacheLister.onFile = (filename) {
- if (filename.endsWith('appcache.manifest')) {
- return;
- }
- Path relativeFilePath = new Path(filename).relativeTo(outputDir);
- write("$relativeFilePath\n");
- };
- toCacheLister.onDone = (done) => endFile();
+ toCache.list(recursive: true).listen(
+ (FileSystemEntity entity) {
+ if (entity.isFile) {
+ var filename = entity.path;
+ if (filename.endsWith('appcache.manifest')) {
+ return;
+ }
+ Path relativeFilePath = new Path(filename).relativeTo(outputDir);
+ write("$relativeFilePath\n");
+ }
+ },
+ onDone: () => endFile());
}
/**
diff --git a/sdk/lib/_internal/dartdoc/lib/markdown.dart b/sdk/lib/_internal/dartdoc/lib/markdown.dart
index ef111fb..a7ea5cc 100644
--- a/sdk/lib/_internal/dartdoc/lib/markdown.dart
+++ b/sdk/lib/_internal/dartdoc/lib/markdown.dart
@@ -5,8 +5,6 @@
/// Parses text in a markdown-like format and renders to HTML.
library markdown;
-import 'classify.dart';
-
// TODO(rnystrom): Use "package:" URL (#4968).
part 'src/markdown/ast.dart';
part 'src/markdown/block_parser.dart';
diff --git a/sdk/lib/_internal/dartdoc/lib/src/client/client-shared.dart b/sdk/lib/_internal/dartdoc/lib/src/client/client-shared.dart
index c90469a..d6e8daa 100644
--- a/sdk/lib/_internal/dartdoc/lib/src/client/client-shared.dart
+++ b/sdk/lib/_internal/dartdoc/lib/src/client/client-shared.dart
@@ -29,8 +29,8 @@
void setupLocation() {
// Figure out where we are.
final body = document.query('body');
- currentLibrary = body.dataAttributes['library'];
- currentType = body.dataAttributes['type'];
+ currentLibrary = body.dataset['library'];
+ currentType = body.dataset['type'];
prefix = (currentType != null) ? '../' : '';
}
@@ -71,9 +71,9 @@
void enableShowHideInherited() {
var showInherited = document.query('#show-inherited');
if (showInherited == null) return;
- showInherited.dataAttributes.putIfAbsent('show-inherited', () => 'block');
+ showInherited.dataset.putIfAbsent('show-inherited', () => 'block');
showInherited.onClick.listen((e) {
- String display = showInherited.dataAttributes['show-inherited'];
+ String display = showInherited.dataset['show-inherited'];
if (display == 'block') {
display = 'none';
showInherited.innerHtml = 'Show inherited';
@@ -81,7 +81,7 @@
display = 'block';
showInherited.innerHtml = 'Hide inherited';
}
- showInherited.dataAttributes['show-inherited'] = display;
+ showInherited.dataset['show-inherited'] = display;
for (var elem in document.queryAll('.inherited')) {
elem.style.display = display;
}
diff --git a/sdk/lib/_internal/dartdoc/lib/src/markdown/block_parser.dart b/sdk/lib/_internal/dartdoc/lib/src/markdown/block_parser.dart
index a5be1a7..bd14f36 100644
--- a/sdk/lib/_internal/dartdoc/lib/src/markdown/block_parser.dart
+++ b/sdk/lib/_internal/dartdoc/lib/src/markdown/block_parser.dart
@@ -234,9 +234,9 @@
childLines.add('');
// Escape the code.
- final escaped = classifySource(childLines.join('\n'));
+ final escaped = escapeHtml(childLines.join('\n'));
- return new Element.text('pre', escaped);
+ return new Element('pre', [new Element.text('code', escaped)]);
}
}
diff --git a/sdk/lib/_internal/libraries.dart b/sdk/lib/_internal/libraries.dart
index 8d5a548..8ab2e7f 100644
--- a/sdk/lib/_internal/libraries.dart
+++ b/sdk/lib/_internal/libraries.dart
@@ -104,6 +104,11 @@
category: "Client",
dart2jsPath: "web_audio/dart2js/web_audio_dart2js.dart"),
+ "web_sql": const LibraryInfo(
+ "web_sql/dartium/web_sql_dartium.dart",
+ category: "Client",
+ dart2jsPath: "web_sql/dart2js/web_sql_dart2js.dart"),
+
"_collection-dev": const LibraryInfo(
"_collection_dev/collection_dev.dart",
category: "Internal",
diff --git a/sdk/lib/async/async.dart b/sdk/lib/async/async.dart
index 8998f83..d6144af 100644
--- a/sdk/lib/async/async.dart
+++ b/sdk/lib/async/async.dart
@@ -6,6 +6,7 @@
part 'async_error.dart';
part 'collection_sink.dart';
+part 'deferred_load.dart';
part 'future.dart';
part 'future_impl.dart';
part 'stream.dart';
diff --git a/sdk/lib/async/async_sources.gypi b/sdk/lib/async/async_sources.gypi
index 549a749..d297cae 100644
--- a/sdk/lib/async/async_sources.gypi
+++ b/sdk/lib/async/async_sources.gypi
@@ -7,6 +7,7 @@
'sources': [
'async_error.dart',
'collection_sink.dart',
+ 'deferred_load.dart',
'future.dart',
'future_impl.dart',
'stream.dart',
@@ -16,4 +17,3 @@
'timer.dart',
],
}
-
diff --git a/sdk/lib/async/deferred_load.dart b/sdk/lib/async/deferred_load.dart
new file mode 100644
index 0000000..bc99a4d
--- /dev/null
+++ b/sdk/lib/async/deferred_load.dart
@@ -0,0 +1,41 @@
+// 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.
+
+/**
+ * Indicates that loading of [libraryName] is deferred.
+ *
+ * Applies to library imports, when used as metadata.
+ *
+ * Example usage:
+ *
+ * [:
+ * @lazy
+ * import 'foo.dart' as foo;
+ *
+ * const lazy = const DeferredLibrary('com.example.foo');
+ *
+ * void main() {
+ * foo.method(); // Throws a NoSuchMethodError, foo is not loaded yet.
+ * lazy.load().then(onFooLoaded);
+ * }
+ *
+ * void onFooLoaded(_) {
+ * foo.method();
+ * }
+ * :]
+ */
+class DeferredLibrary {
+ final String libraryName;
+ final String uri;
+
+ const DeferredLibrary(this.libraryName, {this.uri});
+
+ /**
+ * Ensure that [libraryName] has been loaded.
+ *
+ * The value of the returned future is true if this invocation of
+ * [load] caused the library to be loaded.
+ */
+ external Future<bool> load();
+}
diff --git a/sdk/lib/async/stream_impl.dart b/sdk/lib/async/stream_impl.dart
index af68d43..130f226 100644
--- a/sdk/lib/async/stream_impl.dart
+++ b/sdk/lib/async/stream_impl.dart
@@ -21,9 +21,11 @@
const int _STREAM_EVENT_ID_SHIFT = 2;
/// Bit set while firing and clear while not.
const int _STREAM_FIRING = 8;
+/// Bit set while calling a pause-state or subscription-state change callback.
+const int _STREAM_CALLBACK = 16;
/// The count of times a stream has paused is stored in the
/// state, shifted by this amount.
-const int _STREAM_PAUSE_COUNT_SHIFT = 4;
+const int _STREAM_PAUSE_COUNT_SHIFT = 5;
// States for listeners.
@@ -121,7 +123,7 @@
* should be the last message sent.
*/
void _close() {
- if (_isClosed) throw new StateError("Sending on closed stream");
+ if (_isClosed) return;
_state |= _STREAM_CLOSED;
if (!_canFireEvent) {
// You can't enqueue an event after the Done, so make it const.
@@ -146,6 +148,9 @@
/** Whether one or more active subscribers have requested a pause. */
bool get _isPaused => _state >= (1 << _STREAM_PAUSE_COUNT_SHIFT);
+ /** Whether we are currently executing a state-chance callback. */
+ bool get _isInCallback => (_state & _STREAM_CALLBACK) != 0;
+
/** Check whether the pending event queue is non-empty */
bool get _hasPendingEvent =>
_pendingEvents != null && !_pendingEvents.isEmpty;
@@ -153,6 +158,19 @@
/** Whether we are currently firing an event. */
bool get _isFiring => (_state & _STREAM_FIRING) != 0;
+ /** Whether the state bits allow firing. */
+ bool get _mayFireState {
+ // The state disallows firing if:
+ // - an event is currently firing
+ // - a stat-change callback is being called
+ // - the pause-count is not zero.
+ const int mask =
+ _STREAM_FIRING |
+ _STREAM_CALLBACK |
+ ~((1 << _STREAM_PAUSE_COUNT_SHIFT) - 1);
+ return (_state & mask) == 0;
+ }
+
int get _currentEventIdBit =>
(_state & _STREAM_EVENT_ID ) >> _STREAM_EVENT_ID_SHIFT;
@@ -160,7 +178,7 @@
bool get _hasSubscribers;
/** Whether the stream can fire a new event. */
- bool get _canFireEvent => !_isFiring && !_isPaused && !_hasPendingEvent;
+ bool get _canFireEvent => _mayFireState && !_hasPendingEvent;
// State modification.
@@ -216,8 +234,11 @@
void _endFiring() {
assert(_isFiring);
_state ^= _STREAM_FIRING;
- if (_isPaused) _onPauseStateChange();
- if (!_hasSubscribers) _onSubscriptionStateChange();
+ if (!_hasSubscribers) {
+ _callOnSubscriptionStateChange();
+ } else if (_isPaused) {
+ _callOnPauseStateChange();
+ }
}
/**
@@ -241,7 +262,7 @@
resumeSignal.whenComplete(() { this._resume(listener, true); });
}
if (!wasPaused && !_isFiring) {
- _onPauseStateChange();
+ _callOnPauseStateChange();
}
}
@@ -252,7 +273,7 @@
assert(_isPaused);
_decrementPauseCount(listener);
if (!_isPaused) {
- if (!_isFiring) _onPauseStateChange();
+ if (!_isFiring) _callOnPauseStateChange();
if (_hasPendingEvent) {
// If we can fire events now, fire any pending events right away.
if (fromEvent && !_isFiring) {
@@ -309,6 +330,30 @@
*/
void _forEachSubscriber(void action(_StreamSubscriptionImpl<T> subscription));
+ /** Calls [_onPauseStateChange] while setting callback bit. */
+ void _callOnPauseStateChange() {
+ // After calling [_close], all pauses are handled internally by the Stream.
+ if (_isClosed) return;
+ if (!_isInCallback) {
+ _state |= _STREAM_CALLBACK;
+ _onPauseStateChange();
+ _state ^= _STREAM_CALLBACK;
+ } else {
+ _onPauseStateChange();
+ }
+ }
+
+ /** Calls [_onSubscriptionStateChange] while setting callback bit. */
+ void _callOnSubscriptionStateChange() {
+ if (!_isInCallback) {
+ _state |= _STREAM_CALLBACK;
+ _onSubscriptionStateChange();
+ _state ^= _STREAM_CALLBACK;
+ } else {
+ _onSubscriptionStateChange();
+ }
+ }
+
/**
* Called when the first subscriber requests a pause or the last a resume.
*
@@ -430,9 +475,20 @@
class _SingleStreamImpl<T> extends _StreamImpl<T> {
_StreamListener _subscriber = null;
- /** Whether one or more active subscribers have requested a pause. */
+ // A single-stream is considered paused when it has no subscriber.
+ // Exception is when it's complete (which only matters for pause-state-change
+ // callbacks), where it's not considered paused.
bool get _isPaused => (!_hasSubscribers && !_isComplete) || super._isPaused;
+
+ bool get _canFireEvent {
+ // A single-stream is considered paused when it has no subscriber and
+ // isn't complete, so it can't fire if there is no subscriber.
+ // It won't try to fire when completed, so no need to check that.
+ return _mayFireState && !_hasPendingEvent && _hasSubscribers;
+ }
+
+
/** Whether there is currently a subscriber on this [Stream]. */
bool get _hasSubscribers => _subscriber != null;
@@ -457,7 +513,7 @@
}
_subscriber = subscription;
subscription._setSubscribed(0);
- _onSubscriptionStateChange();
+ _callOnSubscriptionStateChange();
if (_hasPendingEvent) {
_schedulePendingEvents();
}
@@ -485,10 +541,7 @@
int subscriptionPauseCount = subscriber._setUnsubscribed();
_updatePauseCount(-subscriptionPauseCount);
if (!_isFiring) {
- if (subscriptionPauseCount > 0) {
- _onPauseStateChange();
- }
- _onSubscriptionStateChange();
+ _callOnSubscriptionStateChange();
}
}
@@ -607,7 +660,7 @@
bool firstSubscriber = !_hasSubscribers;
_InternalLinkList.add(this, listener);
if (firstSubscriber) {
- _onSubscriptionStateChange();
+ _callOnSubscriptionStateChange();
}
}
@@ -640,7 +693,7 @@
bool wasPaused = _isPaused;
_removeListener(listener);
if (wasPaused != _isPaused) _onPauseStateChange();
- if (!_hasSubscribers) _onSubscriptionStateChange();
+ if (!_hasSubscribers) _callOnSubscriptionStateChange();
}
}
@@ -648,7 +701,7 @@
* Removes a listener from this stream and cancels its pauses.
*
* This is a low-level action that doesn't call [_onSubscriptionStateChange].
- * or [_onPauseStateChange].
+ * or [_callOnPauseStateChange].
*/
void _removeListener(_StreamListener listener) {
int pauseCount = listener._setUnsubscribed();
@@ -1138,7 +1191,7 @@
_SingleStreamMultiplexer(this._source);
- void _onPauseStateChange() {
+ void _callOnPauseStateChange() {
if (_isPaused) {
if (_subscription != null) {
_subscription.pause();
diff --git a/sdk/lib/chrome/dart2js/chrome_dart2js.dart b/sdk/lib/chrome/dart2js/chrome_dart2js.dart
index ec23fde..92b66c5 100644
--- a/sdk/lib/chrome/dart2js/chrome_dart2js.dart
+++ b/sdk/lib/chrome/dart2js/chrome_dart2js.dart
@@ -9,7 +9,8 @@
// 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.
-// DO NOT EDIT
+// DO NOT EDIT - unless you are editing documentation as per:
+// https://code.google.com/p/dart/wiki/ContributingHTMLDocumentation
// Auto-generated dart:chrome library.
/// Native wrappers for the Chrome Packaged App APIs.
diff --git a/sdk/lib/collection/splay_tree.dart b/sdk/lib/collection/splay_tree.dart
index 31be011..0345f8a 100644
--- a/sdk/lib/collection/splay_tree.dart
+++ b/sdk/lib/collection/splay_tree.dart
@@ -26,7 +26,7 @@
* This implementation is a Dart version of the JavaScript
* implementation in the V8 project.
*/
-class SplayTreeMap<K extends Comparable, V> implements Map<K, V> {
+class SplayTreeMap<K extends Comparable<K>, V> implements Map<K, V> {
// The root node of the splay tree. It will contain either the last
// element inserted, or the last element looked up.
diff --git a/sdk/lib/core/comparable.dart b/sdk/lib/core/comparable.dart
index e544201..ee92df9 100644
--- a/sdk/lib/core/comparable.dart
+++ b/sdk/lib/core/comparable.dart
@@ -22,7 +22,7 @@
/**
* Interface used by types that have an intrinsic ordering.
*/
-abstract class Comparable {
+abstract class Comparable<T> {
/**
* Compares this object to another [Comparable]
*
@@ -31,7 +31,7 @@
* May throw an [ArgumentError] if [other] is of a type that
* is not comparable to [:this:].
*/
- int compareTo(Comparable other);
+ int compareTo(T other);
/**
* Compare one comparable to another.
diff --git a/sdk/lib/core/date_time.dart b/sdk/lib/core/date_time.dart
index b57b630..38d179e 100644
--- a/sdk/lib/core/date_time.dart
+++ b/sdk/lib/core/date_time.dart
@@ -8,7 +8,7 @@
* Deprecated class. Please use [DateTime] instead.
*/
@deprecated
-abstract class Date implements Comparable {
+abstract class Date implements Comparable<Date> {
// Weekday constants that are returned by [weekday] method:
static const int MON = 1;
static const int TUE = 2;
diff --git a/sdk/lib/core/errors.dart b/sdk/lib/core/errors.dart
index 92f313f..34fcaed 100644
--- a/sdk/lib/core/errors.dart
+++ b/sdk/lib/core/errors.dart
@@ -19,8 +19,9 @@
}
if (object is String) {
// TODO(ahe): Remove backslash when http://dartbug.com/4995 is fixed.
+ String string = object;
const backslash = '\\';
- String escaped = object
+ String escaped = string
.replaceAll('$backslash', '$backslash$backslash')
.replaceAll('\n', '${backslash}n')
.replaceAll('\r', '${backslash}r')
@@ -151,49 +152,7 @@
[List existingArgumentNames = null])
: this._existingArgumentNames = existingArgumentNames;
- String toString() {
- StringBuffer sb = new StringBuffer();
- int i = 0;
- if (_arguments != null) {
- for (; i < _arguments.length; i++) {
- if (i > 0) {
- sb.add(", ");
- }
- sb.add(Error.safeToString(_arguments[i]));
- }
- }
- if (_namedArguments != null) {
- _namedArguments.forEach((String key, var value) {
- if (i > 0) {
- sb.add(", ");
- }
- sb.add(key);
- sb.add(": ");
- sb.add(Error.safeToString(value));
- i++;
- });
- }
- if (_existingArgumentNames == null) {
- return "NoSuchMethodError : method not found: '$_memberName'\n"
- "Receiver: ${Error.safeToString(_receiver)}\n"
- "Arguments: [$sb]";
- } else {
- String actualParameters = sb.toString();
- sb = new StringBuffer();
- for (int i = 0; i < _existingArgumentNames.length; i++) {
- if (i > 0) {
- sb.add(", ");
- }
- sb.add(_existingArgumentNames[i]);
- }
- String formalParameters = sb.toString();
- return "NoSuchMethodError: incorrect number of arguments passed to "
- "method named '$_memberName'\n"
- "Receiver: ${Error.safeToString(_receiver)}\n"
- "Tried calling: $_memberName($actualParameters)\n"
- "Found: $_memberName($formalParameters)";
- }
- }
+ external String toString();
}
diff --git a/sdk/lib/core/num.dart b/sdk/lib/core/num.dart
index b5e42cb..439022f 100644
--- a/sdk/lib/core/num.dart
+++ b/sdk/lib/core/num.dart
@@ -7,7 +7,7 @@
/**
* All numbers in dart are instances of [num].
*/
-abstract class num implements Comparable {
+abstract class num implements Comparable<num> {
/** Addition operator. */
num operator +(num other);
diff --git a/sdk/lib/core/string.dart b/sdk/lib/core/string.dart
index 4e5b950..ba07bae 100644
--- a/sdk/lib/core/string.dart
+++ b/sdk/lib/core/string.dart
@@ -17,7 +17,7 @@
* Unicode code point. The runes of a string are accessible through the [runes]
* getter.
*/
-abstract class String implements Comparable, Pattern {
+abstract class String implements Comparable<String>, Pattern {
/**
* Allocates a new String for the specified [charCodes].
*
diff --git a/sdk/lib/core/string_buffer.dart b/sdk/lib/core/string_buffer.dart
index cbfcd6e..07d3277 100644
--- a/sdk/lib/core/string_buffer.dart
+++ b/sdk/lib/core/string_buffer.dart
@@ -11,13 +11,16 @@
*/
class StringBuffer implements StringSink {
- /// Creates the string buffer with an initial content.
+ /** Creates the string buffer with an initial content. */
external StringBuffer([Object content = ""]);
- /// Returns the length of the buffer.
+ /**
+ * Returns the length of the content that has been accumulated so far.
+ * This is a constant-time operation.
+ */
external int get length;
- /// Returns whether the buffer is empty.
+ /** Returns whether the buffer is empty. This is a constant-time operation. */
bool get isEmpty => length == 0;
/**
diff --git a/sdk/lib/core/string_sink.dart b/sdk/lib/core/string_sink.dart
index c2eff2d..04650f7 100644
--- a/sdk/lib/core/string_sink.dart
+++ b/sdk/lib/core/string_sink.dart
@@ -7,8 +7,8 @@
abstract class StringSink {
/**
- * Converts [obj] to a String by invoking [:toString:] and adds the result to
- * this [StringSink].
+ * Converts [obj] to a String by invoking `toString` and adds the result to
+ * `this`.
*/
void write(Object obj);
@@ -18,15 +18,15 @@
void writeAll(Iterable objects);
/**
- * Converts [obj] to a String by invoking [:toString:] and adds the result to
- * this [StringSink]. Then adds a new line.
+ * Converts [obj] to a String by invoking `toString` and adds the result to
+ * `this`. Then adds a new line.
*/
void writeln(Object obj);
/**
- * Writes the [charCode] to this [StringSink].
+ * Writes the [charCode] to `this`.
*
- * This method is equivalent to [:write(new String.fromCharCode(charCode)):].
+ * This method is equivalent to `write(new String.fromCharCode(charCode))`.
*/
void writeCharCode(int charCode);
}
diff --git a/sdk/lib/crypto/crypto.dart b/sdk/lib/crypto/crypto.dart
index 590a62e..755c18b 100644
--- a/sdk/lib/crypto/crypto.dart
+++ b/sdk/lib/crypto/crypto.dart
@@ -6,148 +6,10 @@
import 'dart:math';
+part 'crypto_base.dart';
part 'crypto_utils.dart';
part 'hash_utils.dart';
part 'hmac.dart';
part 'md5.dart';
part 'sha1.dart';
part 'sha256.dart';
-
-/**
- * Interface for cryptographic hash functions.
- *
- * The [add] method is used to add data to the hash. The [close] method
- * is used to extract the message digest.
- *
- * Once the [close] method has been called no more data can be added using the
- * [add] method. If [add] is called after the first call to [close] a
- * HashException is thrown.
- *
- * If multiple instances of a given Hash is needed the [newInstance]
- * method can provide a new instance.
- */
-// TODO(floitsch): make Hash implement Sink, StreamSink or similar.
-abstract class Hash {
- /**
- * Add a list of bytes to the hash computation.
- */
- add(List<int> data);
-
- /**
- * Finish the hash computation and extract the message digest as
- * a list of bytes.
- */
- List<int> close();
-
- /**
- * Returns a new instance of this hash function.
- */
- Hash newInstance();
-
- /**
- * Internal block size of the hash in bytes.
- *
- * This is exposed for use by the HMAC class which needs to know the
- * block size for the [Hash] it is using.
- */
- int get blockSize;
-}
-
-/**
- * SHA1 hash function implementation.
- */
-abstract class SHA1 implements Hash {
- factory SHA1() => new _SHA1();
-}
-
-/**
- * SHA256 hash function implementation.
- */
-abstract class SHA256 implements Hash {
- factory SHA256() => new _SHA256();
-}
-
-/**
- * MD5 hash function implementation.
- *
- * WARNING: MD5 has known collisions and should only be used when
- * required for backwards compatibility.
- */
-abstract class MD5 implements Hash {
- factory MD5() => new _MD5();
-}
-
-/**
- * Hash-based Message Authentication Code support.
- *
- * The [add] method is used to add data to the message. The [digest] and
- * [close] methods are used to extract the message authentication code.
- */
-// TODO(floitsch): make Hash implement Sink, StreamSink or similar.
-abstract class HMAC {
- /**
- * Create an [HMAC] object from a [Hash] and a key.
- */
- factory HMAC(Hash hash, List<int> key) => new _HMAC(hash, key);
-
- /**
- * Add a list of bytes to the message.
- */
- add(List<int> data);
-
- /**
- * Perform the actual computation and extract the message digest
- * as a list of bytes.
- */
- List<int> close();
-
- /**
- * Extract the message digest as a list of bytes without closing [this].
- */
- List<int> get digest;
-
- /**
- * Verify that the HMAC computed for the data so far matches the
- * given message digest.
- *
- * This method should be used instead of memcmp-style comparisons
- * to avoid leaking information via timing.
- *
- * Throws an exception if the given digest does not have the same
- * size as the digest computed by this HMAC instance.
- */
- bool verify(List<int> digest);
-}
-
-/**
- * Utility methods for working with message digests.
- */
-abstract class CryptoUtils {
- /**
- * Convert a list of bytes (for example a message digest) into a hex
- * string.
- */
- static String bytesToHex(List<int> bytes) {
- return _CryptoUtils.bytesToHex(bytes);
- }
-
- /**
- * Converts a list of bytes (for example a message digest) into a
- * base64 encoded string optionally broken up in to lines of
- * [lineLength] chars separated by '\r\n'.
- */
- static String bytesToBase64(List<int> bytes, [int lineLength]) {
- return _CryptoUtils.bytesToBase64(bytes, lineLength);
- }
-}
-
-/**
- * HashExceptions are thrown on invalid use of a Hash
- * object.
- */
-class HashException {
- HashException(String this.message);
- toString() => "HashException: $message";
- String message;
-}
-
diff --git a/runtime/lib/crypto/crypto_vm.dart b/sdk/lib/crypto/crypto_base.dart
similarity index 63%
rename from runtime/lib/crypto/crypto_vm.dart
rename to sdk/lib/crypto/crypto_base.dart
index a542984..4231718 100644
--- a/runtime/lib/crypto/crypto_vm.dart
+++ b/sdk/lib/crypto/crypto_base.dart
@@ -1,38 +1,34 @@
-// Copyright (c) 2012, 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.
-// TODO(ager, iposva): Get rid of this file when the VM snapshot
-// generation can deal with normal library structure.
-
-library crypto;
-
-import 'dart:math';
+part of dart.crypto;
/**
* Interface for cryptographic hash functions.
*
- * The [update] method is used to add data to the hash. The [digest] method
+ * The [add] method is used to add data to the hash. The [close] method
* is used to extract the message digest.
*
- * Once the [digest] method has been called no more data can be added using the
- * [update] method. If [update] is called after the first call to [digest] a
+ * Once the [close] method has been called no more data can be added using the
+ * [add] method. If [add] is called after the first call to [close] a
* HashException is thrown.
*
* If multiple instances of a given Hash is needed the [newInstance]
* method can provide a new instance.
*/
+// TODO(floitsch): make Hash implement Sink, StreamSink or similar.
abstract class Hash {
/**
* Add a list of bytes to the hash computation.
*/
- Hash update(List<int> data);
+ add(List<int> data);
/**
* Finish the hash computation and extract the message digest as
* a list of bytes.
*/
- List<int> digest();
+ List<int> close();
/**
* Returns a new instance of this hash function.
@@ -40,7 +36,10 @@
Hash newInstance();
/**
- * Block size of the hash in bytes.
+ * Internal block size of the hash in bytes.
+ *
+ * This is exposed for use by the HMAC class which needs to know the
+ * block size for the [Hash] it is using.
*/
int get blockSize;
}
@@ -72,9 +71,10 @@
/**
* Hash-based Message Authentication Code support.
*
- * The [update] method is used to add data to the message. The [digest] method
- * is used to extract the message authentication code.
+ * The [add] method is used to add data to the message. The [digest] and
+ * [close] methods are used to extract the message authentication code.
*/
+// TODO(floitsch): make Hash implement Sink, StreamSink or similar.
abstract class HMAC {
/**
* Create an [HMAC] object from a [Hash] and a key.
@@ -84,13 +84,30 @@
/**
* Add a list of bytes to the message.
*/
- HMAC update(List<int> data);
+ add(List<int> data);
/**
* Perform the actual computation and extract the message digest
* as a list of bytes.
*/
- List<int> digest();
+ List<int> close();
+
+ /**
+ * Extract the message digest as a list of bytes without closing [this].
+ */
+ List<int> get digest;
+
+ /**
+ * Verify that the HMAC computed for the data so far matches the
+ * given message digest.
+ *
+ * This method should be used instead of memcmp-style comparisons
+ * to avoid leaking information via timing.
+ *
+ * Throws an exception if the given digest does not have the same
+ * size as the digest computed by this HMAC instance.
+ */
+ bool verify(List<int> digest);
}
/**
diff --git a/sdk/lib/crypto/crypto_sources.gypi b/sdk/lib/crypto/crypto_sources.gypi
new file mode 100644
index 0000000..b5a934a
--- /dev/null
+++ b/sdk/lib/crypto/crypto_sources.gypi
@@ -0,0 +1,20 @@
+# 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.
+
+# This file contains all sources for the dart:crypto library.
+#
+# TODO(ager): crypto_base.dart should be removed when the
+# VM can use the 'library' and 'part' directives for libraries.
+# At that point crypto.dart should be the only crypto library file.
+{
+ 'sources': [
+ 'crypto_base.dart',
+ 'crypto_utils.dart',
+ 'hash_utils.dart',
+ 'hmac.dart',
+ 'md5.dart',
+ 'sha1.dart',
+ 'sha256.dart',
+ ],
+}
diff --git a/sdk/lib/html/dart2js/html_dart2js.dart b/sdk/lib/html/dart2js/html_dart2js.dart
index 78346e6..7cf2fcc 100644
--- a/sdk/lib/html/dart2js/html_dart2js.dart
+++ b/sdk/lib/html/dart2js/html_dart2js.dart
@@ -7,6 +7,7 @@
import 'dart:isolate';
import 'dart:json' as json;
import 'dart:math';
+import 'dart:web_sql';
import 'dart:svg' as svg;
import 'dart:web_audio' as web_audio;
import 'dart:_js_helper' show convertDartClosureToJS, Creates, JavaScriptIndexingBehavior, JSName, Null, Returns;
@@ -16,7 +17,8 @@
// 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.
-// DO NOT EDIT
+// DO NOT EDIT - unless you are editing documentation as per:
+// https://code.google.com/p/dart/wiki/ContributingHTMLDocumentation
// Auto-generated dart:html library.
/// The Dart HTML library.
@@ -1476,7 +1478,7 @@
'#.lineDashOffset || #.webkitLineDashOffset', this, this);
@DomName('CanvasRenderingContext2D.lineDashOffset')
- void set lineDashOffset(num value) => JS('void',
+ void set lineDashOffset(num value) => JS('void',
'typeof #.lineDashOffset != "undefined" ? #.lineDashOffset = # : '
'#.webkitLineDashOffset = #', this, this, value, this, value);
}
@@ -2193,6 +2195,8 @@
static const int CSS_VH = 27;
+ static const int CSS_VMAX = 29;
+
static const int CSS_VMIN = 28;
static const int CSS_VW = 26;
@@ -5961,77 +5965,6 @@
@DocsEditable
-@DomName('Database')
-@SupportedBrowser(SupportedBrowser.CHROME)
-@SupportedBrowser(SupportedBrowser.SAFARI)
-@Experimental
-class Database native "*Database" {
-
- /// Checks if this type is supported on the current platform.
- static bool get supported => JS('bool', '!!(window.openDatabase)');
-
- @DomName('Database.version')
- @DocsEditable
- final String version;
-
- @DomName('Database.changeVersion')
- @DocsEditable
- void changeVersion(String oldVersion, String newVersion, [SqlTransactionCallback callback, SqlTransactionErrorCallback errorCallback, VoidCallback successCallback]) native;
-
- @DomName('Database.readTransaction')
- @DocsEditable
- void readTransaction(SqlTransactionCallback callback, [SqlTransactionErrorCallback errorCallback, VoidCallback successCallback]) native;
-
- @DomName('Database.transaction')
- @DocsEditable
- void transaction(SqlTransactionCallback callback, [SqlTransactionErrorCallback errorCallback, VoidCallback successCallback]) native;
-}
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-typedef void DatabaseCallback(database);
-// 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.
-
-
-@DocsEditable
-@DomName('DatabaseSync')
-@SupportedBrowser(SupportedBrowser.CHROME)
-@SupportedBrowser(SupportedBrowser.SAFARI)
-@Experimental
-class DatabaseSync native "*DatabaseSync" {
-
- @DomName('DatabaseSync.lastErrorMessage')
- @DocsEditable
- final String lastErrorMessage;
-
- @DomName('DatabaseSync.version')
- @DocsEditable
- final String version;
-
- @DomName('DatabaseSync.changeVersion')
- @DocsEditable
- void changeVersion(String oldVersion, String newVersion, [SqlTransactionSyncCallback callback]) native;
-
- @DomName('DatabaseSync.readTransaction')
- @DocsEditable
- void readTransaction(SqlTransactionSyncCallback callback) native;
-
- @DomName('DatabaseSync.transaction')
- @DocsEditable
- void transaction(SqlTransactionSyncCallback callback) native;
-}
-// 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.
-
-
-@DocsEditable
@DomName('DedicatedWorkerContext')
class DedicatedWorkerContext extends WorkerContext native "*DedicatedWorkerContext" {
@@ -8502,7 +8435,7 @@
_ElementCssClassSet(this._element);
Set<String> readClasses() {
- var s = new Set<String>();
+ var s = new LinkedHashSet<String>();
var classname = _element.$dom_className;
for (String name in classname.split(' ')) {
@@ -11163,7 +11096,7 @@
@DomName('Float32Array.fromBuffer')
@DocsEditable
- factory Float32Array.fromBuffer(ArrayBuffer buffer, [int byteOffset, int length]) =>
+ factory Float32Array.fromBuffer(ArrayBuffer buffer, [int byteOffset, int length]) =>
_TypedArrayFactoryProvider.createFloat32Array_fromBuffer(buffer, byteOffset, length);
static const int BYTES_PER_ELEMENT = 4;
@@ -11379,7 +11312,7 @@
@DomName('Float64Array.fromBuffer')
@DocsEditable
- factory Float64Array.fromBuffer(ArrayBuffer buffer, [int byteOffset, int length]) =>
+ factory Float64Array.fromBuffer(ArrayBuffer buffer, [int byteOffset, int length]) =>
_TypedArrayFactoryProvider.createFloat64Array_fromBuffer(buffer, byteOffset, length);
static const int BYTES_PER_ELEMENT = 8;
@@ -11580,6 +11513,21 @@
@DocsEditable
+@DomName('FocusEvent')
+class FocusEvent extends UIEvent native "*FocusEvent" {
+
+ EventTarget get relatedTarget => _convertNativeToDart_EventTarget(this._relatedTarget);
+ @JSName('relatedTarget')
+ @DomName('FocusEvent.relatedTarget')
+ @DocsEditable
+ final dynamic _relatedTarget;
+}
+// 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.
+
+
+@DocsEditable
@DomName('FormData')
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.FIREFOX)
@@ -11708,18 +11656,113 @@
@DomName('Geolocation')
class Geolocation native "*Geolocation" {
- @DomName('Geolocation.clearWatch')
- @DocsEditable
- void clearWatch(int watchId) native;
-
@DomName('Geolocation.getCurrentPosition')
- @DocsEditable
- void getCurrentPosition(PositionCallback successCallback, [PositionErrorCallback errorCallback, Object options]) native;
+ Future<Geoposition> getCurrentPosition({bool enableHighAccuracy,
+ Duration timeout, Duration maximumAge}) {
+ var options = {};
+ if (enableHighAccuracy != null) {
+ options['enableHighAccuracy'] = enableHighAccuracy;
+ }
+ if (timeout != null) {
+ options['timeout'] = timeout.inMilliseconds;
+ }
+ if (maximumAge != null) {
+ options['maximumAge'] = maximumAge.inMilliseconds;
+ }
+ var completer = new Completer<Geoposition>();
+ try {
+ $dom_getCurrentPosition(
+ (position) {
+ completer.complete(_ensurePosition(position));
+ },
+ (error) {
+ completer.completeError(error);
+ },
+ options);
+ } catch (e, stacktrace) {
+ completer.completeError(e, stacktrace);
+ }
+ return completer.future;
+ }
@DomName('Geolocation.watchPosition')
+ Stream<Geoposition> watchPosition({bool enableHighAccuracy,
+ Duration timeout, Duration maximumAge}) {
+
+ var options = {};
+ if (enableHighAccuracy != null) {
+ options['enableHighAccuracy'] = enableHighAccuracy;
+ }
+ if (timeout != null) {
+ options['timeout'] = timeout.inMilliseconds;
+ }
+ if (maximumAge != null) {
+ options['maximumAge'] = maximumAge.inMilliseconds;
+ }
+
+ int watchId;
+ var controller;
+ controller = new StreamController<Geoposition>(
+ onSubscriptionStateChange: () {
+ if (controller.hasSubscribers) {
+ assert(watchId == null);
+ watchId = $dom_watchPosition(
+ (position) {
+ controller.add(_ensurePosition(position));
+ },
+ (error) {
+ controller.signalError(error);
+ },
+ options);
+ } else {
+ assert(watchId != null);
+ $dom_clearWatch(watchId);
+ }
+ });
+
+ return controller.stream;
+ }
+
+ Geoposition _ensurePosition(domPosition) {
+ try {
+ // Firefox may throw on this.
+ if (domPosition is Geoposition) {
+ return domPosition;
+ }
+ } catch(e) {}
+ return new _GeopositionWrapper(domPosition);
+ }
+
+ @JSName('clearWatch')
+ @DomName('Geolocation.clearWatch')
@DocsEditable
- int watchPosition(PositionCallback successCallback, [PositionErrorCallback errorCallback, Object options]) native;
+ void $dom_clearWatch(int watchId) native;
+
+ @JSName('getCurrentPosition')
+ @DomName('Geolocation.getCurrentPosition')
+ @DocsEditable
+ void $dom_getCurrentPosition(_PositionCallback successCallback, [_PositionErrorCallback errorCallback, Object options]) native;
+
+ @JSName('watchPosition')
+ @DomName('Geolocation.watchPosition')
+ @DocsEditable
+ int $dom_watchPosition(_PositionCallback successCallback, [_PositionErrorCallback errorCallback, Object options]) native;
}
+
+/**
+ * Wrapper for Firefox- it returns an object which we cannot map correctly.
+ * Basically Firefox was returning a [xpconnect wrapped nsIDOMGeoPosition] but
+ * which has further oddities.
+ */
+class _GeopositionWrapper implements Geoposition {
+ var _ptr;
+ _GeopositionWrapper(this._ptr);
+
+ Coordinates get coords => JS('Coordinates', '#.coords', _ptr);
+ int get timestamp => JS('int', '#.timestamp', _ptr);
+}
+
+
// 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.
@@ -14130,7 +14173,7 @@
@DomName('Int16Array.fromBuffer')
@DocsEditable
- factory Int16Array.fromBuffer(ArrayBuffer buffer, [int byteOffset, int length]) =>
+ factory Int16Array.fromBuffer(ArrayBuffer buffer, [int byteOffset, int length]) =>
_TypedArrayFactoryProvider.createInt16Array_fromBuffer(buffer, byteOffset, length);
static const int BYTES_PER_ELEMENT = 2;
@@ -14346,7 +14389,7 @@
@DomName('Int32Array.fromBuffer')
@DocsEditable
- factory Int32Array.fromBuffer(ArrayBuffer buffer, [int byteOffset, int length]) =>
+ factory Int32Array.fromBuffer(ArrayBuffer buffer, [int byteOffset, int length]) =>
_TypedArrayFactoryProvider.createInt32Array_fromBuffer(buffer, byteOffset, length);
static const int BYTES_PER_ELEMENT = 4;
@@ -14562,7 +14605,7 @@
@DomName('Int8Array.fromBuffer')
@DocsEditable
- factory Int8Array.fromBuffer(ArrayBuffer buffer, [int byteOffset, int length]) =>
+ factory Int8Array.fromBuffer(ArrayBuffer buffer, [int byteOffset, int length]) =>
_TypedArrayFactoryProvider.createInt8Array_fromBuffer(buffer, byteOffset, length);
static const int BYTES_PER_ELEMENT = 1;
@@ -14819,6 +14862,10 @@
@DomName('JavaScriptCallFrame.scopeType')
@DocsEditable
int scopeType(int scopeIndex) native;
+
+ @DomName('JavaScriptCallFrame.setVariableValue')
+ @DocsEditable
+ Object setVariableValue(int scopeIndex, String variableName, Object newValue) native;
}
// 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
@@ -18389,7 +18436,7 @@
// WARNING: Do not edit - generated code.
-typedef void PositionCallback(Geoposition position);
+typedef void _PositionCallback(Geoposition position);
// 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.
@@ -18420,7 +18467,7 @@
// WARNING: Do not edit - generated code.
-typedef void PositionErrorCallback(PositionError error);
+typedef void _PositionErrorCallback(PositionError error);
// 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.
@@ -18965,17 +19012,17 @@
}
/**
- * Checks if Real Time Communication (RTC) APIs are supported and enabled on
+ * Checks if Real Time Communication (RTC) APIs are supported and enabled on
* the current platform.
*/
static bool get supported {
// Currently in Firefox some of the RTC elements are defined but throw an
- // error unless the user has specifically enabled them in their
+ // error unless the user has specifically enabled them in their
// about:config. So we have to construct an element to actually test if RTC
// is supported at at the given time.
try {
var c = new RtcPeerConnection({"iceServers": [ {"url":"stun:foo.com"}]});
- return c is RtcPeerConnection;
+ return c is RtcPeerConnection;
} catch (_) {}
return false;
}
@@ -19024,12 +19071,6 @@
@DocsEditable
final RtcSessionDescription localDescription;
- @DomName('RTCPeerConnection.localStreams')
- @DocsEditable
- @Returns('_MediaStreamList')
- @Creates('_MediaStreamList')
- final List<MediaStream> localStreams;
-
@DomName('RTCPeerConnection.readyState')
@DocsEditable
final String readyState;
@@ -19038,12 +19079,6 @@
@DocsEditable
final RtcSessionDescription remoteDescription;
- @DomName('RTCPeerConnection.remoteStreams')
- @DocsEditable
- @Returns('_MediaStreamList')
- @Creates('_MediaStreamList')
- final List<MediaStream> remoteStreams;
-
@DomName('RTCPeerConnection.signalingState')
@DocsEditable
final String signalingState;
@@ -19101,6 +19136,11 @@
@DocsEditable
void _createAnswer_2(RtcSessionDescriptionCallback successCallback, RtcErrorCallback failureCallback) native;
+ @JSName('createDTMFSender')
+ @DomName('RTCPeerConnection.createDTMFSender')
+ @DocsEditable
+ RtcdtmfSender createDtmfSender(MediaStreamTrack track) native;
+
@DomName('RTCPeerConnection.createDataChannel')
@DocsEditable
RtcDataChannel createDataChannel(String label, [Map options]) {
@@ -19143,6 +19183,14 @@
@DocsEditable
bool dispatchEvent(Event event) native;
+ @DomName('RTCPeerConnection.getLocalStreams')
+ @DocsEditable
+ List<MediaStream> getLocalStreams() native;
+
+ @DomName('RTCPeerConnection.getRemoteStreams')
+ @DocsEditable
+ List<MediaStream> getRemoteStreams() native;
+
@DomName('RTCPeerConnection.getStats')
@DocsEditable
void getStats(RtcStatsCallback successCallback, MediaStreamTrack selector) native;
@@ -19307,42 +19355,64 @@
// 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.
-// WARNING: Do not edit - generated code.
+@DocsEditable
+@DomName('RTCDTMFSender')
+class RtcdtmfSender extends EventTarget native "*RTCDTMFSender" {
-typedef void SqlStatementCallback(SqlTransaction transaction, SqlResultSet resultSet);
+ @JSName('canInsertDTMF')
+ @DomName('RTCDTMFSender.canInsertDTMF')
+ @DocsEditable
+ final bool canInsertDtmf;
+
+ @DomName('RTCDTMFSender.duration')
+ @DocsEditable
+ final int duration;
+
+ @DomName('RTCDTMFSender.interToneGap')
+ @DocsEditable
+ final int interToneGap;
+
+ @DomName('RTCDTMFSender.toneBuffer')
+ @DocsEditable
+ final String toneBuffer;
+
+ @DomName('RTCDTMFSender.track')
+ @DocsEditable
+ final MediaStreamTrack track;
+
+ @JSName('addEventListener')
+ @DomName('RTCDTMFSender.addEventListener')
+ @DocsEditable
+ void $dom_addEventListener(String type, EventListener listener, [bool useCapture]) native;
+
+ @DomName('RTCDTMFSender.dispatchEvent')
+ @DocsEditable
+ bool dispatchEvent(Event event) native;
+
+ @JSName('insertDTMF')
+ @DomName('RTCDTMFSender.insertDTMF')
+ @DocsEditable
+ void insertDtmf(String tones, [int duration, int interToneGap]) native;
+
+ @JSName('removeEventListener')
+ @DomName('RTCDTMFSender.removeEventListener')
+ @DocsEditable
+ void $dom_removeEventListener(String type, EventListener listener, [bool useCapture]) native;
+}
// 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.
-// WARNING: Do not edit - generated code.
+@DocsEditable
+@DomName('RTCDTMFToneChangeEvent')
+class RtcdtmfToneChangeEvent extends Event native "*RTCDTMFToneChangeEvent" {
-typedef void SqlStatementErrorCallback(SqlTransaction transaction, SqlError error);
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-typedef void SqlTransactionCallback(SqlTransaction transaction);
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-typedef void SqlTransactionErrorCallback(SqlError error);
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-typedef void SqlTransactionSyncCallback(SqlTransactionSync transaction);
+ @DomName('RTCDTMFToneChangeEvent.tone')
+ @DocsEditable
+ final String tone;
+}
// 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.
@@ -20530,330 +20600,6 @@
// BSD-style license that can be found in the LICENSE file.
-@DocsEditable
-@DomName('SQLError')
-class SqlError native "*SQLError" {
-
- static const int CONSTRAINT_ERR = 6;
-
- static const int DATABASE_ERR = 1;
-
- static const int QUOTA_ERR = 4;
-
- static const int SYNTAX_ERR = 5;
-
- static const int TIMEOUT_ERR = 7;
-
- static const int TOO_LARGE_ERR = 3;
-
- static const int UNKNOWN_ERR = 0;
-
- static const int VERSION_ERR = 2;
-
- @DomName('SQLError.code')
- @DocsEditable
- final int code;
-
- @DomName('SQLError.message')
- @DocsEditable
- final String message;
-}
-// 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.
-
-
-@DocsEditable
-@DomName('SQLException')
-class SqlException native "*SQLException" {
-
- static const int CONSTRAINT_ERR = 6;
-
- static const int DATABASE_ERR = 1;
-
- static const int QUOTA_ERR = 4;
-
- static const int SYNTAX_ERR = 5;
-
- static const int TIMEOUT_ERR = 7;
-
- static const int TOO_LARGE_ERR = 3;
-
- static const int UNKNOWN_ERR = 0;
-
- static const int VERSION_ERR = 2;
-
- @DomName('SQLException.code')
- @DocsEditable
- final int code;
-
- @DomName('SQLException.message')
- @DocsEditable
- final String message;
-}
-// 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.
-
-
-@DocsEditable
-@DomName('SQLResultSet')
-class SqlResultSet native "*SQLResultSet" {
-
- @DomName('SQLResultSet.insertId')
- @DocsEditable
- final int insertId;
-
- @DomName('SQLResultSet.rows')
- @DocsEditable
- final SqlResultSetRowList rows;
-
- @DomName('SQLResultSet.rowsAffected')
- @DocsEditable
- final int rowsAffected;
-}
-// 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.
-
-
-@DocsEditable
-@DomName('SQLResultSetRowList')
-class SqlResultSetRowList implements JavaScriptIndexingBehavior, List<Map> native "*SQLResultSetRowList" {
-
- @DomName('SQLResultSetRowList.length')
- @DocsEditable
- int get length => JS("int", "#.length", this);
-
- Map operator[](int index) => JS("Map", "#[#]", this, index);
-
- void operator[]=(int index, Map value) {
- throw new UnsupportedError("Cannot assign element of immutable List.");
- }
- // -- start List<Map> mixins.
- // Map is the element type.
-
- // From Iterable<Map>:
-
- Iterator<Map> get iterator {
- // Note: NodeLists are not fixed size. And most probably length shouldn't
- // be cached in both iterator _and_ forEach method. For now caching it
- // for consistency.
- return new FixedSizeListIterator<Map>(this);
- }
-
- dynamic reduce(dynamic initialValue, dynamic combine(dynamic, Map)) {
- return IterableMixinWorkaround.reduce(this, initialValue, combine);
- }
-
- bool contains(Map element) => IterableMixinWorkaround.contains(this, element);
-
- void forEach(void f(Map element)) => IterableMixinWorkaround.forEach(this, f);
-
- String join([String separator]) =>
- IterableMixinWorkaround.joinList(this, separator);
-
- Iterable map(f(Map element)) =>
- IterableMixinWorkaround.mapList(this, f);
-
- Iterable<Map> where(bool f(Map element)) =>
- IterableMixinWorkaround.where(this, f);
-
- Iterable expand(Iterable f(Map element)) =>
- IterableMixinWorkaround.expand(this, f);
-
- bool every(bool f(Map element)) => IterableMixinWorkaround.every(this, f);
-
- bool any(bool f(Map element)) => IterableMixinWorkaround.any(this, f);
-
- List<Map> toList() => new List<Map>.from(this);
- Set<Map> toSet() => new Set<Map>.from(this);
-
- bool get isEmpty => this.length == 0;
-
- Iterable<Map> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
- Iterable<Map> takeWhile(bool test(Map value)) {
- return IterableMixinWorkaround.takeWhile(this, test);
- }
-
- Iterable<Map> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
- Iterable<Map> skipWhile(bool test(Map value)) {
- return IterableMixinWorkaround.skipWhile(this, test);
- }
-
- Map firstMatching(bool test(Map value), { Map orElse() }) {
- return IterableMixinWorkaround.firstMatching(this, test, orElse);
- }
-
- Map lastMatching(bool test(Map value), {Map orElse()}) {
- return IterableMixinWorkaround.lastMatchingInList(this, test, orElse);
- }
-
- Map singleMatching(bool test(Map value)) {
- return IterableMixinWorkaround.singleMatching(this, test);
- }
-
- Map elementAt(int index) {
- return this[index];
- }
-
- // From Collection<Map>:
-
- void add(Map value) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void addLast(Map value) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void addAll(Iterable<Map> iterable) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- // From List<Map>:
- void set length(int value) {
- throw new UnsupportedError("Cannot resize immutable List.");
- }
-
- void clear() {
- throw new UnsupportedError("Cannot clear immutable List.");
- }
-
- Iterable<Map> get reversed {
- return IterableMixinWorkaround.reversedList(this);
- }
-
- void sort([int compare(Map a, Map b)]) {
- throw new UnsupportedError("Cannot sort immutable List.");
- }
-
- int indexOf(Map element, [int start = 0]) =>
- Lists.indexOf(this, element, start, this.length);
-
- int lastIndexOf(Map element, [int start]) {
- if (start == null) start = length - 1;
- return Lists.lastIndexOf(this, element, start);
- }
-
- Map get first {
- if (this.length > 0) return this[0];
- throw new StateError("No elements");
- }
-
- Map get last {
- if (this.length > 0) return this[this.length - 1];
- throw new StateError("No elements");
- }
-
- Map get single {
- if (length == 1) return this[0];
- if (length == 0) throw new StateError("No elements");
- throw new StateError("More than one element");
- }
-
- Map min([int compare(Map a, Map b)]) =>
- IterableMixinWorkaround.min(this, compare);
-
- Map max([int compare(Map a, Map b)]) =>
- IterableMixinWorkaround.max(this, compare);
-
- Map removeAt(int pos) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- Map removeLast() {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void remove(Object object) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void removeAll(Iterable elements) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void retainAll(Iterable elements) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void removeMatching(bool test(Map element)) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void retainMatching(bool test(Map element)) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void setRange(int start, int rangeLength, List<Map> from, [int startFrom]) {
- throw new UnsupportedError("Cannot setRange on immutable List.");
- }
-
- void removeRange(int start, int rangeLength) {
- throw new UnsupportedError("Cannot removeRange on immutable List.");
- }
-
- void insertRange(int start, int rangeLength, [Map initialValue]) {
- throw new UnsupportedError("Cannot insertRange on immutable List.");
- }
-
- List<Map> getRange(int start, int rangeLength) =>
- Lists.getRange(this, start, rangeLength, <Map>[]);
-
- // -- end List<Map> mixins.
-
- @DomName('SQLResultSetRowList.item')
- @DocsEditable
- @Creates('=Object')
- Map item(int index) {
- return convertNativeToDart_Dictionary(_item_1(index));
- }
- @JSName('item')
- @DomName('SQLResultSetRowList.item')
- @DocsEditable
- @Creates('=Object')
- _item_1(index) native;
-}
-// 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.
-
-
-@DocsEditable
-@DomName('SQLTransaction')
-@SupportedBrowser(SupportedBrowser.CHROME)
-@SupportedBrowser(SupportedBrowser.SAFARI)
-@Experimental
-class SqlTransaction native "*SQLTransaction" {
-
- @DomName('SQLTransaction.executeSql')
- @DocsEditable
- void executeSql(String sqlStatement, List arguments, [SqlStatementCallback callback, SqlStatementErrorCallback errorCallback]) native;
-}
-// 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.
-
-
-@DocsEditable
-@DomName('SQLTransactionSync')
-@SupportedBrowser(SupportedBrowser.CHROME)
-@SupportedBrowser(SupportedBrowser.SAFARI)
-@Experimental
-class SqlTransactionSync native "*SQLTransactionSync" {
-
- @DomName('SQLTransactionSync.executeSql')
- @DocsEditable
- SqlResultSet executeSql(String sqlStatement, List arguments) native;
-}
-// 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.
-
-
/**
* The type used by the
* [Window.localStorage] and [Window.sessionStorage] properties.
@@ -22765,7 +22511,7 @@
@DomName('Uint16Array.fromBuffer')
@DocsEditable
- factory Uint16Array.fromBuffer(ArrayBuffer buffer, [int byteOffset, int length]) =>
+ factory Uint16Array.fromBuffer(ArrayBuffer buffer, [int byteOffset, int length]) =>
_TypedArrayFactoryProvider.createUint16Array_fromBuffer(buffer, byteOffset, length);
static const int BYTES_PER_ELEMENT = 2;
@@ -22981,7 +22727,7 @@
@DomName('Uint32Array.fromBuffer')
@DocsEditable
- factory Uint32Array.fromBuffer(ArrayBuffer buffer, [int byteOffset, int length]) =>
+ factory Uint32Array.fromBuffer(ArrayBuffer buffer, [int byteOffset, int length]) =>
_TypedArrayFactoryProvider.createUint32Array_fromBuffer(buffer, byteOffset, length);
static const int BYTES_PER_ELEMENT = 4;
@@ -23197,7 +22943,7 @@
@DomName('Uint8Array.fromBuffer')
@DocsEditable
- factory Uint8Array.fromBuffer(ArrayBuffer buffer, [int byteOffset, int length]) =>
+ factory Uint8Array.fromBuffer(ArrayBuffer buffer, [int byteOffset, int length]) =>
_TypedArrayFactoryProvider.createUint8Array_fromBuffer(buffer, byteOffset, length);
static const int BYTES_PER_ELEMENT = 1;
@@ -23413,7 +23159,7 @@
@DomName('Uint8ClampedArray.fromBuffer')
@DocsEditable
- factory Uint8ClampedArray.fromBuffer(ArrayBuffer buffer, [int byteOffset, int length]) =>
+ factory Uint8ClampedArray.fromBuffer(ArrayBuffer buffer, [int byteOffset, int length]) =>
_TypedArrayFactoryProvider.createUint8ClampedArray_fromBuffer(buffer, byteOffset, length);
// Use implementation from Uint8Array.
@@ -25607,6 +25353,12 @@
}
+ static const int DOM_DELTA_LINE = 0x01;
+
+ static const int DOM_DELTA_PAGE = 0x02;
+
+ static const int DOM_DELTA_PIXEL = 0x00;
+
@JSName('webkitDirectionInvertedFromDevice')
@DomName('WheelEvent.webkitDirectionInvertedFromDevice')
@DocsEditable
@@ -25679,12 +25431,13 @@
'deltaX is not supported');
}
+ @DomName('WheelEvent.deltaMode')
int get deltaMode {
- if (JS('bool', '!!#.deltaMode', this)) {
- // If not available then we're poly-filling and doing pixel scroll.
- return 0;
+ if (JS('bool', '!!(#.deltaMode)', this)) {
+ return JS('int', '#.deltaMode', this);
}
- return this._deltaMode;
+ // If not available then we're poly-filling and doing pixel scroll.
+ return 0;
}
num get _deltaY => JS('num', '#.deltaY', this);
@@ -25692,7 +25445,6 @@
num get _wheelDelta => JS('num', '#.wheelDelta', this);
num get _wheelDeltaX => JS('num', '#.wheelDeltaX', this);
num get _detail => JS('num', '#.detail', this);
- int get _deltaMode => JS('int', '#.deltaMode', this);
bool get _hasInitMouseScrollEvent =>
JS('bool', '!!(#.initMouseScrollEvent)', this);
@@ -25756,7 +25508,7 @@
* frame unwinds, causing the future to complete after all processing has
* completed for the current event, but before any subsequent events.
*/
- void setImmediate(TimeoutHandler callback) {
+ void setImmediate(TimeoutHandler callback) {
_addMicrotaskCallback(callback);
}
/**
@@ -25787,7 +25539,7 @@
* If you need to later cancel this animation, use [requestAnimationFrame]
* instead.
*
- * Note: The code that runs when the future completes should call
+ * Note: The code that runs when the future completes should call
* [animationFrame] again for the animation to continue.
*/
Future<num> get animationFrame {
@@ -25862,7 +25614,7 @@
/**
* Called to draw an animation frame and then request the window to repaint
- * after [callback] has finished (creating the animation).
+ * after [callback] has finished (creating the animation).
*
* Use this method only if you need to later call [cancelAnimationFrame]. If
* not, the preferred Dart idiom is to set animation frames by calling
@@ -26311,9 +26063,9 @@
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.SAFARI)
@Experimental
- @Creates('Database')
- @Creates('DatabaseSync')
- Database openDatabase(String name, String version, String displayName, int estimatedSize, [DatabaseCallback creationCallback]) native;
+ @Creates('SqlDatabase')
+ @Creates('SqlDatabaseSync')
+ SqlDatabase openDatabase(String name, String version, String displayName, int estimatedSize, [DatabaseCallback creationCallback]) native;
@DomName('DOMWindow.postMessage')
@DocsEditable
@@ -26752,14 +26504,14 @@
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.SAFARI)
@Experimental
- Database openDatabase(String name, String version, String displayName, int estimatedSize, [DatabaseCallback creationCallback]) native;
+ SqlDatabase openDatabase(String name, String version, String displayName, int estimatedSize, [DatabaseCallback creationCallback]) native;
@DomName('WorkerContext.openDatabaseSync')
@DocsEditable
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.SAFARI)
@Experimental
- DatabaseSync openDatabaseSync(String name, String version, String displayName, int estimatedSize, [DatabaseCallback creationCallback]) native;
+ SqlDatabaseSync openDatabaseSync(String name, String version, String displayName, int estimatedSize, [DatabaseCallback creationCallback]) native;
@JSName('removeEventListener')
@DomName('WorkerContext.removeEventListener')
@@ -28370,203 +28122,6 @@
@DocsEditable
-@DomName('MediaStreamList')
-class _MediaStreamList implements JavaScriptIndexingBehavior, List<MediaStream> native "*MediaStreamList" {
-
- @DomName('MediaStreamList.length')
- @DocsEditable
- int get length => JS("int", "#.length", this);
-
- MediaStream operator[](int index) => JS("MediaStream", "#[#]", this, index);
-
- void operator[]=(int index, MediaStream value) {
- throw new UnsupportedError("Cannot assign element of immutable List.");
- }
- // -- start List<MediaStream> mixins.
- // MediaStream is the element type.
-
- // From Iterable<MediaStream>:
-
- Iterator<MediaStream> get iterator {
- // Note: NodeLists are not fixed size. And most probably length shouldn't
- // be cached in both iterator _and_ forEach method. For now caching it
- // for consistency.
- return new FixedSizeListIterator<MediaStream>(this);
- }
-
- dynamic reduce(dynamic initialValue, dynamic combine(dynamic, MediaStream)) {
- return IterableMixinWorkaround.reduce(this, initialValue, combine);
- }
-
- bool contains(MediaStream element) => IterableMixinWorkaround.contains(this, element);
-
- void forEach(void f(MediaStream element)) => IterableMixinWorkaround.forEach(this, f);
-
- String join([String separator]) =>
- IterableMixinWorkaround.joinList(this, separator);
-
- Iterable map(f(MediaStream element)) =>
- IterableMixinWorkaround.mapList(this, f);
-
- Iterable<MediaStream> where(bool f(MediaStream element)) =>
- IterableMixinWorkaround.where(this, f);
-
- Iterable expand(Iterable f(MediaStream element)) =>
- IterableMixinWorkaround.expand(this, f);
-
- bool every(bool f(MediaStream element)) => IterableMixinWorkaround.every(this, f);
-
- bool any(bool f(MediaStream element)) => IterableMixinWorkaround.any(this, f);
-
- List<MediaStream> toList() => new List<MediaStream>.from(this);
- Set<MediaStream> toSet() => new Set<MediaStream>.from(this);
-
- bool get isEmpty => this.length == 0;
-
- Iterable<MediaStream> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
- Iterable<MediaStream> takeWhile(bool test(MediaStream value)) {
- return IterableMixinWorkaround.takeWhile(this, test);
- }
-
- Iterable<MediaStream> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
- Iterable<MediaStream> skipWhile(bool test(MediaStream value)) {
- return IterableMixinWorkaround.skipWhile(this, test);
- }
-
- MediaStream firstMatching(bool test(MediaStream value), { MediaStream orElse() }) {
- return IterableMixinWorkaround.firstMatching(this, test, orElse);
- }
-
- MediaStream lastMatching(bool test(MediaStream value), {MediaStream orElse()}) {
- return IterableMixinWorkaround.lastMatchingInList(this, test, orElse);
- }
-
- MediaStream singleMatching(bool test(MediaStream value)) {
- return IterableMixinWorkaround.singleMatching(this, test);
- }
-
- MediaStream elementAt(int index) {
- return this[index];
- }
-
- // From Collection<MediaStream>:
-
- void add(MediaStream value) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void addLast(MediaStream value) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void addAll(Iterable<MediaStream> iterable) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- // From List<MediaStream>:
- void set length(int value) {
- throw new UnsupportedError("Cannot resize immutable List.");
- }
-
- void clear() {
- throw new UnsupportedError("Cannot clear immutable List.");
- }
-
- Iterable<MediaStream> get reversed {
- return IterableMixinWorkaround.reversedList(this);
- }
-
- void sort([int compare(MediaStream a, MediaStream b)]) {
- throw new UnsupportedError("Cannot sort immutable List.");
- }
-
- int indexOf(MediaStream element, [int start = 0]) =>
- Lists.indexOf(this, element, start, this.length);
-
- int lastIndexOf(MediaStream element, [int start]) {
- if (start == null) start = length - 1;
- return Lists.lastIndexOf(this, element, start);
- }
-
- MediaStream get first {
- if (this.length > 0) return this[0];
- throw new StateError("No elements");
- }
-
- MediaStream get last {
- if (this.length > 0) return this[this.length - 1];
- throw new StateError("No elements");
- }
-
- MediaStream get single {
- if (length == 1) return this[0];
- if (length == 0) throw new StateError("No elements");
- throw new StateError("More than one element");
- }
-
- MediaStream min([int compare(MediaStream a, MediaStream b)]) =>
- IterableMixinWorkaround.min(this, compare);
-
- MediaStream max([int compare(MediaStream a, MediaStream b)]) =>
- IterableMixinWorkaround.max(this, compare);
-
- MediaStream removeAt(int pos) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- MediaStream removeLast() {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void remove(Object object) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void removeAll(Iterable elements) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void retainAll(Iterable elements) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void removeMatching(bool test(MediaStream element)) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void retainMatching(bool test(MediaStream element)) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void setRange(int start, int rangeLength, List<MediaStream> from, [int startFrom]) {
- throw new UnsupportedError("Cannot setRange on immutable List.");
- }
-
- void removeRange(int start, int rangeLength) {
- throw new UnsupportedError("Cannot removeRange on immutable List.");
- }
-
- void insertRange(int start, int rangeLength, [MediaStream initialValue]) {
- throw new UnsupportedError("Cannot insertRange on immutable List.");
- }
-
- List<MediaStream> getRange(int start, int rangeLength) =>
- Lists.getRange(this, start, rangeLength, <MediaStream>[]);
-
- // -- end List<MediaStream> mixins.
-
- @DomName('MediaStreamList.item')
- @DocsEditable
- MediaStream item(int index) native;
-}
-// 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.
-
-
-@DocsEditable
@DomName('NamedNodeMap')
class _NamedNodeMap implements JavaScriptIndexingBehavior, List<Node> native "*NamedNodeMap" {
@@ -30538,9 +30093,9 @@
/**
- * Defines the keycode values for keys that are returned by
+ * Defines the keycode values for keys that are returned by
* KeyboardEvent.keyCode.
- *
+ *
* Important note: There is substantial divergence in how different browsers
* handle keycodes and their variants in different locales/keyboard layouts. We
* provide these constants to help make code processing keys more readable.
@@ -30548,7 +30103,7 @@
abstract class KeyCode {
// These constant names were borrowed from Closure's Keycode enumeration
// class.
- // http://closure-library.googlecode.com/svn/docs/closure_goog_events_keycodes.js.source.html
+ // http://closure-library.googlecode.com/svn/docs/closure_goog_events_keycodes.js.source.html
static const int WIN_KEY_FF_LINUX = 0;
static const int MAC_ENTER = 3;
static const int BACKSPACE = 8;
@@ -30743,12 +30298,12 @@
(keyCode >= A && keyCode <= Z)) {
return true;
}
-
+
// Safari sends zero key code for non-latin characters.
if (_Device.isWebKit && keyCode == 0) {
return true;
}
-
+
return (keyCode == SPACE || keyCode == QUESTION_MARK || keyCode == NUM_PLUS
|| keyCode == NUM_MINUS || keyCode == NUM_PERIOD ||
keyCode == NUM_DIVISION || keyCode == SEMICOLON ||
@@ -32536,7 +32091,7 @@
final int _length; // Cache array length for faster access.
int _position;
T _current;
-
+
FixedSizeListIterator(List<T> array)
: _array = array,
_position = -1,
diff --git a/sdk/lib/html/dartium/html_dartium.dart b/sdk/lib/html/dartium/html_dartium.dart
index 017bfb3..0dc02f2 100644
--- a/sdk/lib/html/dartium/html_dartium.dart
+++ b/sdk/lib/html/dartium/html_dartium.dart
@@ -7,6 +7,7 @@
import 'dart:isolate';
import 'dart:json' as json;
import 'dart:nativewrappers';
+import 'dart:web_sql';
import 'dart:svg' as svg;
import 'dart:web_audio' as web_audio;
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -2762,6 +2763,8 @@
static const int CSS_VH = 27;
+ static const int CSS_VMAX = 29;
+
static const int CSS_VMIN = 28;
static const int CSS_VW = 26;
@@ -6746,85 +6749,6 @@
@DocsEditable
-@DomName('Database')
-@SupportedBrowser(SupportedBrowser.CHROME)
-@SupportedBrowser(SupportedBrowser.SAFARI)
-@Experimental
-class Database extends NativeFieldWrapperClass1 {
- Database.internal();
-
- /// Checks if this type is supported on the current platform.
- static bool get supported => true;
-
- @DomName('Database.version')
- @DocsEditable
- String get version native "Database_version_Getter";
-
- @DomName('Database.changeVersion')
- @DocsEditable
- void changeVersion(String oldVersion, String newVersion, [SqlTransactionCallback callback, SqlTransactionErrorCallback errorCallback, VoidCallback successCallback]) native "Database_changeVersion_Callback";
-
- @DomName('Database.readTransaction')
- @DocsEditable
- void readTransaction(SqlTransactionCallback callback, [SqlTransactionErrorCallback errorCallback, VoidCallback successCallback]) native "Database_readTransaction_Callback";
-
- @DomName('Database.transaction')
- @DocsEditable
- void transaction(SqlTransactionCallback callback, [SqlTransactionErrorCallback errorCallback, VoidCallback successCallback]) native "Database_transaction_Callback";
-
-}
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-typedef void DatabaseCallback(database);
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-@DocsEditable
-@DomName('DatabaseSync')
-@SupportedBrowser(SupportedBrowser.CHROME)
-@SupportedBrowser(SupportedBrowser.SAFARI)
-@Experimental
-class DatabaseSync extends NativeFieldWrapperClass1 {
- DatabaseSync.internal();
-
- @DomName('DatabaseSync.lastErrorMessage')
- @DocsEditable
- String get lastErrorMessage native "DatabaseSync_lastErrorMessage_Getter";
-
- @DomName('DatabaseSync.version')
- @DocsEditable
- String get version native "DatabaseSync_version_Getter";
-
- @DomName('DatabaseSync.changeVersion')
- @DocsEditable
- void changeVersion(String oldVersion, String newVersion, [SqlTransactionSyncCallback callback]) native "DatabaseSync_changeVersion_Callback";
-
- @DomName('DatabaseSync.readTransaction')
- @DocsEditable
- void readTransaction(SqlTransactionSyncCallback callback) native "DatabaseSync_readTransaction_Callback";
-
- @DomName('DatabaseSync.transaction')
- @DocsEditable
- void transaction(SqlTransactionSyncCallback callback) native "DatabaseSync_transaction_Callback";
-
-}
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-@DocsEditable
@DomName('DedicatedWorkerContext')
class DedicatedWorkerContext extends WorkerContext {
DedicatedWorkerContext.internal() : super.internal();
@@ -9263,7 +9187,7 @@
_ElementCssClassSet(this._element);
Set<String> readClasses() {
- var s = new Set<String>();
+ var s = new LinkedHashSet<String>();
var classname = _element.$dom_className;
for (String name in classname.split(' ')) {
@@ -12402,6 +12326,23 @@
@DocsEditable
+@DomName('FocusEvent')
+class FocusEvent extends UIEvent {
+ FocusEvent.internal() : super.internal();
+
+ @DomName('FocusEvent.relatedTarget')
+ @DocsEditable
+ EventTarget get relatedTarget native "FocusEvent_relatedTarget_Getter";
+
+}
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+@DocsEditable
@DomName('FormData')
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.FIREFOX)
@@ -12564,27 +12505,104 @@
// 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.
-// WARNING: Do not edit - generated code.
-
@DocsEditable
@DomName('Geolocation')
class Geolocation extends NativeFieldWrapperClass1 {
+
+ @DomName('Geolocation.getCurrentPosition')
+ Future<Geoposition> getCurrentPosition({bool enableHighAccuracy,
+ Duration timeout, Duration maximumAge}) {
+ var options = {};
+ if (enableHighAccuracy != null) {
+ options['enableHighAccuracy'] = enableHighAccuracy;
+ }
+ if (timeout != null) {
+ options['timeout'] = timeout.inMilliseconds;
+ }
+ if (maximumAge != null) {
+ options['maximumAge'] = maximumAge.inMilliseconds;
+ }
+ var completer = new Completer<Geoposition>();
+ try {
+ $dom_getCurrentPosition(
+ (position) {
+ completer.complete(_ensurePosition(position));
+ },
+ (error) {
+ completer.completeError(error);
+ },
+ options);
+ } catch (e, stacktrace) {
+ completer.completeError(e, stacktrace);
+ }
+ return completer.future;
+ }
+
+ @DomName('Geolocation.watchPosition')
+ Stream<Geoposition> watchPosition({bool enableHighAccuracy,
+ Duration timeout, Duration maximumAge}) {
+
+ var options = {};
+ if (enableHighAccuracy != null) {
+ options['enableHighAccuracy'] = enableHighAccuracy;
+ }
+ if (timeout != null) {
+ options['timeout'] = timeout.inMilliseconds;
+ }
+ if (maximumAge != null) {
+ options['maximumAge'] = maximumAge.inMilliseconds;
+ }
+
+ int watchId;
+ var controller;
+ controller = new StreamController<Geoposition>(
+ onSubscriptionStateChange: () {
+ if (controller.hasSubscribers) {
+ assert(watchId == null);
+ watchId = $dom_watchPosition(
+ (position) {
+ controller.add(_ensurePosition(position));
+ },
+ (error) {
+ controller.signalError(error);
+ },
+ options);
+ } else {
+ assert(watchId != null);
+ $dom_clearWatch(watchId);
+ }
+ });
+
+ return controller.stream;
+ }
+
+ Geoposition _ensurePosition(domPosition) {
+ try {
+ // Firefox may throw on this.
+ if (domPosition is Geoposition) {
+ return domPosition;
+ }
+ } catch(e) {}
+ return new _GeopositionWrapper(domPosition);
+ }
Geolocation.internal();
@DomName('Geolocation.clearWatch')
@DocsEditable
- void clearWatch(int watchId) native "Geolocation_clearWatch_Callback";
+ void $dom_clearWatch(int watchId) native "Geolocation_clearWatch_Callback";
@DomName('Geolocation.getCurrentPosition')
@DocsEditable
- void getCurrentPosition(PositionCallback successCallback, [PositionErrorCallback errorCallback, Object options]) native "Geolocation_getCurrentPosition_Callback";
+ void $dom_getCurrentPosition(_PositionCallback successCallback, [_PositionErrorCallback errorCallback, Object options]) native "Geolocation_getCurrentPosition_Callback";
@DomName('Geolocation.watchPosition')
@DocsEditable
- int watchPosition(PositionCallback successCallback, [PositionErrorCallback errorCallback, Object options]) native "Geolocation_watchPosition_Callback";
-
+ int $dom_watchPosition(_PositionCallback successCallback, [_PositionErrorCallback errorCallback, Object options]) native "Geolocation_watchPosition_Callback";
}
+
+
+
// 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.
@@ -16092,6 +16110,10 @@
@DocsEditable
int scopeType(int scopeIndex) native "JavaScriptCallFrame_scopeType_Callback";
+ @DomName('JavaScriptCallFrame.setVariableValue')
+ @DocsEditable
+ Object setVariableValue(int scopeIndex, String variableName, Object newValue) native "JavaScriptCallFrame_setVariableValue_Callback";
+
}
// 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
@@ -20081,7 +20103,7 @@
// WARNING: Do not edit - generated code.
-typedef void PositionCallback(Geoposition position);
+typedef void _PositionCallback(Geoposition position);
// 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.
@@ -20116,7 +20138,7 @@
// WARNING: Do not edit - generated code.
-typedef void PositionErrorCallback(PositionError error);
+typedef void _PositionErrorCallback(PositionError error);
// 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.
@@ -20820,10 +20842,6 @@
@DocsEditable
RtcSessionDescription get localDescription native "RTCPeerConnection_localDescription_Getter";
- @DomName('RTCPeerConnection.localStreams')
- @DocsEditable
- List<MediaStream> get localStreams native "RTCPeerConnection_localStreams_Getter";
-
@DomName('RTCPeerConnection.readyState')
@DocsEditable
String get readyState native "RTCPeerConnection_readyState_Getter";
@@ -20832,10 +20850,6 @@
@DocsEditable
RtcSessionDescription get remoteDescription native "RTCPeerConnection_remoteDescription_Getter";
- @DomName('RTCPeerConnection.remoteStreams')
- @DocsEditable
- List<MediaStream> get remoteStreams native "RTCPeerConnection_remoteStreams_Getter";
-
@DomName('RTCPeerConnection.signalingState')
@DocsEditable
String get signalingState native "RTCPeerConnection_signalingState_Getter";
@@ -20860,6 +20874,10 @@
@DocsEditable
void createAnswer(RtcSessionDescriptionCallback successCallback, [RtcErrorCallback failureCallback, Map mediaConstraints]) native "RTCPeerConnection_createAnswer_Callback";
+ @DomName('RTCPeerConnection.createDTMFSender')
+ @DocsEditable
+ RtcdtmfSender createDtmfSender(MediaStreamTrack track) native "RTCPeerConnection_createDTMFSender_Callback";
+
@DomName('RTCPeerConnection.createDataChannel')
@DocsEditable
RtcDataChannel createDataChannel(String label, [Map options]) native "RTCPeerConnection_createDataChannel_Callback";
@@ -20872,6 +20890,14 @@
@DocsEditable
bool dispatchEvent(Event event) native "RTCPeerConnection_dispatchEvent_Callback";
+ @DomName('RTCPeerConnection.getLocalStreams')
+ @DocsEditable
+ List<MediaStream> getLocalStreams() native "RTCPeerConnection_getLocalStreams_Callback";
+
+ @DomName('RTCPeerConnection.getRemoteStreams')
+ @DocsEditable
+ List<MediaStream> getRemoteStreams() native "RTCPeerConnection_getRemoteStreams_Callback";
+
@DomName('RTCPeerConnection.getStats')
@DocsEditable
void getStats(RtcStatsCallback successCallback, MediaStreamTrack selector) native "RTCPeerConnection_getStats_Callback";
@@ -21041,7 +21067,69 @@
// WARNING: Do not edit - generated code.
-typedef void SqlStatementCallback(SqlTransaction transaction, SqlResultSet resultSet);
+@DocsEditable
+@DomName('RTCDTMFSender')
+class RtcdtmfSender extends EventTarget {
+ RtcdtmfSender.internal() : super.internal();
+
+ @DomName('RTCDTMFSender.canInsertDTMF')
+ @DocsEditable
+ bool get canInsertDtmf native "RTCDTMFSender_canInsertDTMF_Getter";
+
+ @DomName('RTCDTMFSender.duration')
+ @DocsEditable
+ int get duration native "RTCDTMFSender_duration_Getter";
+
+ @DomName('RTCDTMFSender.interToneGap')
+ @DocsEditable
+ int get interToneGap native "RTCDTMFSender_interToneGap_Getter";
+
+ @DomName('RTCDTMFSender.toneBuffer')
+ @DocsEditable
+ String get toneBuffer native "RTCDTMFSender_toneBuffer_Getter";
+
+ @DomName('RTCDTMFSender.track')
+ @DocsEditable
+ MediaStreamTrack get track native "RTCDTMFSender_track_Getter";
+
+ @DomName('RTCDTMFSender.addEventListener')
+ @DocsEditable
+ void $dom_addEventListener(String type, EventListener listener, [bool useCapture]) native "RTCDTMFSender_addEventListener_Callback";
+
+ @DomName('RTCDTMFSender.dispatchEvent')
+ @DocsEditable
+ bool dispatchEvent(Event event) native "RTCDTMFSender_dispatchEvent_Callback";
+
+ void insertDtmf(String tones, [int duration, int interToneGap]) {
+ if (?interToneGap) {
+ _insertDTMF_1(tones, duration, interToneGap);
+ return;
+ }
+ if (?duration) {
+ _insertDTMF_2(tones, duration);
+ return;
+ }
+ _insertDTMF_3(tones);
+ return;
+ }
+
+ @DomName('RTCDTMFSender._insertDTMF_1')
+ @DocsEditable
+ void _insertDTMF_1(tones, duration, interToneGap) native "RTCDTMFSender__insertDTMF_1_Callback";
+
+ @DomName('RTCDTMFSender._insertDTMF_2')
+ @DocsEditable
+ void _insertDTMF_2(tones, duration) native "RTCDTMFSender__insertDTMF_2_Callback";
+
+ @DomName('RTCDTMFSender._insertDTMF_3')
+ @DocsEditable
+ void _insertDTMF_3(tones) native "RTCDTMFSender__insertDTMF_3_Callback";
+
+ @DomName('RTCDTMFSender.removeEventListener')
+ @DocsEditable
+ void $dom_removeEventListener(String type, EventListener listener, [bool useCapture]) native "RTCDTMFSender_removeEventListener_Callback";
+
+}
// 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.
@@ -21049,31 +21137,16 @@
// WARNING: Do not edit - generated code.
-typedef void SqlStatementErrorCallback(SqlTransaction transaction, SqlError error);
-// 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.
+@DocsEditable
+@DomName('RTCDTMFToneChangeEvent')
+class RtcdtmfToneChangeEvent extends Event {
+ RtcdtmfToneChangeEvent.internal() : super.internal();
-// WARNING: Do not edit - generated code.
+ @DomName('RTCDTMFToneChangeEvent.tone')
+ @DocsEditable
+ String get tone native "RTCDTMFToneChangeEvent_tone_Getter";
-
-typedef void SqlTransactionCallback(SqlTransaction transaction);
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-typedef void SqlTransactionErrorCallback(SqlError error);
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-typedef void SqlTransactionSyncCallback(SqlTransactionSync transaction);
+}
// 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.
@@ -22480,346 +22553,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.
-// WARNING: Do not edit - generated code.
-
-
-@DocsEditable
-@DomName('SQLError')
-class SqlError extends NativeFieldWrapperClass1 {
- SqlError.internal();
-
- static const int CONSTRAINT_ERR = 6;
-
- static const int DATABASE_ERR = 1;
-
- static const int QUOTA_ERR = 4;
-
- static const int SYNTAX_ERR = 5;
-
- static const int TIMEOUT_ERR = 7;
-
- static const int TOO_LARGE_ERR = 3;
-
- static const int UNKNOWN_ERR = 0;
-
- static const int VERSION_ERR = 2;
-
- @DomName('SQLError.code')
- @DocsEditable
- int get code native "SQLError_code_Getter";
-
- @DomName('SQLError.message')
- @DocsEditable
- String get message native "SQLError_message_Getter";
-
-}
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-@DocsEditable
-@DomName('SQLException')
-class SqlException extends NativeFieldWrapperClass1 {
- SqlException.internal();
-
- static const int CONSTRAINT_ERR = 6;
-
- static const int DATABASE_ERR = 1;
-
- static const int QUOTA_ERR = 4;
-
- static const int SYNTAX_ERR = 5;
-
- static const int TIMEOUT_ERR = 7;
-
- static const int TOO_LARGE_ERR = 3;
-
- static const int UNKNOWN_ERR = 0;
-
- static const int VERSION_ERR = 2;
-
- @DomName('SQLException.code')
- @DocsEditable
- int get code native "SQLException_code_Getter";
-
- @DomName('SQLException.message')
- @DocsEditable
- String get message native "SQLException_message_Getter";
-
-}
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-@DocsEditable
-@DomName('SQLResultSet')
-class SqlResultSet extends NativeFieldWrapperClass1 {
- SqlResultSet.internal();
-
- @DomName('SQLResultSet.insertId')
- @DocsEditable
- int get insertId native "SQLResultSet_insertId_Getter";
-
- @DomName('SQLResultSet.rows')
- @DocsEditable
- SqlResultSetRowList get rows native "SQLResultSet_rows_Getter";
-
- @DomName('SQLResultSet.rowsAffected')
- @DocsEditable
- int get rowsAffected native "SQLResultSet_rowsAffected_Getter";
-
-}
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-@DocsEditable
-@DomName('SQLResultSetRowList')
-class SqlResultSetRowList extends NativeFieldWrapperClass1 implements List<Map> {
- SqlResultSetRowList.internal();
-
- @DomName('SQLResultSetRowList.length')
- @DocsEditable
- int get length native "SQLResultSetRowList_length_Getter";
-
- Map operator[](int index) native "SQLResultSetRowList_item_Callback";
-
- void operator[]=(int index, Map value) {
- throw new UnsupportedError("Cannot assign element of immutable List.");
- }
- // -- start List<Map> mixins.
- // Map is the element type.
-
- // From Iterable<Map>:
-
- Iterator<Map> get iterator {
- // Note: NodeLists are not fixed size. And most probably length shouldn't
- // be cached in both iterator _and_ forEach method. For now caching it
- // for consistency.
- return new FixedSizeListIterator<Map>(this);
- }
-
- dynamic reduce(dynamic initialValue, dynamic combine(dynamic, Map)) {
- return IterableMixinWorkaround.reduce(this, initialValue, combine);
- }
-
- bool contains(Map element) => IterableMixinWorkaround.contains(this, element);
-
- void forEach(void f(Map element)) => IterableMixinWorkaround.forEach(this, f);
-
- String join([String separator]) =>
- IterableMixinWorkaround.joinList(this, separator);
-
- Iterable map(f(Map element)) =>
- IterableMixinWorkaround.mapList(this, f);
-
- Iterable<Map> where(bool f(Map element)) =>
- IterableMixinWorkaround.where(this, f);
-
- Iterable expand(Iterable f(Map element)) =>
- IterableMixinWorkaround.expand(this, f);
-
- bool every(bool f(Map element)) => IterableMixinWorkaround.every(this, f);
-
- bool any(bool f(Map element)) => IterableMixinWorkaround.any(this, f);
-
- List<Map> toList() => new List<Map>.from(this);
- Set<Map> toSet() => new Set<Map>.from(this);
-
- bool get isEmpty => this.length == 0;
-
- Iterable<Map> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
- Iterable<Map> takeWhile(bool test(Map value)) {
- return IterableMixinWorkaround.takeWhile(this, test);
- }
-
- Iterable<Map> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
- Iterable<Map> skipWhile(bool test(Map value)) {
- return IterableMixinWorkaround.skipWhile(this, test);
- }
-
- Map firstMatching(bool test(Map value), { Map orElse() }) {
- return IterableMixinWorkaround.firstMatching(this, test, orElse);
- }
-
- Map lastMatching(bool test(Map value), {Map orElse()}) {
- return IterableMixinWorkaround.lastMatchingInList(this, test, orElse);
- }
-
- Map singleMatching(bool test(Map value)) {
- return IterableMixinWorkaround.singleMatching(this, test);
- }
-
- Map elementAt(int index) {
- return this[index];
- }
-
- // From Collection<Map>:
-
- void add(Map value) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void addLast(Map value) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void addAll(Iterable<Map> iterable) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- // From List<Map>:
- void set length(int value) {
- throw new UnsupportedError("Cannot resize immutable List.");
- }
-
- void clear() {
- throw new UnsupportedError("Cannot clear immutable List.");
- }
-
- Iterable<Map> get reversed {
- return IterableMixinWorkaround.reversedList(this);
- }
-
- void sort([int compare(Map a, Map b)]) {
- throw new UnsupportedError("Cannot sort immutable List.");
- }
-
- int indexOf(Map element, [int start = 0]) =>
- Lists.indexOf(this, element, start, this.length);
-
- int lastIndexOf(Map element, [int start]) {
- if (start == null) start = length - 1;
- return Lists.lastIndexOf(this, element, start);
- }
-
- Map get first {
- if (this.length > 0) return this[0];
- throw new StateError("No elements");
- }
-
- Map get last {
- if (this.length > 0) return this[this.length - 1];
- throw new StateError("No elements");
- }
-
- Map get single {
- if (length == 1) return this[0];
- if (length == 0) throw new StateError("No elements");
- throw new StateError("More than one element");
- }
-
- Map min([int compare(Map a, Map b)]) =>
- IterableMixinWorkaround.min(this, compare);
-
- Map max([int compare(Map a, Map b)]) =>
- IterableMixinWorkaround.max(this, compare);
-
- Map removeAt(int pos) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- Map removeLast() {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void remove(Object object) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void removeAll(Iterable elements) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void retainAll(Iterable elements) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void removeMatching(bool test(Map element)) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void retainMatching(bool test(Map element)) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void setRange(int start, int rangeLength, List<Map> from, [int startFrom]) {
- throw new UnsupportedError("Cannot setRange on immutable List.");
- }
-
- void removeRange(int start, int rangeLength) {
- throw new UnsupportedError("Cannot removeRange on immutable List.");
- }
-
- void insertRange(int start, int rangeLength, [Map initialValue]) {
- throw new UnsupportedError("Cannot insertRange on immutable List.");
- }
-
- List<Map> getRange(int start, int rangeLength) =>
- Lists.getRange(this, start, rangeLength, <Map>[]);
-
- // -- end List<Map> mixins.
-
- @DomName('SQLResultSetRowList.item')
- @DocsEditable
- Map item(int index) native "SQLResultSetRowList_item_Callback";
-
-}
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-@DocsEditable
-@DomName('SQLTransaction')
-@SupportedBrowser(SupportedBrowser.CHROME)
-@SupportedBrowser(SupportedBrowser.SAFARI)
-@Experimental
-class SqlTransaction extends NativeFieldWrapperClass1 {
- SqlTransaction.internal();
-
- @DomName('SQLTransaction.executeSql')
- @DocsEditable
- void executeSql(String sqlStatement, List arguments, [SqlStatementCallback callback, SqlStatementErrorCallback errorCallback]) native "SQLTransaction_executeSql_Callback";
-
-}
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-@DocsEditable
-@DomName('SQLTransactionSync')
-@SupportedBrowser(SupportedBrowser.CHROME)
-@SupportedBrowser(SupportedBrowser.SAFARI)
-@Experimental
-class SqlTransactionSync extends NativeFieldWrapperClass1 {
- SqlTransactionSync.internal();
-
- @DomName('SQLTransactionSync.executeSql')
- @DocsEditable
- SqlResultSet executeSql(String sqlStatement, List arguments) native "SQLTransactionSync_executeSql_Callback";
-
-}
-// 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.
-
/**
* The type used by the
@@ -28106,6 +27839,16 @@
WheelEvent.internal() : super.internal();
+ static const int DOM_DELTA_LINE = 0x01;
+
+ static const int DOM_DELTA_PAGE = 0x02;
+
+ static const int DOM_DELTA_PIXEL = 0x00;
+
+ @DomName('WheelEvent.deltaMode')
+ @DocsEditable
+ int get deltaMode native "WheelEvent_deltaMode_Getter";
+
@DomName('WheelEvent.webkitDirectionInvertedFromDevice')
@DocsEditable
@SupportedBrowser(SupportedBrowser.CHROME)
@@ -28130,9 +27873,6 @@
num get deltaX => $dom_wheelDeltaX;
@DomName('WheelEvent.deltaY')
num get deltaY => $dom_wheelDeltaY;
- @DomName('WheelEvent.deltaMode')
- int get deltaMode => 0;
-
}
// 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
@@ -28585,7 +28325,7 @@
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.SAFARI)
@Experimental
- Database openDatabase(String name, String version, String displayName, int estimatedSize, [DatabaseCallback creationCallback]) native "DOMWindow_openDatabase_Callback";
+ SqlDatabase openDatabase(String name, String version, String displayName, int estimatedSize, [DatabaseCallback creationCallback]) native "DOMWindow_openDatabase_Callback";
@DomName('DOMWindow.postMessage')
@DocsEditable
@@ -29000,14 +28740,14 @@
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.SAFARI)
@Experimental
- Database openDatabase(String name, String version, String displayName, int estimatedSize, [DatabaseCallback creationCallback]) native "WorkerContext_openDatabase_Callback";
+ SqlDatabase openDatabase(String name, String version, String displayName, int estimatedSize, [DatabaseCallback creationCallback]) native "WorkerContext_openDatabase_Callback";
@DomName('WorkerContext.openDatabaseSync')
@DocsEditable
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.SAFARI)
@Experimental
- DatabaseSync openDatabaseSync(String name, String version, String displayName, int estimatedSize, [DatabaseCallback creationCallback]) native "WorkerContext_openDatabaseSync_Callback";
+ SqlDatabaseSync openDatabaseSync(String name, String version, String displayName, int estimatedSize, [DatabaseCallback creationCallback]) native "WorkerContext_openDatabaseSync_Callback";
@DomName('WorkerContext.removeEventListener')
@DocsEditable
@@ -30836,207 +30576,6 @@
@DocsEditable
-@DomName('MediaStreamList')
-class _MediaStreamList extends NativeFieldWrapperClass1 implements List<MediaStream> {
- _MediaStreamList.internal();
-
- @DomName('MediaStreamList.length')
- @DocsEditable
- int get length native "MediaStreamList_length_Getter";
-
- MediaStream operator[](int index) native "MediaStreamList_item_Callback";
-
- void operator[]=(int index, MediaStream value) {
- throw new UnsupportedError("Cannot assign element of immutable List.");
- }
- // -- start List<MediaStream> mixins.
- // MediaStream is the element type.
-
- // From Iterable<MediaStream>:
-
- Iterator<MediaStream> get iterator {
- // Note: NodeLists are not fixed size. And most probably length shouldn't
- // be cached in both iterator _and_ forEach method. For now caching it
- // for consistency.
- return new FixedSizeListIterator<MediaStream>(this);
- }
-
- dynamic reduce(dynamic initialValue, dynamic combine(dynamic, MediaStream)) {
- return IterableMixinWorkaround.reduce(this, initialValue, combine);
- }
-
- bool contains(MediaStream element) => IterableMixinWorkaround.contains(this, element);
-
- void forEach(void f(MediaStream element)) => IterableMixinWorkaround.forEach(this, f);
-
- String join([String separator]) =>
- IterableMixinWorkaround.joinList(this, separator);
-
- Iterable map(f(MediaStream element)) =>
- IterableMixinWorkaround.mapList(this, f);
-
- Iterable<MediaStream> where(bool f(MediaStream element)) =>
- IterableMixinWorkaround.where(this, f);
-
- Iterable expand(Iterable f(MediaStream element)) =>
- IterableMixinWorkaround.expand(this, f);
-
- bool every(bool f(MediaStream element)) => IterableMixinWorkaround.every(this, f);
-
- bool any(bool f(MediaStream element)) => IterableMixinWorkaround.any(this, f);
-
- List<MediaStream> toList() => new List<MediaStream>.from(this);
- Set<MediaStream> toSet() => new Set<MediaStream>.from(this);
-
- bool get isEmpty => this.length == 0;
-
- Iterable<MediaStream> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
- Iterable<MediaStream> takeWhile(bool test(MediaStream value)) {
- return IterableMixinWorkaround.takeWhile(this, test);
- }
-
- Iterable<MediaStream> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
- Iterable<MediaStream> skipWhile(bool test(MediaStream value)) {
- return IterableMixinWorkaround.skipWhile(this, test);
- }
-
- MediaStream firstMatching(bool test(MediaStream value), { MediaStream orElse() }) {
- return IterableMixinWorkaround.firstMatching(this, test, orElse);
- }
-
- MediaStream lastMatching(bool test(MediaStream value), {MediaStream orElse()}) {
- return IterableMixinWorkaround.lastMatchingInList(this, test, orElse);
- }
-
- MediaStream singleMatching(bool test(MediaStream value)) {
- return IterableMixinWorkaround.singleMatching(this, test);
- }
-
- MediaStream elementAt(int index) {
- return this[index];
- }
-
- // From Collection<MediaStream>:
-
- void add(MediaStream value) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void addLast(MediaStream value) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void addAll(Iterable<MediaStream> iterable) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- // From List<MediaStream>:
- void set length(int value) {
- throw new UnsupportedError("Cannot resize immutable List.");
- }
-
- void clear() {
- throw new UnsupportedError("Cannot clear immutable List.");
- }
-
- Iterable<MediaStream> get reversed {
- return IterableMixinWorkaround.reversedList(this);
- }
-
- void sort([int compare(MediaStream a, MediaStream b)]) {
- throw new UnsupportedError("Cannot sort immutable List.");
- }
-
- int indexOf(MediaStream element, [int start = 0]) =>
- Lists.indexOf(this, element, start, this.length);
-
- int lastIndexOf(MediaStream element, [int start]) {
- if (start == null) start = length - 1;
- return Lists.lastIndexOf(this, element, start);
- }
-
- MediaStream get first {
- if (this.length > 0) return this[0];
- throw new StateError("No elements");
- }
-
- MediaStream get last {
- if (this.length > 0) return this[this.length - 1];
- throw new StateError("No elements");
- }
-
- MediaStream get single {
- if (length == 1) return this[0];
- if (length == 0) throw new StateError("No elements");
- throw new StateError("More than one element");
- }
-
- MediaStream min([int compare(MediaStream a, MediaStream b)]) =>
- IterableMixinWorkaround.min(this, compare);
-
- MediaStream max([int compare(MediaStream a, MediaStream b)]) =>
- IterableMixinWorkaround.max(this, compare);
-
- MediaStream removeAt(int pos) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- MediaStream removeLast() {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void remove(Object object) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void removeAll(Iterable elements) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void retainAll(Iterable elements) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void removeMatching(bool test(MediaStream element)) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void retainMatching(bool test(MediaStream element)) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void setRange(int start, int rangeLength, List<MediaStream> from, [int startFrom]) {
- throw new UnsupportedError("Cannot setRange on immutable List.");
- }
-
- void removeRange(int start, int rangeLength) {
- throw new UnsupportedError("Cannot removeRange on immutable List.");
- }
-
- void insertRange(int start, int rangeLength, [MediaStream initialValue]) {
- throw new UnsupportedError("Cannot insertRange on immutable List.");
- }
-
- List<MediaStream> getRange(int start, int rangeLength) =>
- Lists.getRange(this, start, rangeLength, <MediaStream>[]);
-
- // -- end List<MediaStream> mixins.
-
- @DomName('MediaStreamList.item')
- @DocsEditable
- MediaStream item(int index) native "MediaStreamList_item_Callback";
-
-}
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-@DocsEditable
@DomName('NamedNodeMap')
class _NamedNodeMap extends NativeFieldWrapperClass1 implements List<Node> {
_NamedNodeMap.internal();
diff --git a/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart b/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart
index 76547dd..9180c8a 100644
--- a/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart
+++ b/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart
@@ -9,7 +9,8 @@
// 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.
-// DO NOT EDIT
+// DO NOT EDIT - unless you are editing documentation as per:
+// https://code.google.com/p/dart/wiki/ContributingHTMLDocumentation
// Auto-generated dart:svg library.
diff --git a/sdk/lib/io/chunked_stream.dart b/sdk/lib/io/chunked_stream.dart
deleted file mode 100644
index 07fd9b4..0000000
--- a/sdk/lib/io/chunked_stream.dart
+++ /dev/null
@@ -1,140 +0,0 @@
-// 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.
-
-part of dart.io;
-
-class _ChunkedInputStream implements ChunkedInputStream {
- _ChunkedInputStream(InputStream this._input, int this._chunkSize)
- : _bufferList = new _BufferList() {
- _input.onClosed = _onClosed;
- }
-
- List<int> read() {
- if (_closed) return null;
- var result = _bufferList.readBytes(_chunkSize);
- if (result == null) {
- _readData();
- result = _bufferList.readBytes(_chunkSize);
- }
- if (result == null && _inputClosed) {
- if (_bufferList.length == 0) {
- result = null;
- } else {
- result = _bufferList.readBytes(_bufferList.length);
- }
- }
- _checkInstallDataHandler();
- return result;
- }
-
- int get chunkSize => _chunkSize;
-
- void set chunkSize(int chunkSize) {
- _chunkSize = chunkSize;
- _checkInstallDataHandler();
- _checkScheduleCallback();
- }
-
- bool get closed => _closed;
-
- void set onData(void callback()) {
- _clientDataHandler = callback;
- _checkInstallDataHandler();
- }
-
- void set onClosed(void callback()) {
- _clientCloseHandler = callback;
- }
-
- void set onError(void callback(e)) {
- _input.onError = callback;
- }
-
- void _onData() {
- _readData();
- if (_bufferList.length >= _chunkSize && _clientDataHandler != null) {
- _clientDataHandler();
- }
- _checkScheduleCallback();
- _checkInstallDataHandler();
- }
-
- void _readData() {
- List<int> data = _input.read();
- if (data != null) {
- _bufferList.add(data);
- }
- }
-
- void _onClosed() {
- _inputClosed = true;
- if (_bufferList.length == 0 && _clientCloseHandler != null) {
- _clientCloseHandler();
- _closed = true;
- } else {
- _checkScheduleCallback();
- }
- }
-
- void _checkInstallDataHandler() {
- if (_clientDataHandler == null) {
- _input.onData = null;
- } else {
- if (_bufferList.length < _chunkSize && !_inputClosed) {
- _input.onData = _onData;
- } else {
- _input.onData = null;
- }
- }
- }
-
- void _checkScheduleCallback() {
- // TODO(sgjesse): Find a better way of scheduling callbacks from
- // the event loop.
- void issueDataCallback() {
- _scheduledDataCallback = null;
- if (_clientDataHandler != null) {
- _clientDataHandler();
- _checkScheduleCallback();
- }
- }
-
- void issueCloseCallback() {
- _scheduledCloseCallback = null;
- if (!_closed) {
- if (_clientCloseHandler != null) _clientCloseHandler();
- _closed = true;
- }
- }
-
- // Schedule data callback if enough data in buffer.
- if ((_bufferList.length >= _chunkSize ||
- (_bufferList.length > 0 && _inputClosed)) &&
- _clientDataHandler != null &&
- _scheduledDataCallback == null) {
- _scheduledDataCallback = Timer.run(issueDataCallback);
- }
-
- // Schedule close callback if no more data and input is closed.
- if (_bufferList.length == 0 &&
- _inputClosed &&
- !_closed &&
- _scheduledCloseCallback == null) {
- if (_scheduledDataCallback != null) {
- _scheduledDataCallback.cancel();
- }
- _scheduledCloseCallback = Timer.run(issueCloseCallback);
- }
- }
-
- InputStream _input;
- _BufferList _bufferList;
- int _chunkSize;
- bool _inputClosed = false; // Is the underlying input stream closed?
- bool _closed = false; // Has the close handler been called?.
- Timer _scheduledDataCallback;
- Timer _scheduledCloseCallback;
- Function _clientDataHandler;
- Function _clientCloseHandler;
-}
diff --git a/sdk/lib/io/directory.dart b/sdk/lib/io/directory.dart
index 60717b8..fd70674 100644
--- a/sdk/lib/io/directory.dart
+++ b/sdk/lib/io/directory.dart
@@ -7,7 +7,7 @@
/**
* [Directory] objects are used for working with directories.
*/
-abstract class Directory {
+abstract class Directory extends FileSystemEntity {
/**
* Creates a directory object. The path is either an absolute path,
* or it is a relative path which is interpreted relative to the directory
@@ -65,8 +65,8 @@
/**
* Creates a temporary directory with a name based on the current
- * path. This name and path is used as a template, and additional
- * characters are appended to it by the call to make a unique
+ * path. The path is used as a template, and additional
+ * characters are appended to it to make a unique temporary
* directory name. If the path is the empty string, a default
* system temp directory and name are used for the template.
*
@@ -77,15 +77,15 @@
/**
* Synchronously creates a temporary directory with a name based on the
- * current path. This name and path is used as a template, and additional
- * characters are appended to it by the call to make a unique directory name.
+ * current path. The path is used as a template, and additional
+ * characters are appended to it to make a unique temporary directory name.
* If the path is the empty string, a default system temp directory and name
* are used for the template. Returns the newly created temporary directory.
*/
Directory createTempSync();
/**
- * Deletes the directory with this name.
+ * Deletes this directory.
*
* If [recursive] is false, the directory must be empty.
*
@@ -99,7 +99,7 @@
Future<Directory> delete({recursive: false});
/**
- * Synchronously deletes the directory with this name.
+ * Synchronously deletes this directory.
*
* If [recursive] is false, the directory must be empty.
*
@@ -111,17 +111,17 @@
void deleteSync({recursive: false});
/**
- * Rename this directory. Returns a [:Future<Directory>:] that completes
+ * Renames this directory. Returns a [:Future<Directory>:] that completes
* with a [Directory] instance for the renamed directory.
*
* If newPath identifies an existing directory, that directory is
- * replaced. If newPath identifies an existing file the operation
+ * replaced. If newPath identifies an existing file, the operation
* fails and the future completes with an exception.
*/
Future<Directory> rename(String newPath);
/**
- * Synchronously rename this directory. Returns a [Directory]
+ * Synchronously renames this directory. Returns a [Directory]
* instance for the renamed directory.
*
* If newPath identifies an existing directory, that directory is
@@ -131,20 +131,22 @@
Directory renameSync(String newPath);
/**
- * List the sub-directories and files of this
- * [Directory]. Optionally recurse into sub-directories. Returns a
- * [DirectoryLister] object representing the active listing
- * operation. Handlers for files and directories should be
- * registered on this DirectoryLister object.
+ * Lists the sub-directories and files of this [Directory].
+ * Optionally recurses into sub-directories.
+ *
+ * The result is a stream of [FileSystemEntity] objects
+ * for the directories and files.
*/
- DirectoryLister list({bool recursive: false});
+ Stream<FileSystemEntity> list({bool recursive: false});
/**
- * List the sub-directories and files of this
- * [Directory]. Optionally recurse into sub-directories. Returns a
- * List containing Directory and File objects.
+ * Lists the sub-directories and files of this [Directory].
+ * Optionally recurses into sub-directories.
+ *
+ * Returns a [List] containing [FileSystemEntity] objects for the
+ * directories and files.
*/
- List listSync({bool recursive: false});
+ List<FileSystemEntity> listSync({bool recursive: false});
/**
* Returns a human readable string for this Directory instance.
@@ -158,49 +160,6 @@
}
-/**
- * A [DirectoryLister] represents an actively running listing operation.
- *
- * A [DirectoryLister] is obtained from a [Directory] object by calling
- * the [:Directory.list:] method.
- *
- * Directory dir = new Directory('path/to/my/dir');
- * DirectoryLister lister = dir.list();
- *
- * For each file and directory, the file or directory handler is
- * called. When all directories have been listed the done handler is
- * called. If the listing operation is recursive, the error handler is
- * called if a subdirectory cannot be opened for listing.
- */
-abstract class DirectoryLister {
- /**
- * Sets the directory handler that is called for all directories
- * during listing. The directory handler is called with the full
- * path of the directory.
- */
- void set onDir(void onDir(String dir));
-
- /**
- * Sets the handler that is called for all files during listing. The
- * file handler is called with the full path of the file.
- */
- void set onFile(void onFile(String file));
-
- /**
- * Set the handler that is called when a listing is done. The
- * handler is called with an indication of whether or not the
- * listing operation completed.
- */
- void set onDone(void onDone(bool completed));
-
- /**
- * Sets the handler that is called if there is an error while
- * listing directories.
- */
- void set onError(void onError(e));
-}
-
-
class DirectoryIOException implements Exception {
const DirectoryIOException([String this.message = "",
String this.path = "",
diff --git a/sdk/lib/io/directory_impl.dart b/sdk/lib/io/directory_impl.dart
index 59911ed..5963d83 100644
--- a/sdk/lib/io/directory_impl.dart
+++ b/sdk/lib/io/directory_impl.dart
@@ -226,8 +226,65 @@
return new Directory(newPath);
}
- DirectoryLister list({bool recursive: false}) {
- return new _DirectoryLister(_path, recursive);
+ Stream<FileSystemEntity> list({bool recursive: false}) {
+ const int LIST_FILE = 0;
+ const int LIST_DIRECTORY = 1;
+ const int LIST_ERROR = 2;
+ const int LIST_DONE = 3;
+
+ const int RESPONSE_TYPE = 0;
+ const int RESPONSE_PATH = 1;
+ const int RESPONSE_COMPLETE = 1;
+ const int RESPONSE_ERROR = 2;
+
+ var controller = new StreamController<FileSystemEntity>();
+
+ List request = [ _Directory.LIST_REQUEST, path, recursive ];
+ ReceivePort responsePort = new ReceivePort();
+ // Use a separate directory service port for each listing as
+ // listing operations on the same directory can run in parallel.
+ _Directory._newServicePort().send(request, responsePort.toSendPort());
+ responsePort.receive((message, replyTo) {
+ if (message is !List || message[RESPONSE_TYPE] is !int) {
+ responsePort.close();
+ controller.signalError(new DirectoryIOException("Internal error"));
+ return;
+ }
+ switch (message[RESPONSE_TYPE]) {
+ case LIST_FILE:
+ controller.add(new File(message[RESPONSE_PATH]));
+ break;
+ case LIST_DIRECTORY:
+ controller.add(new Directory(message[RESPONSE_PATH]));
+ break;
+ case LIST_ERROR:
+ var errorType =
+ message[RESPONSE_ERROR][_ERROR_RESPONSE_ERROR_TYPE];
+ if (errorType == _ILLEGAL_ARGUMENT_RESPONSE) {
+ controller.signalError(new ArgumentError());
+ } else if (errorType == _OSERROR_RESPONSE) {
+ var responseError = message[RESPONSE_ERROR];
+ var err = new OSError(
+ responseError[_OSERROR_RESPONSE_MESSAGE],
+ responseError[_OSERROR_RESPONSE_ERROR_CODE]);
+ var errorPath = message[RESPONSE_PATH];
+ if (errorPath == null) errorPath = path;
+ controller.signalError(
+ new DirectoryIOException("Directory listing failed",
+ errorPath,
+ err));
+ } else {
+ controller.signalError(new DirectoryIOException("Internal error"));
+ }
+ break;
+ case LIST_DONE:
+ responsePort.close();
+ controller.close();
+ break;
+ }
+ });
+
+ return controller.stream;
}
List listSync({bool recursive: false}) {
@@ -268,93 +325,3 @@
final String _path;
SendPort _directoryService;
}
-
-class _DirectoryLister implements DirectoryLister {
- _DirectoryLister(String path, bool recursive) {
- const int LIST_DIRECTORY = 0;
- const int LIST_FILE = 1;
- const int LIST_ERROR = 2;
- const int LIST_DONE = 3;
-
- final int RESPONSE_TYPE = 0;
- final int RESPONSE_PATH = 1;
- final int RESPONSE_COMPLETE = 1;
- final int RESPONSE_ERROR = 2;
-
- List request = new List.fixedLength(3);
- request[0] = _Directory.LIST_REQUEST;
- request[1] = path;
- request[2] = recursive;
- ReceivePort responsePort = new ReceivePort();
- // Use a separate directory service port for each listing as
- // listing operations on the same directory can run in parallel.
- _Directory._newServicePort().send(request, responsePort.toSendPort());
- responsePort.receive((message, replyTo) {
- if (message is !List || message[RESPONSE_TYPE] is !int) {
- responsePort.close();
- _reportError(new DirectoryIOException("Internal error"));
- return;
- }
- switch (message[RESPONSE_TYPE]) {
- case LIST_DIRECTORY:
- if (_onDir != null) _onDir(message[RESPONSE_PATH]);
- break;
- case LIST_FILE:
- if (_onFile != null) _onFile(message[RESPONSE_PATH]);
- break;
- case LIST_ERROR:
- var errorType =
- message[RESPONSE_ERROR][_ERROR_RESPONSE_ERROR_TYPE];
- if (errorType == _ILLEGAL_ARGUMENT_RESPONSE) {
- _reportError(new ArgumentError());
- } else if (errorType == _OSERROR_RESPONSE) {
- var responseError = message[RESPONSE_ERROR];
- var err = new OSError(
- responseError[_OSERROR_RESPONSE_MESSAGE],
- responseError[_OSERROR_RESPONSE_ERROR_CODE]);
- var errorPath = message[RESPONSE_PATH];
- if (errorPath == null) errorPath = path;
- _reportError(new DirectoryIOException("Directory listing failed",
- errorPath,
- err));
- } else {
- _reportError(new DirectoryIOException("Internal error"));
- }
- break;
- case LIST_DONE:
- responsePort.close();
- if (_onDone != null) _onDone(message[RESPONSE_COMPLETE]);
- break;
- }
- });
- }
-
- void set onDir(void onDir(String dir)) {
- _onDir = onDir;
- }
-
- void set onFile(void onFile(String file)) {
- _onFile = onFile;
- }
-
- void set onDone(void onDone(bool completed)) {
- _onDone = onDone;
- }
-
- void set onError(void onError(e)) {
- _onError = onError;
- }
-
- void _reportError(e) {
- if (_onError != null) {
- _onError(e);
- } else {
- throw e;
- }
- }
-
- Function _onDir;
- Function _onFile;
- Function _onDone;
- Function _onError;
-}
diff --git a/sdk/lib/io/file.dart b/sdk/lib/io/file.dart
index 53a348c..ff956c8 100644
--- a/sdk/lib/io/file.dart
+++ b/sdk/lib/io/file.dart
@@ -19,11 +19,14 @@
/**
* [File] objects are references to files.
*
- * To operate on the underlying file data you need to either get
- * streams using [openInputStream] and [openOutputStream] or open the
- * file for random access operations using [open].
+ * To operate on the underlying file data there are two options:
+ *
+ * * Use streaming: read the contents of the file from the [Stream]
+ * this.[openRead]() and write to the file by writing to the [IOSink]
+ * this.[openWrite]().
+ * * Open the file for random access operations using [open].
*/
-abstract class File {
+abstract class File extends FileSystemEntity {
/**
* Create a File object.
*/
@@ -35,8 +38,8 @@
factory File.fromPath(Path path) => new _File.fromPath(path);
/**
- * Check if the file exists. Does not block and returns a
- * [:Future<bool>:].
+ * Check if the file exists. Returns a
+ * [:Future<bool>:] that completes when the answer is known.
*/
Future<bool> exists();
@@ -49,7 +52,7 @@
* Create the file. Returns a [:Future<File>:] that completes with
* the file when it has been created.
*
- * Existing files are left untouched by create. Calling create on an
+ * Existing files are left untouched by [create]. Calling [create] on an
* existing file might fail if there are restrictive permissions on
* the file.
*/
@@ -57,8 +60,8 @@
/**
* Synchronously create the file. Existing files are left untouched
- * by create. Calling create on an existing file might fail if there
- * are restrictive permissions on the file.
+ * by [createSync]. Calling [createSync] on an existing file might fail
+ * if there are restrictive permissions on the file.
*/
void createSync();
@@ -74,14 +77,14 @@
void deleteSync();
/**
- * Get a Directory object for the directory containing this
+ * Get a [Directory] object for the directory containing this
* file. Returns a [:Future<Directory>:] that completes with the
* directory.
*/
Future<Directory> directory();
/**
- * Synchronously get a Directory object for the directory containing
+ * Synchronously get a [Directory] object for the directory containing
* this file.
*/
Directory directorySync();
@@ -113,27 +116,27 @@
/**
* Open the file for random access operations. Returns a
* [:Future<RandomAccessFile>:] that completes with the opened
- * random access file. RandomAccessFiles must be closed using the
- * [close] method.
+ * random access file. [RandomAccessFile]s must be closed using the
+ * [RandomAccessFile.close] method.
*
* Files can be opened in three modes:
*
- * FileMode.READ: open the file for reading.
+ * [FileMode.READ]: open the file for reading.
*
- * FileMode.WRITE: open the file for both reading and writing and
+ * [FileMode.WRITE]: open the file for both reading and writing and
* truncate the file to length zero. If the file does not exist the
* file is created.
*
- * FileMode.APPEND: same as FileMode.WRITE except that the file is
+ * [FileMode.APPEND]: same as [FileMode.WRITE] except that the file is
* not truncated.
*/
Future<RandomAccessFile> open([FileMode mode = FileMode.READ]);
/**
* Synchronously open the file for random access operations. The
- * result is a RandomAccessFile on which random access operations
- * can be performed. Opened RandomAccessFiles must be closed using
- * the [close] method.
+ * result is a [RandomAccessFile] on which random access operations
+ * can be performed. Opened [RandomAccessFile]s must be closed using
+ * the [RandomAccessFile.close] method.
*
* See [open] for information on the [mode] argument.
*/
@@ -151,26 +154,28 @@
String fullPathSync();
/**
- * Create a new independent input stream for the file. The file
- * input stream must be closed when no longer used to free up system
- * resources.
+ * Create a new independent [Stream](../dart_async/Stream.html) for the
+ * contents of this file.
+ *
+ * In order to make sure that system resources are freed, the stream
+ * must be read to completion or the subscription on the stream must
+ * be cancelled.
*/
- InputStream openInputStream();
+ Stream<List<int>> openRead();
+
/**
- * Creates a new independent output stream for the file. The file
- * output stream must be closed when no longer used to free up
+ * Creates a new independent [IOSink] for the file. The
+ * [IOSink] must be closed when no longer used, to free
* system resources.
*
- * An output stream can be opened in two modes:
+ * An [IOSink] for a file can be opened in two modes:
*
- * FileMode.WRITE: create the stream and truncate the underlying
- * file to length zero.
- *
- * FileMode.APPEND: create the stream and set the position to the end of
- * the underlying file.
+ * * [FileMode.WRITE]: truncates the file to length zero.
+ * * [FileMode.APPEND]: sets the initial write position to the end
+ * of the file.
*/
- OutputStream openOutputStream([FileMode mode = FileMode.WRITE]);
+ IOSink<File> openWrite([FileMode mode = FileMode.WRITE]);
/**
* Read the entire file contents as a list of bytes. Returns a
@@ -186,7 +191,7 @@
/**
* Read the entire file contents as a string using the given
- * [encoding].
+ * [Encoding].
*
* Returns a [:Future<String>:] that completes with the string once
* the file contents has been read.
@@ -195,13 +200,13 @@
/**
* Synchronously read the entire file contents as a string using the
- * given [encoding].
+ * given [Encoding].
*/
String readAsStringSync([Encoding encoding = Encoding.UTF_8]);
/**
- * Read the entire file contents as lines of text using the give
- * [encoding].
+ * Read the entire file contents as lines of text using the given
+ * [Encoding].
*
* Returns a [:Future<List<String>>:] that completes with the lines
* once the file contents has been read.
@@ -210,7 +215,7 @@
/**
* Synchronously read the entire file contents as lines of text
- * using the given [encoding].
+ * using the given [Encoding].
*/
List<String> readAsLinesSync([Encoding encoding = Encoding.UTF_8]);
@@ -223,18 +228,18 @@
*
* By default [writeAsBytes] creates the file for writing and truncates the
* file if it already exists. In order to append the bytes to an existing
- * file pass [:FileMode.APPEND:] as the optional mode parameter.
+ * file, pass [FileMode.APPEND] as the optional mode parameter.
*/
Future<File> writeAsBytes(List<int> bytes, [FileMode mode = FileMode.WRITE]);
/**
* Synchronously write a list of bytes to a file.
*
- * Opens the file, writes the list of bytes to it and closses the file.
+ * Opens the file, writes the list of bytes to it and closes the file.
*
* By default [writeAsBytesSync] creates the file for writing and truncates
* the file if it already exists. In order to append the bytes to an existing
- * file pass [:FileMode.APPEND:] as the optional mode parameter.
+ * file, pass [FileMode.APPEND] as the optional mode parameter.
*/
void writeAsBytesSync(List<int> bytes, [FileMode mode = FileMode.WRITE]);
@@ -247,7 +252,7 @@
*
* By default [writeAsString] creates the file for writing and truncates the
* file if it already exists. In order to append the bytes to an existing
- * file pass [:FileMode.APPEND:] as the optional mode parameter.
+ * file, pass [FileMode.APPEND] as the optional mode parameter.
*/
Future<File> writeAsString(String contents,
{FileMode mode: FileMode.WRITE,
@@ -261,7 +266,7 @@
*
* By default [writeAsStringSync] creates the file for writing and
* truncates the file if it already exists. In order to append the bytes
- * to an existing file pass [:FileMode.APPEND:] as the optional mode
+ * to an existing file, pass [FileMode.APPEND] as the optional mode
* parameter.
*/
void writeAsStringSync(String contents,
@@ -282,79 +287,85 @@
*/
abstract class RandomAccessFile {
/**
- * Close the file. Returns a [:Future<RandomAccessFile>:] that
+ * Closes the file. Returns a [:Future<RandomAccessFile>:] that
* completes with this RandomAccessFile when it has been closed.
*/
Future<RandomAccessFile> close();
/**
- * Synchronously close the file.
+ * Synchronously closes the file.
*/
void closeSync();
/**
- * Read a byte from the file. Returns a [:Future<int>:] that
- * completes with the byte or -1 if end of file has been reached.
+ * Reads a byte from the file. Returns a [:Future<int>:] that
+ * completes with the byte, or with -1 if end-of-file has been reached.
*/
Future<int> readByte();
/**
- * Synchronously read a single byte from the file. If end of file
+ * Synchronously reads a single byte from the file. If end-of-file
* has been reached -1 is returned.
*/
int readByteSync();
/**
- * Reads from a file and returns the result as a list of bytes.
+ * Reads [bytes] bytes from a file and returns the result as a list of bytes.
*/
Future<List<int>> read(int bytes);
/**
- * Synchronously reads from a file and returns the result in a
+ * Synchronously reads a maximum of [bytes] bytes from a file
+ * and returns the result in a
* list of bytes.
*/
List<int> readSync(int bytes);
/**
- * Read a List<int> from the file. Returns a [:Future<int>:] that
- * completes with an indication of how much was read.
+ * Reads into an existing List<int> from the file. A maximum of [bytes] bytes
+ * is read into [buffer], starting at position [offset] in the buffer.
+ * Returns a [:Future<int>:] that completes with the number of bytes read.
*/
Future<int> readList(List<int> buffer, int offset, int bytes);
/**
- * Synchronously read a List<int> from the file. Returns the number
- * of bytes read.
+ * Synchronously reads from a file into [buffer]. A maximum of [bytes] bytes
+ * is read into [buffer], starting at position [offset] in the buffer.
+ * Returns the number of bytes read.
*/
int readListSync(List<int> buffer, int offset, int bytes);
/**
- * Write a single byte to the file. Returns a
+ * Writes a single byte to the file. Returns a
* [:Future<RandomAccessFile>:] that completes with this
* RandomAccessFile when the write completes.
*/
Future<RandomAccessFile> writeByte(int value);
/**
- * Synchronously write a single byte to the file. Returns the
+ * Synchronously writes a single byte to the file. Returns the
* number of bytes successfully written.
*/
int writeByteSync(int value);
/**
- * Write a List<int> to the file. Returns a
+ * Writes from a List<int> to the file. [bytes] bytes are written from
+ * [buffer], starting at position [offset] in the buffer. Returns a
* [:Future<RandomAccessFile>:] that completes with this
* RandomAccessFile when the write completes.
*/
Future<RandomAccessFile> writeList(List<int> buffer, int offset, int bytes);
/**
- * Synchronously write a List<int> to the file. Returns the number
+ * Synchronously writes a List<int> to the file.
+ * [bytes] bytes are written from
+ * [buffer], starting at position [offset] in the buffer. Returns the number
* of bytes successfully written.
*/
int writeListSync(List<int> buffer, int offset, int bytes);
/**
- * Write a string to the file using the given [encoding]. Returns a
+ * Writes a string to the file using the given [Encoding]. Returns a
* [:Future<RandomAccessFile>:] that completes with this
* RandomAccessFile when the write completes.
*/
@@ -362,78 +373,78 @@
[Encoding encoding = Encoding.UTF_8]);
/**
- * Synchronously write a single string to the file using the given
- * [encoding]. Returns the number of characters successfully
+ * Synchronously writes a single string to the file using the given
+ * [Encoding]. Returns the number of characters successfully
* written.
*/
int writeStringSync(String string,
[Encoding encoding = Encoding.UTF_8]);
/**
- * Get the current byte position in the file. Returns a
+ * Gets the current byte position in the file. Returns a
* [:Future<int>:] that completes with the position.
*/
Future<int> position();
/**
- * Synchronously get the current byte position in the file.
+ * Synchronously gets the current byte position in the file.
*/
int positionSync();
/**
- * Set the byte position in the file. Returns a
+ * Sets the byte position in the file. Returns a
* [:Future<RandomAccessFile>:] that completes with this
* RandomAccessFile when the position has been set.
*/
Future<RandomAccessFile> setPosition(int position);
/**
- * Synchronously set the byte position in the file.
+ * Synchronously sets the byte position in the file.
*/
void setPositionSync(int position);
/**
- * Truncate (or extend) the file to [length] bytes. Returns a
+ * Truncates (or extends) the file to [length] bytes. Returns a
* [:Future<RandomAccessFile>:] that completes with this
* RandomAccessFile when the truncation has been performed.
*/
Future<RandomAccessFile> truncate(int length);
/**
- * Synchronously truncate (or extend) the file to [length] bytes.
+ * Synchronously truncates (or extends) the file to [length] bytes.
*/
void truncateSync(int length);
/**
- * Get the length of the file. Returns a [:Future<int>:] that
+ * Gets the length of the file. Returns a [:Future<int>:] that
* completes with the length in bytes.
*/
Future<int> length();
/**
- * Synchronously get the length of the file.
+ * Synchronously gets the length of the file.
*/
int lengthSync();
/**
- * Flush the contents of the file to disk. Returns a
+ * Flushes the contents of the file to disk. Returns a
* [:Future<RandomAccessFile>:] that completes with this
* RandomAccessFile when the flush operation completes.
*/
Future<RandomAccessFile> flush();
/**
- * Synchronously flush the contents of the file to disk.
+ * Synchronously flushes the contents of the file to disk.
*/
void flushSync();
/**
- * Returns a human readable string for this File instance.
+ * Returns a human-readable string for this RandomAccessFile instance.
*/
String toString();
/**
- * Get the name of the file.
+ * Gets the name of the file underlying this RandomAccessFile.
*/
String get name;
}
diff --git a/sdk/lib/io/file_impl.dart b/sdk/lib/io/file_impl.dart
index f6cade2..ce60209 100644
--- a/sdk/lib/io/file_impl.dart
+++ b/sdk/lib/io/file_impl.dart
@@ -4,293 +4,202 @@
part of dart.io;
-class _FileInputStream extends _BaseDataInputStream implements InputStream {
- _FileInputStream(String name)
- : _data = const [],
- _position = 0,
- _filePosition = 0 {
- var file = new File(name);
- var future = file.open(FileMode.READ);
- future.then(_setupOpenedFile)
- .catchError((e) {
- _reportError(e.error);
- });
+
+class _FileStream extends Stream<List<int>> {
+ // Stream controller.
+ StreamController<List<int>> _controller;
+
+ // Read the file in blocks of size 64k.
+ final int _blockSize = 64 * 1024;
+
+ // Information about the underlying file.
+ String _name;
+ RandomAccessFile _openedFile;
+ int _position;
+
+ // Has the stream been paused or unsubscribed?
+ bool _paused = false;
+ bool _unsubscribed = false;
+
+ // Is there a read currently in progress?
+ bool _readInProgress = false;
+
+ // Block read but not yet send because stream is paused.
+ List<int> _currentBlock;
+
+ _FileStream(String this._name) : _position = 0 {
+ _setupController();
}
- _FileInputStream.fromStdio(int fd)
- : _data = const [],
- _position = 0,
- _filePosition = 0 {
- assert(fd == 0);
- _setupOpenedFile(_File._openStdioSync(fd));
+ _FileStream.forStdin() : _position = 0 {
+ _setupController();
}
- void _setupOpenedFile(RandomAccessFile openedFile) {
- _openedFile = openedFile;
- if (_streamMarkedClosed) {
- // This input stream has already been closed.
- _fileLength = 0;
- _closeFile();
- return;
+ StreamSubscription<List<int>> listen(void onData(List<int> event),
+ {void onError(AsyncError error),
+ void onDone(),
+ bool unsubscribeOnError}) {
+ return _controller.stream.listen(onData,
+ onError: onError,
+ onDone: onDone,
+ unsubscribeOnError: unsubscribeOnError);
+ }
+
+ void _setupController() {
+ _controller = new StreamController<List<int>>(
+ onSubscriptionStateChange: _onSubscriptionStateChange,
+ onPauseStateChange: _onPauseStateChange);
+ }
+
+ Future _closeFile() {
+ Future closeFuture;
+ if (_openedFile != null) {
+ Future closeFuture = _openedFile.close();
+ _openedFile = null;
+ return closeFuture;
+ } else {
+ return new Future.immediate(null);
}
- var futureOpen = _openedFile.length();
- futureOpen
- .then((len) {
- _fileLength = len;
- _fillBuffer();
+ }
+
+ void _readBlock() {
+ // Don't start a new read if one is already in progress.
+ if (_readInProgress) return;
+ _readInProgress = true;
+ _openedFile.length()
+ .then((length) {
+ if (_position >= length) {
+ _readInProgress = false;
+ if (!_unsubscribed) {
+ _closeFile().then((_) { _controller.close(); });
+ _unsubscribed = true;
+ }
+ return null;
+ } else {
+ return _openedFile.read(_blockSize);
+ }
+ })
+ .then((block) {
+ _readInProgress = false;
+ if (block == null || _unsubscribed) {
+ return;
+ }
+ _position += block.length;
+ if (_paused) {
+ _currentBlock = block;
+ } else {
+ _controller.add(block);
+ _readBlock();
+ }
})
.catchError((e) {
- _reportError(e.error);
+ if (!_unsubscribed) {
+ _controller.signalError(e);
+ _closeFile().then((_) { _controller.close(); });
+ _unsubscribed = true;
+ }
});
}
- void _closeFile() {
- if (_openedFile == null) {
- _streamMarkedClosed = true;
- return;
+ void _start() {
+ Future<RandomAccessFile> openFuture;
+ if (_name != null) {
+ openFuture = new File(_name).open(FileMode.READ);
+ } else {
+ openFuture = new Future.immediate(_File._openStdioSync(0));
}
- if (available() == 0) _cancelScheduledDataCallback();
- if (!_openedFile.closed) {
- _openedFile.close().then((ignore) {
- _streamMarkedClosed = true;
- _checkScheduleCallbacks();
+ openFuture
+ .then((RandomAccessFile opened) {
+ _openedFile = opened;
+ _readBlock();
+ })
+ .catchError((e) {
+ _controller.signalError(e);
+ _controller.close();
});
- }
}
- void _fillBuffer() {
- Expect.equals(_position, _data.length);
- if (_openedFile == null) return; // Called before the file is opened.
- int size = min(_bufferLength, _fileLength - _filePosition);
- if (size == 0) {
+ void _resume() {
+ _paused = false;
+ if (_currentBlock != null) {
+ _controller.add(_currentBlock);
+ _currentBlock = null;
+ }
+ // Resume reading unless we are already done.
+ if (_openedFile != null) _readBlock();
+ }
+
+ void _onSubscriptionStateChange() {
+ if (_controller.hasSubscribers) {
+ _start();
+ } else {
+ _unsubscribed = true;
_closeFile();
- return;
}
- // If there is currently a _fillBuffer call waiting on read,
- // let it fill the buffer instead of us.
- if (_activeFillBufferCall) return;
- _activeFillBufferCall = true;
- var future = _openedFile.read(size);
- future.then((data) {
- _data = data;
- _position = 0;
- _filePosition += _data.length;
- _activeFillBufferCall = false;
-
- if (_fileLength == _filePosition) {
- _closeFile();
- }
- _checkScheduleCallbacks();
- }).catchError((e) {
- _activeFillBufferCall = false;
- _reportError(e.error);
- });
}
- int available() {
- return closed ? 0 : _data.length - _position;
- }
-
- void pipe(OutputStream output, {bool close: true}) {
- _pipe(this, output, close: close);
- }
-
- void _finishRead() {
- if (_position == _data.length && !_streamMarkedClosed) {
- _fillBuffer();
+ void _onPauseStateChange() {
+ if (_controller.isPaused) {
+ _paused = true;
} else {
- _checkScheduleCallbacks();
+ _resume();
}
}
-
- List<int> _read(int bytesToRead) {
- List<int> result;
- if (_position == 0 && bytesToRead == _data.length) {
- result = _data;
- _data = const [];
- } else {
- result = new Uint8List(bytesToRead);
- result.setRange(0, bytesToRead, _data, _position);
- _position += bytesToRead;
- }
- _finishRead();
- return result;
- }
-
- int _readInto(List<int> buffer, int offset, int len) {
- buffer.setRange(offset, len, _data, _position);
- _position += len;
- _finishRead();
- return len;
- }
-
- void _close() {
- _data = const [];
- _position = 0;
- _filePosition = 0;
- _fileLength = 0;
- _closeFile();
- }
-
- static const int _bufferLength = 64 * 1024;
-
- RandomAccessFile _openedFile;
- List<int> _data;
- int _position;
- int _filePosition;
- int _fileLength;
- bool _activeFillBufferCall = false;
}
+class _FileStreamConsumer extends StreamConsumer<List<int>, File> {
+ File _file;
+ Future<RandomAccessFile> _openFuture;
+ StreamSubscription _subscription;
-class _PendingOperation {
- const _PendingOperation(this._id);
- static const _PendingOperation CLOSE = const _PendingOperation(0);
- static const _PendingOperation FLUSH = const _PendingOperation(1);
- final int _id;
-}
-
-
-class _FileOutputStream extends _BaseOutputStream implements OutputStream {
- _FileOutputStream(String name, FileMode mode) {
- _pendingOperations = new List();
- var f = new File(name);
- var openFuture = f.open(mode);
- openFuture.then((openedFile) {
- _file = openedFile;
- _processPendingOperations();
- }).catchError((e) {
- _reportError(e.error);
- });
+ _FileStreamConsumer(File this._file, FileMode mode) {
+ _openFuture = _file.open(mode);
}
- _FileOutputStream.fromStdio(int fd) {
+ _FileStreamConsumer.fromStdio(int fd) {
assert(1 <= fd && fd <= 2);
- _file = _File._openStdioSync(fd);
+ _openFuture = new Future.immediate(_File._openStdioSync(fd));
}
- bool write(List<int> buffer, [bool copyBuffer = false]) {
- var data = buffer;
- if (copyBuffer) {
- var length = buffer.length;
- data = new Uint8List(length);
- data.setRange(0, length, buffer, 0);
- }
- if (_file == null) {
- _pendingOperations.add(data);
- } else {
- _write(data, 0, data.length);
- }
- return false;
- }
-
- bool writeFrom(List<int> buffer, [int offset = 0, int len]) {
- // A copy is required by the interface.
- var length = buffer.length - offset;
- if (len != null) {
- if (len > length) throw new RangeError.value(len);
- length = len;
- }
- var copy = new Uint8List(length);
- copy.setRange(0, length, buffer, offset);
- return write(copy);
- }
-
-
- void flush() {
- if (_file == null) {
- _pendingOperations.add(_PendingOperation.FLUSH);
- } else {
- _file.flush().then((ignored) => null);
- }
- }
-
-
- void close() {
- _streamMarkedClosed = true;
- if (_file == null) {
- _pendingOperations.add(_PendingOperation.CLOSE);
- } else if (!_closeCallbackScheduled) {
- _file.close().then((ignore) {
- if (_onClosed != null) _onClosed();
+ Future<File> consume(Stream<List<int>> stream) {
+ Completer<File> completer = new Completer<File>();
+ _openFuture
+ .then((openedFile) {
+ _subscription = stream.listen(
+ (d) {
+ _subscription.pause();
+ openedFile.writeList(d, 0, d.length)
+ .then((_) => _subscription.resume())
+ .catchError((e) {
+ openedFile.close();
+ completer.completeError(e);
+ });
+ },
+ onDone: () {
+ // Wait for the file to close (and therefore flush) before
+ // completing the future.
+ openedFile.close()
+ .then((_) {
+ completer.complete(_file);
+ })
+ .catchError((e) {
+ completer.completeError(e);
+ });
+ },
+ onError: (e) {
+ openedFile.close();
+ completer.completeError(e);
+ },
+ unsubscribeOnError: true);
+ })
+ .catchError((e) {
+ completer.completeError(e);
});
- _closeCallbackScheduled = true;
- }
+ return completer.future;
}
-
- void set onNoPendingWrites(void callback()) {
- _onNoPendingWrites = callback;
- if ((_pendingOperations == null || _pendingOperations.length == 0) &&
- outstandingWrites == 0 &&
- !_streamMarkedClosed &&
- _onNoPendingWrites != null) {
- Timer.run(() {
- if (_onNoPendingWrites != null) {
- _onNoPendingWrites();
- }
- });
- }
- }
-
- void set onClosed(void callback()) {
- _onClosed = callback;
- }
-
- void _processPendingOperations() {
- _pendingOperations.forEach((buffer) {
- if (buffer is _PendingOperation) {
- if (identical(buffer, _PendingOperation.CLOSE)) {
- close();
- } else {
- assert(identical(buffer, _PendingOperation.FLUSH));
- flush();
- }
- } else {
- write(buffer);
- }
- });
- _pendingOperations = null;
- }
-
- void _write(List<int> buffer, int offset, int len) {
- outstandingWrites++;
- var writeListFuture = _file.writeList(buffer, offset, len);
- writeListFuture.then((ignore) {
- outstandingWrites--;
- if (outstandingWrites == 0 &&
- !_streamMarkedClosed &&
- _onNoPendingWrites != null) {
- _onNoPendingWrites();
- }
- }).catchError((e) {
- outstandingWrites--;
- _reportError(e.error);
- });
- }
-
- bool get closed => _streamMarkedClosed;
-
- RandomAccessFile _file;
-
- // When this is set to true the stream is marked closed. When a
- // stream is marked closed no more data can be written.
- bool _streamMarkedClosed = false;
-
- // When this is set to true, the close callback has been scheduled and the
- // stream will be fully closed once it's called.
- bool _closeCallbackScheduled = false;
-
- // Number of writes that have not yet completed.
- int outstandingWrites = 0;
-
- // List of pending writes that were issued before the underlying
- // file was successfully opened.
- List _pendingOperations;
-
- Function _onNoPendingWrites;
- Function _onClosed;
}
+
const int _EXISTS_REQUEST = 0;
const int _CREATE_REQUEST = 1;
const int _DELETE_REQUEST = 2;
@@ -555,36 +464,35 @@
return result;
}
- InputStream openInputStream() {
- return new _FileInputStream(_name);
+ Stream<List<int>> openRead() {
+ return new _FileStream(_name);
}
- OutputStream openOutputStream([FileMode mode = FileMode.WRITE]) {
+ IOSink<File> openWrite([FileMode mode = FileMode.WRITE]) {
if (mode != FileMode.WRITE &&
mode != FileMode.APPEND) {
throw new FileIOException(
"Wrong FileMode. Use FileMode.WRITE or FileMode.APPEND");
}
- return new _FileOutputStream(_name, mode);
+ var consumer = new _FileStreamConsumer(this, mode);
+ return new IOSink<File>(consumer);
}
Future<List<int>> readAsBytes() {
_ensureFileService();
Completer<List<int>> completer = new Completer<List<int>>();
var chunks = new _BufferList();
- var stream = openInputStream();
- stream.onClosed = () {
- var result = chunks.readBytes(chunks.length);
- if (result == null) result = <int>[];
- completer.complete(result);
- };
- stream.onData = () {
- var chunk = stream.read();
- chunks.add(chunk);
- };
- stream.onError = (e) {
- completer.completeError(e);
- };
+ openRead().listen(
+ (d) => chunks.add(d),
+ onDone: () {
+ var result = chunks.readBytes(chunks.length);
+ if (result == null) result = <int>[];
+ completer.complete(result);
+ },
+ onError: (e) {
+ completer.completeError(e);
+ },
+ unsubscribeOnError: true);
return completer.future;
}
@@ -603,67 +511,54 @@
Future<String> readAsString([Encoding encoding = Encoding.UTF_8]) {
_ensureFileService();
return readAsBytes().then((bytes) {
- if (bytes.length == 0) return "";
- var decoder = _StringDecoders.decoder(encoding);
- decoder.write(bytes);
- return decoder.decoded();
+ return _decodeString(bytes, encoding);
});
}
String readAsStringSync([Encoding encoding = Encoding.UTF_8]) {
- var decoder = _StringDecoders.decoder(encoding);
List<int> bytes = readAsBytesSync();
- if (bytes.length == 0) return "";
- decoder.write(bytes);
- return decoder.decoded();
+ return _decodeString(bytes, encoding);
}
- List<String> _getDecodedLines(_StringDecoder decoder) {
- List<String> result = [];
- var line = decoder.decodedLine;
- while (line != null) {
- result.add(line);
- line = decoder.decodedLine;
- }
- // If there is more data with no terminating line break we treat
- // it as the last line.
- var data = decoder.decoded();
- if (data != null) {
- result.add(data);
- }
- return result;
+ static List<String> _decodeLines(List<int> bytes, Encoding encoding) {
+ if (bytes.length == 0) return [];
+ var list = [];
+ var controller = new StreamController();
+ controller.stream
+ .transform(new StringDecoder(encoding))
+ .transform(new LineTransformer())
+ .listen((line) => list.add(line));
+ controller.add(bytes);
+ controller.close();
+ return list;
}
Future<List<String>> readAsLines([Encoding encoding = Encoding.UTF_8]) {
_ensureFileService();
Completer<List<String>> completer = new Completer<List<String>>();
return readAsBytes().then((bytes) {
- var decoder = _StringDecoders.decoder(encoding);
- decoder.write(bytes);
- return _getDecodedLines(decoder);
+ return _decodeLines(bytes, encoding);
});
}
List<String> readAsLinesSync([Encoding encoding = Encoding.UTF_8]) {
- var decoder = _StringDecoders.decoder(encoding);
- List<int> bytes = readAsBytesSync();
- decoder.write(bytes);
- return _getDecodedLines(decoder);
+ return _decodeLines(readAsBytesSync(), encoding);
}
Future<File> writeAsBytes(List<int> bytes,
[FileMode mode = FileMode.WRITE]) {
Completer<File> completer = new Completer<File>();
try {
- var stream = openOutputStream(mode);
- stream.write(bytes);
+ var stream = openWrite(mode);
+ stream.add(bytes);
stream.close();
- stream.onClosed = () {
- completer.complete(this);
- };
- stream.onError = (e) {
- completer.completeError(e);
- };
+ stream.done
+ .then((_) {
+ completer.complete(this);
+ })
+ .catchError((e) {
+ completer.completeError(e);
+ });
} catch (e) {
Timer.run(() => completer.completeError(e));
return completer.future;
@@ -681,8 +576,7 @@
{FileMode mode: FileMode.WRITE,
Encoding encoding: Encoding.UTF_8}) {
try {
- var data = _StringEncoders.encoder(encoding).encodeString(contents);
- return writeAsBytes(data, mode);
+ return writeAsBytes(_encodeString(contents, encoding), mode);
} catch (e) {
var completer = new Completer();
Timer.run(() => completer.completeError(e));
@@ -693,8 +587,7 @@
void writeAsStringSync(String contents,
{FileMode mode: FileMode.WRITE,
Encoding encoding: Encoding.UTF_8}) {
- var data = _StringEncoders.encoder(encoding).encodeString(contents);
- writeAsBytesSync(data, mode);
+ writeAsBytesSync(_encodeString(contents, encoding), mode);
}
String get name => _name;
@@ -987,7 +880,7 @@
});
return completer.future;
}
- var data = _StringEncoders.encoder(encoding).encodeString(string);
+ var data = _encodeString(string, encoding);
return writeList(data, 0, data.length);
}
@@ -996,7 +889,7 @@
throw new FileIOException(
"Invalid encoding in writeStringSync: $encoding");
}
- var data = _StringEncoders.encoder(encoding).encodeString(string);
+ var data = _encodeString(string, encoding);
return writeListSync(data, 0, data.length);
}
diff --git a/sdk/lib/io/file_system_entity.dart b/sdk/lib/io/file_system_entity.dart
new file mode 100644
index 0000000..103f2be
--- /dev/null
+++ b/sdk/lib/io/file_system_entity.dart
@@ -0,0 +1,18 @@
+// 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.
+
+part of dart.io;
+
+/**
+ * A [FileSystemEntity] is a common super class for [File] and
+ * [Directory] objects.
+ *
+ * [FileSystemEntity] objects are returned from directory listing
+ * operations. To determine if a FileSystemEntity is a [File] or a
+ * [Directory], perform a type check:
+ *
+ * if (entity is File) (entity as File).readAsStringSync();
+ */
+abstract class FileSystemEntity {
+}
diff --git a/sdk/lib/io/http.dart b/sdk/lib/io/http.dart
index 2862036..9c5a6fb 100644
--- a/sdk/lib/io/http.dart
+++ b/sdk/lib/io/http.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
@@ -57,54 +57,56 @@
/**
* HTTP server.
*/
-abstract class HttpServer {
- factory HttpServer() => new _HttpServer();
-
+abstract class HttpServer implements Stream<HttpRequest> {
+ // TODO(ajohnsen): Document with example, once the stream API is final.
+ // TODO(ajohnsen): Add HttpServer.secure.
/**
* Start listening for HTTP requests on the specified [host] and
* [port]. If a [port] of 0 is specified the server will choose an
* ephemeral port. The optional argument [backlog] can be used to
- * specify the listen backlog for the underlying OS listen.
- * The optional arguments [certificate_name] and [requestClientCertificate]
- * are used by the HttpsServer class, which shares the same interface.
- * See [addRequestHandler] and [defaultRequestHandler] for
- * information on how incoming HTTP requests are handled.
+ * specify the listen backlog for the underlying OS listen
+ * setup.
*/
- void listen(String host,
- int port,
- {int backlog: 128,
- String certificate_name,
- bool requestClientCertificate: false});
+ static Future<HttpServer> bind([String address = "127.0.0.1",
+ int port = 0,
+ int backlog = 0])
+ => _HttpServer.bind(address, port, backlog);
/**
- * Attach the HTTP server to an existing [:ServerSocket:]. If the
+ * Start listening for HTTPS requests on the specified [host] and
+ * [port]. If a [port] of 0 is specified the server will choose an
+ * ephemeral port. The optional argument [backlog] can be used to
+ * specify the listen backlog for the underlying OS listen
+ * setup.
+ *
+ * The certificate with Distinguished Name [certificate_name] is looked
+ * up in the certificate database, and is used as the server certificate.
+ * if [requestClientCertificate] is true, the server will request clients
+ * to authenticate with a client certificate.
+ */
+
+ static Future<HttpServer> bindSecure(String address,
+ int port,
+ {int backlog: 0,
+ String certificateName,
+ bool requestClientCertificate: false})
+ => _HttpServer.bindSecure(address,
+ port,
+ backlog,
+ certificateName,
+ requestClientCertificate);
+
+ /**
+ * Attach the HTTP server to an existing [:ServerSocket:]. When the
* [HttpServer] is closed, the [HttpServer] will just detach itself,
- * and not close [serverSocket].
+ * close current connections but not close [serverSocket].
*/
- void listenOn(ServerSocket serverSocket);
+ factory HttpServer.listenOn(ServerSocket serverSocket)
+ => new _HttpServer.listenOn(serverSocket);
/**
- * Adds a request handler to the list of request handlers. The
- * function [matcher] is called with the request and must return
- * [:true:] if the [handler] should handle the request. The first
- * handler for which [matcher] returns [:true:] will be handed the
- * request.
- */
- addRequestHandler(bool matcher(HttpRequest request),
- void handler(HttpRequest request, HttpResponse response));
-
- /**
- * Sets the default request handler. This request handler will be
- * called if none of the request handlers registered by
- * [addRequestHandler] matches the current request. If no default
- * request handler is set the server will just respond with status
- * code [:NOT_FOUND:] (404).
- */
- void set defaultRequestHandler(
- void handler(HttpRequest request, HttpResponse response));
-
- /**
- * Stop server listening.
+ * Stop server listening. This will make the [Stream] close with a done
+ * event.
*/
void close();
@@ -116,11 +118,6 @@
int get port;
/**
- * Sets the error handler that is called when a connection error occurs.
- */
- void set onError(void callback(e));
-
- /**
* Set the timeout, in seconds, for sessions of this HTTP server. Default
* is 20 minutes.
*/
@@ -128,21 +125,13 @@
/**
* Returns a [:HttpConnectionsInfo:] object with an overview of the
- * current connection handled by the server.
+ * current connections handled by the server.
*/
HttpConnectionsInfo connectionsInfo();
}
/**
- * HTTPS server.
- */
-abstract class HttpsServer implements HttpServer {
- factory HttpsServer() => new _HttpServer.httpsServer();
-}
-
-
-/**
* Overview information of the [:HttpServer:] socket connections.
*/
class HttpConnectionsInfo {
@@ -445,18 +434,13 @@
String toString();
}
-abstract class HttpSession {
+abstract class HttpSession implements Map {
/**
* Get the id for the current session.
*/
String get id;
/**
- * Access the user-data associated with the session.
- */
- dynamic data;
-
- /**
* Destroy the session. This will terminate the session and any further
* connections with this id will be given a new id and session.
*/
@@ -466,6 +450,11 @@
* Set a callback that will be called when the session is timed out.
*/
void set onTimeout(void callback());
+
+ /**
+ * Is true if the session have not been sent to the client yet.
+ */
+ bool get isNew;
}
@@ -590,9 +579,11 @@
/**
- * Http request delivered to the HTTP server callback.
+ * Http request delivered to the HTTP server callback. The [HttpRequest] is a
+ * [Stream] of the body content of the request. Listen to the body to handle the
+ * data and be notified once the entire body is received.
*/
-abstract class HttpRequest {
+abstract class HttpRequest implements Stream<List<int>> {
/**
* Returns the content length of the request body. If the size of
* the request body is not known in advance this -1.
@@ -600,11 +591,6 @@
int get contentLength;
/**
- * Returns the persistent connection state signaled by the client.
- */
- bool get persistentConnection;
-
- /**
* Returns the method for the request.
*/
String get method;
@@ -612,17 +598,7 @@
/**
* Returns the URI for the request.
*/
- String get uri;
-
- /**
- * Returns the path part of the URI.
- */
- String get path;
-
- /**
- * Returns the query string.
- */
- String get queryString;
+ Uri get uri;
/**
* Returns the parsed query string.
@@ -640,6 +616,11 @@
List<Cookie> get cookies;
/**
+ * Returns the persistent connection state signaled by the client.
+ */
+ bool get persistentConnection;
+
+ /**
* Returns the client certificate of the client making the request.
* Returns null if the connection is not a secure TLS or SSL connection,
* or if the server does not request a client certificate, or if the client
@@ -648,19 +629,12 @@
X509Certificate get certificate;
/**
- * Returns, or initialize, a session for the given request. If the session is
- * being initialized by this call, [init] will be called with the
- * newly create session. Here the [:HttpSession.data:] field can be set, if
- * needed.
+ * Get the session for the given request. If the session is
+ * being initialized by this call, [:isNew:] will be true for the returned
+ * session.
* See [:HttpServer.sessionTimeout:] on how to change default timeout.
*/
- HttpSession session([init(HttpSession session)]);
-
- /**
- * Returns the input stream for the request. This is used to read
- * the request data.
- */
- InputStream get inputStream;
+ HttpSession get session;
/**
* Returns the HTTP protocol version used in the request. This will
@@ -673,13 +647,20 @@
* isn't available.
*/
HttpConnectionInfo get connectionInfo;
+
+ /**
+ * Get the [HttpResponse] object, used for sending back the response to the
+ * client.
+ */
+ HttpResponse get response;
}
/**
* HTTP response to be send back to the client.
*/
-abstract class HttpResponse {
+abstract class HttpResponse implements IOSink<HttpResponse> {
+ // TODO(ajohnsen): Add documentation of how to pipe a file to the response.
/**
* Gets and sets the content length of the response. If the size of
* the response is not known in advance set the content length to
@@ -719,17 +700,6 @@
List<Cookie> get cookies;
/**
- * Returns the output stream for the response. This is used to write
- * the response data. When all response data has been written close
- * the stream to indicate the end of the response.
- *
- * When this is accessed for the first time the response header is
- * send. Calling any methods that will change the header after
- * having retrieved the output stream will throw an exception.
- */
- OutputStream get outputStream;
-
- /**
* Detach the underlying socket from the HTTP server. When the
* socket is detached the HTTP server will no longer perform any
* operations on it.
@@ -737,7 +707,7 @@
* This is normally used when a HTTP upgrade request is received
* and the communication should continue with a different protocol.
*/
- DetachedSocket detachSocket();
+ Future<Socket> detachSocket();
/**
* Get information about the client connection. Returns [null] if the socket
@@ -753,7 +723,7 @@
* try to reuse opened sockets for several requests to support HTTP 1.1
* persistent connections. This means that sockets will be kept open for some
* time after a requests have completed, unless HTTP procedures indicate that it
- * must be closed as part of completing the request. Use [:HttpClient.shutdown:]
+ * must be closed as part of completing the request. Use [:HttpClient.close:]
* to force close the idle sockets.
*/
abstract class HttpClient {
@@ -763,50 +733,51 @@
factory HttpClient() => new _HttpClient();
/**
- * Opens a HTTP connection. The returned [HttpClientConnection] is
- * used to register callbacks for asynchronous events on the HTTP
- * connection. The "Host" header for the request will be set to the
- * value [host]:[port]. This can be overridden through the
- * HttpClientRequest interface before the request is sent. NOTE if
- * [host] is an IP address this will still be set in the "Host"
+ * Opens a HTTP connection. The returned [HttpClientRequest] is used to
+ * fill in the content of the request before sending it. The "Host" header for
+ * the request will be set to the value [host]:[port]. This can be overridden
+ * through the [HttpClientRequest] interface before the request is sent.
+ * NOTE if [host] is an IP address this will still be set in the "Host"
* header.
*/
- HttpClientConnection open(String method, String host, int port, String path);
+ Future<HttpClientRequest> open(String method,
+ String host,
+ int port,
+ String path);
/**
- * Opens a HTTP connection. The returned [HttpClientConnection] is
- * used to register callbacks for asynchronous events on the HTTP
- * connection. The "Host" header for the request will be set based
- * the host and port specified in [url]. This can be overridden
- * through the HttpClientRequest interface before the request is
- * sent. NOTE if the host is specified as an IP address this will
- * still be set in the "Host" header.
+ * Opens a HTTP connection. The returned [HttpClientRequest] is used to
+ * fill in the content of the request before sending it. The "Host" header for
+ * the request will be set to the value [host]:[port]. This can be overridden
+ * through the [HttpClientRequest] interface before the request is sent.
+ * NOTE if [host] is an IP address this will still be set in the "Host"
+ * header.
*/
- HttpClientConnection openUrl(String method, Uri url);
+ Future<HttpClientRequest> openUrl(String method, Uri url);
/**
* Opens a HTTP connection using the GET method. See [open] for
* details. Using this method to open a HTTP connection will set the
* content length to 0.
*/
- HttpClientConnection get(String host, int port, String path);
+ Future<HttpClientRequest> get(String host, int port, String path);
/**
* Opens a HTTP connection using the GET method. See [openUrl] for
* details. Using this method to open a HTTP connection will set the
* content length to 0.
*/
- HttpClientConnection getUrl(Uri url);
+ Future<HttpClientRequest> getUrl(Uri url);
/**
* Opens a HTTP connection using the POST method. See [open] for details.
*/
- HttpClientConnection post(String host, int port, String path);
+ Future<HttpClientRequest> post(String host, int port, String path);
/**
* Opens a HTTP connection using the POST method. See [openUrl] for details.
*/
- HttpClientConnection postUrl(Uri url);
+ Future<HttpClientRequest> postUrl(Uri url);
/**
* Sets the function to be called when a site is requesting
@@ -881,109 +852,23 @@
* trying to establish a new connection after calling [shutdown]
* will throw an exception.
*/
- void shutdown({bool force: false});
-}
-
-
-/**
- * A [HttpClientConnection] is returned by all [HttpClient] methods
- * that initiate a connection to an HTTP server. The handlers will be
- * called as the connection state progresses.
- *
- * The setting of all handlers is optional. If [onRequest] is not set
- * the request will be send without any additional headers and an
- * empty body. If [onResponse] is not set the response will be read
- * and discarded.
- */
-abstract class HttpClientConnection {
- /**
- * Sets the handler that is called when the connection is established.
- */
- void set onRequest(void callback(HttpClientRequest request));
-
- /**
- * Sets callback to be called when the request has been send and
- * the response is ready for processing. The callback is called when
- * all headers of the response are received and data is ready to be
- * received.
- */
- void set onResponse(void callback(HttpClientResponse response));
-
- /**
- * Sets the handler that gets called if an error occurs while
- * connecting or processing the HTTP request.
- */
- void set onError(void callback(e));
-
- /**
- * Set this property to [:true:] if this connection should
- * automatically follow redirects. The default is
- * [:true:].
- *
- * Automatic redirect will only happen for "GET" and "HEAD" requests
- * and only for the status codes [:HttpStatus.MOVED_PERMANENTLY:]
- * (301), [:HttpStatus.FOUND:] (302),
- * [:HttpStatus.MOVED_TEMPORARILY:] (302, alias for
- * [:HttpStatus.FOUND:]), [:HttpStatus.SEE_OTHER:] (303) and
- * [:HttpStatus.TEMPORARY_REDIRECT:] (307). For
- * [:HttpStatus.SEE_OTHER:] (303) autmatic redirect will also happen
- * for "POST" requests with the method changed to "GET" when
- * following the redirect.
- *
- * All headers added to the request will be added to the redirection
- * request(s). However, any body send with the request will not be
- * part of the redirection request(s).
- */
- bool followRedirects;
-
- /**
- * Set this property to the maximum number of redirects to follow
- * when [followRedirects] is [:true:]. If this number is exceeded the
- * [onError] callback will be called with a [RedirectLimitExceeded]
- * exception. The default value is 5.
- */
- int maxRedirects;
-
- /**
- * Returns the series of redirects this connection has been through.
- */
- List<RedirectInfo> get redirects;
-
- /**
- * Redirect this connection to a new URL. The default value for
- * [method] is the method for the current request. The default value
- * for [url] is the value of the [:HttpHeaders.LOCATION:] header of
- * the current response. All body data must have been read from the
- * current response before calling [redirect].
- *
- * All headers added to the request will be added to the redirection
- * request(s). However, any body send with the request will not be
- * part of the redirection request(s).
- */
- void redirect([String method, Uri url]);
-
- /**
- * Detach the underlying socket from the HTTP client. When the
- * socket is detached the HTTP client will no longer perform any
- * operations on it.
- *
- * This is normally used when a HTTP upgrade is negotiated and the
- * communication should continue with a different protocol.
- */
- DetachedSocket detachSocket();
-
- /**
- * Get information about the client connection. Returns [null] if the socket
- * isn't available.
- */
- HttpConnectionInfo get connectionInfo;
+ void close({bool force: false});
}
/**
* HTTP request for a client connection.
+ *
+ * The request is an [IOSink], used to write the request data. When
+ * all request data has been written, close the stream to indicate the end of
+ * the request.
+ *
+ * When this is accessed for the first time the request header is
+ * send. Calling any methods that will change the header after
+ * having retrieved the output stream will throw an exception.
*/
-abstract class HttpClientRequest {
+abstract class HttpClientRequest
+ implements IOSink<HttpClientRequest> {
/**
* Gets and sets the content length of the request. If the size of
* the request is not known in advance set content length to -1,
@@ -1008,22 +893,60 @@
bool persistentConnection;
/**
- * Returns the output stream for the request. This is used to write
- * the request data. When all request data has been written close
- * the stream to indicate the end of the request.
- *
- * When this is accessed for the first time the request header is
- * send. Calling any methods that will change the header after
- * having retrieved the output stream will throw an exception.
+ * A [HttpClientResponse] future that will complete once the response is
+ * available. If an error occours before the response is available, this
+ * future will complete with an error.
*/
- OutputStream get outputStream;
+ Future<HttpClientResponse> get response;
+
+ /**
+ * Close the request for input. Returns the value of [response].
+ */
+ Future<HttpClientResponse> close();
+
+ /**
+ * Set this property to [:true:] if this request should
+ * automatically follow redirects. The default is [:true:].
+ *
+ * Automatic redirect will only happen for "GET" and "HEAD" requests
+ * and only for the status codes [:HttpHeaders.MOVED_PERMANENTLY:]
+ * (301), [:HttpStatus.FOUND:] (302),
+ * [:HttpStatus.MOVED_TEMPORARILY:] (302, alias for
+ * [:HttpStatus.FOUND:]), [:HttpStatus.SEE_OTHER:] (303) and
+ * [:HttpStatus.TEMPORARY_REDIRECT:] (307). For
+ * [:HttpStatus.SEE_OTHER:] (303) autmatic redirect will also happen
+ * for "POST" requests with the method changed to "GET" when
+ * following the redirect.
+ *
+ * All headers added to the request will be added to the redirection
+ * request(s). However, any body send with the request will not be
+ * part of the redirection request(s).
+ */
+ bool followRedirects;
+
+ /**
+ * Set this property to the maximum number of redirects to follow
+ * when [followRedirects] is [:true:]. If this number is exceeded the
+ * [onError] callback will be called with a [RedirectLimitExceeded]
+ * exception. The default value is 5.
+ */
+ int maxRedirects;
+
+ /**
+ * Get information about the client connection. Returns [null] if the socket
+ * isn't available.
+ */
+ HttpConnectionInfo get connectionInfo;
}
/**
- * HTTP response for a client connection.
+ * HTTP response for a client connection. The [HttpClientResponse] is a
+ * [Stream] of the body content of the response. Listen to the body to handle
+ * the data and be notified once the entire body is received.
+
*/
-abstract class HttpClientResponse {
+abstract class HttpClientResponse implements Stream<List<int>> {
/**
* Returns the status code.
*/
@@ -1054,11 +977,49 @@
bool get isRedirect;
/**
+ * Returns the series of redirects this connection has been through. The
+ * list will be empty if no redirects was followed. [redirects] will be
+ * updated both in the case of an automatic and a manual redirect.
+ */
+ List<RedirectInfo> get redirects;
+
+ /**
+ * Redirect this connection to a new URL. The default value for
+ * [method] is the method for the current request. The default value
+ * for [url] is the value of the [:HttpHeaders.LOCATION:] header of
+ * the current response. All body data must have been read from the
+ * current response before calling [redirect].
+ *
+ * All headers added to the request will be added to the redirection
+ * request(s). However, any body send with the request will not be
+ * part of the redirection request(s).
+ *
+ * If [followLoops] is set to [true], redirect will follow the redirect,
+ * even if was already visited. Default value is [false].
+ *
+ * [redirect] will ignore [maxRedirects] and always perform the redirect.
+ */
+ Future<HttpClientResponse> redirect([String method,
+ Uri url,
+ bool followLoops]);
+
+
+ /**
* Returns the response headers.
*/
HttpHeaders get headers;
/**
+ * Detach the underlying socket from the HTTP client. When the
+ * socket is detached the HTTP client will no longer perform any
+ * operations on it.
+ *
+ * This is normally used when a HTTP upgrade is negotiated and the
+ * communication should continue with a different protocol.
+ */
+ Future<Socket> detachSocket();
+
+ /**
* Cookies set by the server (from the Set-Cookie header).
*/
List<Cookie> get cookies;
@@ -1070,10 +1031,10 @@
X509Certificate get certificate;
/**
- * Returns the input stream for the response. This is used to read
- * the response data.
+ * Get information about the client connection. Returns [null] if the socket
+ * isn't available.
*/
- InputStream get inputStream;
+ HttpConnectionInfo get connectionInfo;
}
diff --git a/sdk/lib/io/http_headers.dart b/sdk/lib/io/http_headers.dart
index 928334d..949ace3 100644
--- a/sdk/lib/io/http_headers.dart
+++ b/sdk/lib/io/http_headers.dart
@@ -1,11 +1,12 @@
-// Copyright (c) 2012, 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.
part of dart.io;
class _HttpHeaders implements HttpHeaders {
- _HttpHeaders() : _headers = new Map<String, List<String>>();
+ _HttpHeaders(String this.protocolVersion)
+ : _headers = new Map<String, List<String>>();
List<String> operator[](String name) {
name = name.toLowerCase();
@@ -67,6 +68,29 @@
_noFoldingHeaders.add(name);
}
+ bool get persistentConnection {
+ List<String> connection = this[HttpHeaders.CONNECTION];
+ if (protocolVersion == "1.1") {
+ if (connection == null) return true;
+ return !connection.any((value) => value.toLowerCase() == "close");
+ } else {
+ if (connection == null) return false;
+ return connection.any((value) => value.toLowerCase() == "keep-alive");
+ }
+ }
+
+ void set persistentConnection(bool persistentConnection) {
+ _checkMutable();
+ // Determine the value of the "Connection" header.
+ remove(HttpHeaders.CONNECTION, "close");
+ remove(HttpHeaders.CONNECTION, "keep-alive");
+ if (protocolVersion == "1.1" && !persistentConnection) {
+ add(HttpHeaders.CONNECTION, "close");
+ } else if (protocolVersion == "1.0" && persistentConnection) {
+ add(HttpHeaders.CONNECTION, "keep-alive");
+ }
+ }
+
int get contentLength => _contentLength;
void set contentLength(int contentLength) {
@@ -223,27 +247,31 @@
throw new HttpException("Unexpected type for header named $name");
}
} else if (lowerCaseName == "host") {
- int pos = value.indexOf(":");
- if (pos == -1) {
- _host = value;
- _port = HttpClient.DEFAULT_HTTP_PORT;
- } else {
- if (pos > 0) {
- _host = value.substring(0, pos);
- } else {
- _host = null;
- }
- if (pos + 1 == value.length) {
+ if (value is String) {
+ int pos = (value as String).indexOf(":");
+ if (pos == -1) {
+ _host = value;
_port = HttpClient.DEFAULT_HTTP_PORT;
} else {
- try {
- _port = int.parse(value.substring(pos + 1));
- } on FormatException catch (e) {
- _port = null;
+ if (pos > 0) {
+ _host = (value as String).substring(0, pos);
+ } else {
+ _host = null;
+ }
+ if (pos + 1 == value.length) {
+ _port = HttpClient.DEFAULT_HTTP_PORT;
+ } else {
+ try {
+ _port = int.parse(value.substring(pos + 1));
+ } on FormatException catch (e) {
+ _port = null;
+ }
}
}
+ _set("host", value);
+ } else {
+ throw new HttpException("Unexpected type for header named $name");
}
- _set("host", value);
} else if (lowerCaseName == "content-type") {
_set("content-type", value);
} else {
@@ -290,7 +318,7 @@
return true;
}
- void _finalize(String protocolVersion) {
+ void _finalize() {
// If the content length is not known make sure chunked transfer
// encoding is used for HTTP 1.1.
if (contentLength < 0 && protocolVersion == "1.1") {
@@ -306,7 +334,7 @@
_mutable = false;
}
- _write(_HttpConnectionBase connection) {
+ _write(IOSink sink) {
final COLONSP = const [_CharCode.COLON, _CharCode.SP];
final COMMASP = const [_CharCode.COMMA, _CharCode.SP];
final CRLF = const [_CharCode.CR, _CharCode.LF];
@@ -316,7 +344,7 @@
var bufferPos = 0;
void writeBuffer() {
- connection._writeFrom(buffer, 0, bufferPos);
+ sink.add(buffer.getRange(0, bufferPos));
bufferPos = 0;
}
@@ -381,12 +409,78 @@
return sb.toString();
}
+ List<Cookie> _parseCookies() {
+ // Parse a Cookie header value according to the rules in RFC 6265.
+ var cookies = new List<Cookie>();
+ void parseCookieString(String s) {
+ int index = 0;
+
+ bool done() => index == s.length;
+
+ void skipWS() {
+ while (!done()) {
+ if (s[index] != " " && s[index] != "\t") return;
+ index++;
+ }
+ }
+
+ String parseName() {
+ int start = index;
+ while (!done()) {
+ if (s[index] == " " || s[index] == "\t" || s[index] == "=") break;
+ index++;
+ }
+ return s.substring(start, index).toLowerCase();
+ }
+
+ String parseValue() {
+ int start = index;
+ while (!done()) {
+ if (s[index] == " " || s[index] == "\t" || s[index] == ";") break;
+ index++;
+ }
+ return s.substring(start, index).toLowerCase();
+ }
+
+ void expect(String expected) {
+ if (done()) {
+ throw new HttpException("Failed to parse header value [$s]");
+ }
+ if (s[index] != expected) {
+ throw new HttpException("Failed to parse header value [$s]");
+ }
+ index++;
+ }
+
+ while (!done()) {
+ skipWS();
+ if (done()) return;
+ String name = parseName();
+ skipWS();
+ expect("=");
+ skipWS();
+ String value = parseValue();
+ cookies.add(new _Cookie(name, value));
+ skipWS();
+ if (done()) return;
+ expect(";");
+ }
+ }
+ List<String> values = this["cookie"];
+ if (values != null) {
+ values.forEach((headerValue) => parseCookieString(headerValue));
+ }
+ return cookies;
+ }
+
+
bool _mutable = true; // Are the headers currently mutable?
Map<String, List<String>> _headers;
List<String> _noFoldingHeaders;
int _contentLength = -1;
bool _chunkedTransferEncoding = false;
+ final String protocolVersion;
String _host;
int _port;
}
diff --git a/sdk/lib/io/http_impl.dart b/sdk/lib/io/http_impl.dart
index 4a8d266a..8bc8a98 100644
--- a/sdk/lib/io/http_impl.dart
+++ b/sdk/lib/io/http_impl.dart
@@ -4,344 +4,94 @@
part of dart.io;
-// The close queue handles graceful closing of HTTP connections. When
-// a connection is added to the queue it will enter a wait state
-// waiting for all data written and possibly socket shutdown from
-// peer.
-class _CloseQueue {
- _CloseQueue() : _q = new Set<_HttpConnectionBase>();
+class _HttpIncoming
+ extends Stream<List<int>> implements StreamSink<List<int>> {
+ final int _transferLength;
+ final Completer _dataCompleter = new Completer();
+ Stream<List<int>> _stream;
- void add(_HttpConnectionBase connection) {
- void closeIfDone() {
- // When either the client has closed or all data has been
- // written to the client we close the underlying socket
- // completely.
- if (connection._isWriteClosed || connection._isReadClosed) {
- _q.remove(connection);
- connection._socket.close();
- if (connection.onClosed != null) connection.onClosed();
- }
- }
+ bool fullBodyRead = false;
- // If the connection is already fully closed don't insert it into
- // the queue.
- if (connection._isFullyClosed) {
- connection._socket.close();
- if (connection.onClosed != null) connection.onClosed();
- return;
- }
+ // Common properties.
+ final _HttpHeaders headers;
+ bool upgraded = false;
- connection._state |= _HttpConnectionBase.CLOSING;
- _q.add(connection);
+ // ClientResponse properties.
+ int statusCode;
+ String reasonPhrase;
- // If the output stream is not closed for writing, close it now and
- // wait for callback when closed.
- if (!connection._isWriteClosed) {
- connection._socket.outputStream.close();
- connection._socket.outputStream.onClosed = () {
- connection._state |= _HttpConnectionBase.WRITE_CLOSED;
- closeIfDone();
- };
- } else {
- connection._socket.outputStream.onClosed = () { assert(false); };
- }
+ // Request properties.
+ String method;
+ Uri uri;
- // If the request is not already fully read wait for the socket to close.
- // As the _isReadClosed state from the HTTP request processing indicate
- // that the response has been parsed this does not necesarily mean tha
- // the socket is closed.
- if (!connection._isReadClosed) {
- connection._socket.onClosed = () {
- connection._state |= _HttpConnectionBase.READ_CLOSED;
- closeIfDone();
- };
- }
+ // The transfer length if the length of the message body as it
+ // appears in the message (RFC 2616 section 4.4). This can be -1 if
+ // the length of the massage body is not known due to transfer
+ // codings.
+ int get transferLength => _transferLength;
- // Ignore any data on a socket in the close queue.
- connection._socket.onData = connection._socket.read;
-
- // If an error occurs immediately close the socket.
- connection._socket.onError = (e) {
- connection._state |= _HttpConnectionBase.READ_CLOSED;
- connection._state |= _HttpConnectionBase.WRITE_CLOSED;
- closeIfDone();
- };
+ _HttpIncoming(_HttpHeaders this.headers,
+ int this._transferLength,
+ Stream<List<int>> this._stream) {
}
- void shutdown() {
- _q.forEach((_HttpConnectionBase connection) {
- connection._socket.close();
- });
+ StreamSubscription<List<int>> listen(void onData(List<int> event),
+ {void onError(AsyncError error),
+ void onDone(),
+ bool unsubscribeOnError}) {
+ return _stream.listen(onData,
+ onError: onError,
+ onDone: onDone,
+ unsubscribeOnError: unsubscribeOnError);
}
- final Set<_HttpConnectionBase> _q;
+ // Is completed once all data have been received.
+ Future get dataDone => _dataCompleter.future;
+
+ void close() {
+ fullBodyRead = true;
+ _dataCompleter.complete();
+ }
}
-
-class _HttpRequestResponseBase {
- static const int START = 0;
- static const int HEADER_SENT = 1;
- static const int DONE = 2;
- static const int UPGRADED = 3;
-
- _HttpRequestResponseBase(_HttpConnectionBase this._httpConnection)
- : _state = START, _headResponse = false;
-
- int get contentLength => _headers.contentLength;
- HttpHeaders get headers => _headers;
-
- bool get persistentConnection {
- List<String> connection = headers[HttpHeaders.CONNECTION];
- if (_protocolVersion == "1.1") {
- if (connection == null) return true;
- return !headers[HttpHeaders.CONNECTION].any(
- (value) => value.toLowerCase() == "close");
- } else {
- if (connection == null) return false;
- return headers[HttpHeaders.CONNECTION].any(
- (value) => value.toLowerCase() == "keep-alive");
- }
- }
-
- X509Certificate get certificate {
- var socket = _httpConnection._socket as SecureSocket;
- return socket == null ? socket : socket.peerCertificate;
- }
-
- void set persistentConnection(bool persistentConnection) {
- if (_outputStream != null) throw new HttpException("Header already sent");
-
- // Determine the value of the "Connection" header.
- headers.remove(HttpHeaders.CONNECTION, "close");
- headers.remove(HttpHeaders.CONNECTION, "keep-alive");
- if (_protocolVersion == "1.1" && !persistentConnection) {
- headers.add(HttpHeaders.CONNECTION, "close");
- } else if (_protocolVersion == "1.0" && persistentConnection) {
- headers.add(HttpHeaders.CONNECTION, "keep-alive");
- }
- }
-
-
- bool _write(List<int> data, bool copyBuffer) {
- if (_headResponse) return true;
- _ensureHeadersSent();
- bool allWritten = true;
- if (data.length > 0) {
- if (_headers.chunkedTransferEncoding) {
- // Write chunk size if transfer encoding is chunked.
- _writeHexString(data.length);
- _writeCRLF();
- _httpConnection._write(data, copyBuffer);
- allWritten = _writeCRLF();
- } else {
- _updateContentLength(data.length);
- allWritten = _httpConnection._write(data, copyBuffer);
- }
- }
- return allWritten;
- }
-
- bool _writeList(List<int> data, int offset, int count) {
- if (_headResponse) return true;
- _ensureHeadersSent();
- bool allWritten = true;
- if (count > 0) {
- if (_headers.chunkedTransferEncoding) {
- // Write chunk size if transfer encoding is chunked.
- _writeHexString(count);
- _writeCRLF();
- _httpConnection._writeFrom(data, offset, count);
- allWritten = _writeCRLF();
- } else {
- _updateContentLength(count);
- allWritten = _httpConnection._writeFrom(data, offset, count);
- }
- }
- return allWritten;
- }
-
- bool _writeDone() {
- bool allWritten = true;
- if (_headers.chunkedTransferEncoding) {
- // Terminate the content if transfer encoding is chunked.
- allWritten = _httpConnection._write(_Const.END_CHUNKED);
- } else {
- if (!_headResponse && _bodyBytesWritten < _headers.contentLength) {
- throw new HttpException("Sending less than specified content length");
- }
- assert(_headResponse || _bodyBytesWritten == _headers.contentLength);
- }
- return allWritten;
- }
-
- bool _writeHeaders() {
- _headers._write(_httpConnection);
- // Terminate header.
- return _writeCRLF();
- }
-
- bool _writeHexString(int x) {
- final List<int> hexDigits = [0x30, 0x31, 0x32, 0x33, 0x34,
- 0x35, 0x36, 0x37, 0x38, 0x39,
- 0x41, 0x42, 0x43, 0x44, 0x45, 0x46];
- List<int> hex = new Uint8List(10);
- int index = hex.length;
- while (x > 0) {
- index--;
- hex[index] = hexDigits[x % 16];
- x = x >> 4;
- }
- return _httpConnection._writeFrom(hex, index, hex.length - index);
- }
-
- bool _writeCRLF() {
- final CRLF = const [_CharCode.CR, _CharCode.LF];
- return _httpConnection._write(CRLF);
- }
-
- bool _writeSP() {
- final SP = const [_CharCode.SP];
- return _httpConnection._write(SP);
- }
-
- void _ensureHeadersSent() {
- // Ensure that headers are written.
- if (_state == START) {
- _writeHeader();
- }
- }
-
- void _updateContentLength(int bytes) {
- if (_bodyBytesWritten + bytes > _headers.contentLength) {
- throw new HttpException("Writing more than specified content length");
- }
- _bodyBytesWritten += bytes;
- }
-
- HttpConnectionInfo get connectionInfo => _httpConnection.connectionInfo;
-
- bool get _done => _state == DONE;
-
- int _state;
- bool _headResponse;
-
- _HttpConnectionBase _httpConnection;
- _HttpHeaders _headers;
+class _HttpInboundMessage extends Stream<List<int>> {
+ final _HttpIncoming _incoming;
List<Cookie> _cookies;
- String _protocolVersion = "1.1";
- // Number of body bytes written. This is only actual body data not
- // including headers or chunk information of using chinked transfer
- // encoding.
- int _bodyBytesWritten = 0;
-}
-
-
-// Parsed HTTP request providing information on the HTTP headers.
-class _HttpRequest extends _HttpRequestResponseBase implements HttpRequest {
- _HttpRequest(_HttpConnection connection) : super(connection);
-
- String get method => _method;
- String get uri => _uri;
- String get path => _path;
- String get queryString => _queryString;
- Map get queryParameters => _queryParameters;
+ _HttpInboundMessage(_HttpIncoming this._incoming);
List<Cookie> get cookies {
if (_cookies != null) return _cookies;
-
- // Parse a Cookie header value according to the rules in RFC 6265.
- void _parseCookieString(String s) {
- int index = 0;
-
- bool done() => index == s.length;
-
- void skipWS() {
- while (!done()) {
- if (s[index] != " " && s[index] != "\t") return;
- index++;
- }
- }
-
- String parseName() {
- int start = index;
- while (!done()) {
- if (s[index] == " " || s[index] == "\t" || s[index] == "=") break;
- index++;
- }
- return s.substring(start, index).toLowerCase();
- }
-
- String parseValue() {
- int start = index;
- while (!done()) {
- if (s[index] == " " || s[index] == "\t" || s[index] == ";") break;
- index++;
- }
- return s.substring(start, index).toLowerCase();
- }
-
- void expect(String expected) {
- if (done()) {
- throw new HttpException("Failed to parse header value [$s]");
- }
- if (s[index] != expected) {
- throw new HttpException("Failed to parse header value [$s]");
- }
- index++;
- }
-
- while (!done()) {
- skipWS();
- if (done()) return;
- String name = parseName();
- skipWS();
- expect("=");
- skipWS();
- String value = parseValue();
- _cookies.add(new _Cookie(name, value));
- skipWS();
- if (done()) return;
- expect(";");
- }
- }
-
- _cookies = new List<Cookie>();
- List<String> headerValues = headers["cookie"];
- if (headerValues != null) {
- headerValues.forEach((headerValue) => _parseCookieString(headerValue));
- }
- return _cookies;
+ return _cookies = headers._parseCookies();
}
- InputStream get inputStream {
- if (_inputStream == null) {
- _inputStream = new _HttpInputStream(this);
- }
- return _inputStream;
- }
+ HttpHeaders get headers => _incoming.headers;
+ String get protocolVersion => headers.protocolVersion;
+ int get contentLength => headers.contentLength;
+ bool get persistentConnection => headers.persistentConnection;
+}
- String get protocolVersion => _protocolVersion;
- HttpSession session([init(HttpSession session)]) {
- if (_session != null) {
- // It's already mapped, use it.
- return _session;
- }
- // Create session, store it in connection, and return.
- var sessionManager = _httpConnection._server._sessionManager;
- return _session = sessionManager.createSession(init);
- }
+class _HttpRequest extends _HttpInboundMessage implements HttpRequest {
+ final HttpResponse response;
- void _onRequestReceived(String method,
- String uri,
- String version,
- _HttpHeaders headers) {
- _method = method;
- _uri = uri;
- _parseRequestUri(uri);
- _headers = headers;
- if (_httpConnection._server._sessionManagerInstance != null) {
+ // Lazy initialized parsed query parameters.
+ Map<String, String> _queryParameters;
+
+ final _HttpServer _httpServer;
+
+ final _HttpConnection _httpConnection;
+
+ HttpSession _session;
+
+ _HttpRequest(_HttpResponse this.response,
+ _HttpIncoming _incoming,
+ _HttpServer this._httpServer,
+ _HttpConnection this._httpConnection)
+ : super(_incoming) {
+ response.headers.persistentConnection = headers.persistentConnection;
+
+ if (_httpServer._sessionManagerInstance != null) {
// Map to session if exists.
var sessionId = cookies.reduce(null, (last, cookie) {
if (last != null) return last;
@@ -349,175 +99,382 @@
cookie.value : null;
});
if (sessionId != null) {
- var sessionManager = _httpConnection._server._sessionManager;
- _session = sessionManager.getSession(sessionId);
+ _session = _httpServer._sessionManager.getSession(sessionId);
if (_session != null) {
_session._markSeen();
}
}
}
-
- // Prepare for receiving data.
- _buffer = new _BufferList();
}
- void _onDataReceived(List<int> data) {
- _buffer.add(data);
- if (_inputStream != null) _inputStream._dataReceived();
+ StreamSubscription<List<int>> listen(void onData(List<int> event),
+ {void onError(AsyncError error),
+ void onDone(),
+ bool unsubscribeOnError}) {
+ return _incoming.listen(onData,
+ onError: onError,
+ onDone: onDone,
+ unsubscribeOnError: unsubscribeOnError);
}
- void _onDataEnd() {
- if (_inputStream != null) {
- _inputStream._closeReceived();
- } else {
- inputStream._streamMarkedClosed = true;
+ Map<String, String> get queryParameters {
+ if (_queryParameters == null) {
+ _queryParameters = _HttpUtils.splitQueryString(uri.query);
}
+ return _queryParameters;
}
- // Escaped characters in uri are expected to have been parsed.
- void _parseRequestUri(String uri) {
- int position;
- position = uri.indexOf("?", 0);
- if (position == -1) {
- _path = _HttpUtils.decodeUrlEncodedString(_uri);
- _queryString = null;
- _queryParameters = new Map();
- } else {
- _path = _HttpUtils.decodeUrlEncodedString(_uri.substring(0, position));
- _queryString = _uri.substring(position + 1);
- _queryParameters = _HttpUtils.splitQueryString(_queryString);
+ Uri get uri => _incoming.uri;
+
+ String get method => _incoming.method;
+
+ HttpSession get session {
+ if (_session != null) {
+ // It's already mapped, use it.
+ return _session;
}
+ // Create session, store it in connection, and return.
+ return _session = _httpServer._sessionManager.createSession();
}
- // Delegate functions for the HttpInputStream implementation.
- int _streamAvailable() {
- return _buffer.length;
- }
+ HttpConnectionInfo get connectionInfo => _httpConnection.connectionInfo;
- List<int> _streamRead(int bytesToRead) {
- return _buffer.readBytes(bytesToRead);
+ X509Certificate get certificate {
+ Socket socket = _httpConnection._socket;
+ if (socket is SecureSocket) return socket.peerCertificate;
+ return null;
}
-
- int _streamReadInto(List<int> buffer, int offset, int len) {
- List<int> data = _buffer.readBytes(len);
- buffer.setRange(offset, data.length, data);
- return data.length;
- }
-
- void _streamSetErrorHandler(callback(e)) {
- _streamErrorHandler = callback;
- }
-
- String _method;
- String _uri;
- String _path;
- String _queryString;
- Map<String, String> _queryParameters;
- _HttpInputStream _inputStream;
- _BufferList _buffer;
- Function _streamErrorHandler;
- _HttpSession _session;
}
-// HTTP response object for sending a HTTP response.
-class _HttpResponse extends _HttpRequestResponseBase implements HttpResponse {
- _HttpResponse(_HttpConnection httpConnection)
- : super(httpConnection),
- _statusCode = HttpStatus.OK {
- _headers = new _HttpHeaders();
- }
+class _HttpClientResponse
+ extends _HttpInboundMessage implements HttpClientResponse {
+ List<RedirectInfo> get redirects => _httpRequest._responseRedirects;
- void set contentLength(int contentLength) {
- if (_state >= _HttpRequestResponseBase.HEADER_SENT) {
- throw new HttpException("Header already sent");
+ // The HttpClient this response belongs to.
+ final _HttpClient _httpClient;
+
+ // The HttpClientRequest of this response.
+ final _HttpClientRequest _httpRequest;
+
+ List<Cookie> _cookies;
+
+ _HttpClientResponse(_HttpIncoming _incoming,
+ _HttpClientRequest this._httpRequest,
+ _HttpClient this._httpClient)
+ : super(_incoming);
+
+ int get statusCode => _incoming.statusCode;
+ String get reasonPhrase => _incoming.reasonPhrase;
+
+ List<Cookie> get cookies {
+ if (_cookies != null) return _cookies;
+ _cookies = new List<Cookie>();
+ List<String> values = headers["set-cookie"];
+ if (values != null) {
+ values.forEach((value) {
+ _cookies.add(new Cookie.fromSetCookieValue(value));
+ });
}
- _headers.contentLength = contentLength;
+ return _cookies;
}
- int get statusCode => _statusCode;
- void set statusCode(int statusCode) {
- if (_outputStream != null) throw new HttpException("Header already sent");
- _statusCode = statusCode;
+ bool get isRedirect {
+ if (_httpRequest.method == "GET" || _httpRequest.method == "HEAD") {
+ return statusCode == HttpStatus.MOVED_PERMANENTLY ||
+ statusCode == HttpStatus.FOUND ||
+ statusCode == HttpStatus.SEE_OTHER ||
+ statusCode == HttpStatus.TEMPORARY_REDIRECT;
+ } else if (_httpRequest.method == "POST") {
+ return statusCode == HttpStatus.SEE_OTHER;
+ }
+ return false;
}
- String get reasonPhrase => _findReasonPhrase(_statusCode);
- void set reasonPhrase(String reasonPhrase) {
- if (_outputStream != null) throw new HttpException("Header already sent");
- _reasonPhrase = reasonPhrase;
+ Future<HttpClientResponse> redirect([String method,
+ Uri url,
+ bool followLoops]) {
+ if (method == null) {
+ // Set method as defined by RFC 2616 section 10.3.4.
+ if (statusCode == HttpStatus.SEE_OTHER && _httpRequest.method == "POST") {
+ method = "GET";
+ } else {
+ method = _httpRequest.method;
+ }
+ }
+ if (url == null) {
+ String location = headers.value(HttpHeaders.LOCATION);
+ if (location == null) {
+ throw new StateError("Response has no Location header for redirect");
+ }
+ url = Uri.parse(location);
+ }
+ if (followLoops != true) {
+ for (var redirect in redirects) {
+ if (redirect.location == url) {
+ return new Future.immediateError(
+ new RedirectLoopException(redirects));
+ }
+ }
+ }
+ return _httpClient._openUrlFromRequest(method, url, _httpRequest)
+ .then((request) {
+ request._responseRedirects.addAll(this.redirects);
+ request._responseRedirects.add(new _RedirectInfo(statusCode,
+ method,
+ url));
+ return request.close();
+ });
}
+ StreamSubscription<List<int>> listen(void onData(List<int> event),
+ {void onError(AsyncError error),
+ void onDone(),
+ bool unsubscribeOnError}) {
+ return _incoming.listen(onData,
+ onError: onError,
+ onDone: onDone,
+ unsubscribeOnError: unsubscribeOnError);
+ }
+
+ Future<Socket> detachSocket() {
+ _httpClient._connectionClosed(_httpRequest._httpClientConnection);
+ return _httpRequest._httpClientConnection.detachSocket();
+ }
+
+ HttpConnectionInfo get connectionInfo => _httpRequest.connectionInfo;
+
+ bool get _shouldAuthenticate {
+ // Only try to authenticate if there is a challenge in the response.
+ List<String> challenge = headers[HttpHeaders.WWW_AUTHENTICATE];
+ return statusCode == HttpStatus.UNAUTHORIZED &&
+ challenge != null && challenge.length == 1;
+ }
+
+ Future<HttpClientResponse> _authenticate() {
+ Future<HttpClientResponse> retryWithCredentials(_Credentials cr) {
+ if (cr != null) {
+ // TODO(sgjesse): Support digest.
+ if (cr.scheme == _AuthenticationScheme.BASIC) {
+ // Drain body and retry.
+ return reduce(null, (x, y) {}).then((_) {
+ return _httpClient._openUrlFromRequest(_httpRequest.method,
+ _httpRequest.uri,
+ _httpRequest)
+ .then((request) => request.close());
+ });
+ }
+ }
+
+ // Fall through to here to perform normal response handling if
+ // there is no sensible authorization handling.
+ return new Future.immediate(this);
+ }
+
+ List<String> challenge = headers[HttpHeaders.WWW_AUTHENTICATE];
+ assert(challenge != null || challenge.length == 1);
+ _HeaderValue header =
+ new _HeaderValue.fromString(challenge[0], parameterSeparator: ",");
+ _AuthenticationScheme scheme =
+ new _AuthenticationScheme.fromString(header.value);
+ String realm = header.parameters["realm"];
+
+ // See if any credentials are available.
+ _Credentials cr = _httpClient._findCredentials(_httpRequest.uri, scheme);
+
+ if (cr != null && !cr.used) {
+ // If credentials found prepare for retrying the request.
+ return retryWithCredentials(cr);
+ }
+
+ // Ask for more credentials if none found or the one found has
+ // already been used. If it has already been used it must now be
+ // invalid and is removed.
+ if (cr != null) {
+ _httpClient._removeCredentials(cr);
+ cr = null;
+ }
+ if (_httpClient._authenticate != null) {
+ Future authComplete = _httpClient._authenticate(_httpRequest.uri,
+ scheme.toString(),
+ realm);
+ return authComplete.then((credsAvailable) {
+ if (credsAvailable) {
+ cr = _httpClient._findCredentials(_httpRequest.uri, scheme);
+ return retryWithCredentials(cr);
+ } else {
+ // No credentials available, complete with original response.
+ return this;
+ }
+ });
+ }
+ // No credentials were found and the callback was not set.
+ return new Future.immediate(this);
+ }
+}
+
+
+class _HttpOutboundMessage<T> extends IOSink {
+ // Used to mark when the body should be written. This is used for HEAD
+ // requests and in error handling.
+ bool _ignoreBody = false;
+
+ _HttpOutboundMessage(String protocolVersion, _HttpOutgoing outgoing)
+ : super(outgoing),
+ _outgoing = outgoing,
+ headers = new _HttpHeaders(protocolVersion);
+
+ int get contentLength => headers.contentLength;
+ void set contentLength(int contentLength) {
+ headers.contentLength = contentLength;
+ }
+
+ bool get persistentConnection => headers.persistentConnection;
+ bool set persistentConnection(bool p) {
+ headers.persistentConnection = p;
+ }
+
+ Future<T> consume(Stream<List<int>> stream) {
+ _writeHeaders();
+ if (_ignoreBody) return new Future.immediate(this);
+ if (_chunked) {
+ // Transform when chunked.
+ stream = stream.transform(new _ChunkedTransformer());
+ }
+ return super.consume(stream).then((_) => this);
+ }
+
+ void add(List<int> data) {
+ _writeHeaders();
+ if (_ignoreBody) return;
+ if (_chunked) {
+ _ChunkedTransformer._addChunk(data, super.add);
+ } else {
+ super.add(data);
+ }
+ }
+
+ void close() {
+ if (!_headersWritten && !_ignoreBody && headers.chunkedTransferEncoding) {
+ // If no body was written, _ignoreBody is false (it's not a HEAD
+ // request) and the content-length is unspecified, set contentLength to 0.
+ headers.contentLength = 0;
+ }
+ _writeHeaders();
+ if (!_ignoreBody) {
+ if (_chunked) {
+ _ChunkedTransformer._addChunk([], super.add);
+ }
+ }
+ super.close();
+ }
+
+ void _writeHeaders() {
+ if (_headersWritten) return;
+ bool _tmpIgnoreBody = _ignoreBody;
+ _ignoreBody = false;
+ _headersWritten = true;
+ _writeHeader();
+ _ignoreBody = _tmpIgnoreBody;
+ if (_ignoreBody) {
+ super.close();
+ return;
+ }
+ _chunked = headers.chunkedTransferEncoding;
+ if (!_chunked) {
+ _outgoing.setTransferLength(headers.contentLength);
+ }
+ }
+
+ void _writeHeader(); // TODO(ajohnsen): Better name.
+
+ final _HttpHeaders headers;
+
+ final _HttpOutgoing _outgoing;
+ bool _headersWritten = false;
+ bool _chunked = false;
+}
+
+
+class _HttpResponse extends _HttpOutboundMessage<HttpResponse>
+ implements HttpResponse {
+ int statusCode = 200;
+ String _reasonPhrase;
+ List<Cookie> _cookies;
+ _HttpRequest _httpRequest;
+
+ _HttpResponse(String protocolVersion,
+ _HttpOutgoing _outgoing)
+ : super(protocolVersion, _outgoing);
+
List<Cookie> get cookies {
if (_cookies == null) _cookies = new List<Cookie>();
return _cookies;
}
- OutputStream get outputStream {
- if (_state >= _HttpRequestResponseBase.DONE) {
- throw new HttpException("Response closed");
+ String get reasonPhrase => _findReasonPhrase(statusCode);
+ void set reasonPhrase(String reasonPhrase) {
+ if (_headersWritten) throw new StateError("Header already sent");
+ _reasonPhrase = reasonPhrase;
+ }
+
+ Future<Socket> detachSocket() {
+ if (_headersWritten) throw new StateError("Headers already sent");
+ _writeHeaders();
+ var future = _httpRequest._httpConnection.detachSocket();
+ // Close connection so the socket is 'free'.
+ close();
+ return future;
+ }
+
+ HttpConnectionInfo get connectionInfo => _httpRequest.connectionInfo;
+
+ void _writeHeader() {
+ writeSP() => add([_CharCode.SP]);
+ writeCRLF() => add([_CharCode.CR, _CharCode.LF]);
+
+ // Write status line.
+ if (headers.protocolVersion == "1.1") {
+ add(_Const.HTTP11);
+ } else {
+ add(_Const.HTTP10);
}
- if (_outputStream == null) {
- _outputStream = new _HttpOutputStream(this);
+ writeSP();
+ addString(statusCode.toString());
+ writeSP();
+ addString(reasonPhrase);
+ writeCRLF();
+
+ var session = _httpRequest._session;
+ if (session != null && !session._destroyed) {
+ // Mark as not new.
+ session._isNew = false;
+ // Make sure we only send the current session id.
+ bool found = false;
+ for (int i = 0; i < cookies.length; i++) {
+ if (cookies[i].name.toUpperCase() == _DART_SESSION_ID) {
+ cookies[i].value = session.id;
+ cookies[i].httpOnly = true;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ cookies.add(new Cookie(_DART_SESSION_ID, session.id)..httpOnly = true);
+ }
}
- return _outputStream;
- }
-
- DetachedSocket detachSocket() {
- if (_state >= _HttpRequestResponseBase.DONE) {
- throw new HttpException("Response closed");
+ // Add all the cookies set to the headers.
+ if (_cookies != null) {
+ _cookies.forEach((cookie) {
+ headers.add("set-cookie", cookie);
+ });
}
- // Ensure that headers are written.
- if (_state == _HttpRequestResponseBase.START) {
- _writeHeader();
- }
- _state = _HttpRequestResponseBase.UPGRADED;
- // Ensure that any trailing data is written.
- _writeDone();
- // Indicate to the connection that the response handling is done.
- return _httpConnection._detachSocket();
- }
- // Delegate functions for the HttpOutputStream implementation.
- bool _streamWrite(List<int> buffer, bool copyBuffer) {
- if (_done) throw new HttpException("Response closed");
- return _write(buffer, copyBuffer);
- }
+ headers._finalize();
- bool _streamWriteFrom(List<int> buffer, int offset, int len) {
- if (_done) throw new HttpException("Response closed");
- return _writeList(buffer, offset, len);
- }
-
- void _streamFlush() {
- _httpConnection._flush();
- }
-
- void _streamClose() {
- _ensureHeadersSent();
- _state = _HttpRequestResponseBase.DONE;
- // Stop tracking no pending write events.
- _httpConnection._onNoPendingWrites = null;
- // Ensure that any trailing data is written.
- _writeDone();
- // Indicate to the connection that the response handling is done.
- _httpConnection._responseClosed();
- if (_streamClosedHandler != null) {
- Timer.run(_streamClosedHandler);
- }
- }
-
- void _streamSetNoPendingWriteHandler(callback()) {
- if (_state != _HttpRequestResponseBase.DONE) {
- _httpConnection._onNoPendingWrites = callback;
- }
- }
-
- void _streamSetClosedHandler(callback()) {
- _streamClosedHandler = callback;
- }
-
- void _streamSetErrorHandler(callback(e)) {
- _streamErrorHandler = callback;
+ // Write headers.
+ headers._write(this);
+ writeCRLF();
}
String _findReasonPhrase(int statusCode) {
@@ -574,547 +531,871 @@
default: return "Status $statusCode";
}
}
+}
- bool _writeHeader() {
- List<int> data;
- // Write status line.
- if (_protocolVersion == "1.1") {
- _httpConnection._write(_Const.HTTP11);
- } else {
- _httpConnection._write(_Const.HTTP10);
+class _HttpClientRequest extends _HttpOutboundMessage<HttpClientRequest>
+ implements HttpClientRequest {
+ final String method;
+ final Uri uri;
+ final List<Cookie> cookies = new List<Cookie>();
+
+ // The HttpClient this request belongs to.
+ final _HttpClient _httpClient;
+ final _HttpClientConnection _httpClientConnection;
+
+ final Completer<HttpClientResponse> _responseCompleter
+ = new Completer<HttpClientResponse>();
+
+ final bool _usingProxy;
+
+ // TODO(ajohnsen): Get default value from client?
+ bool _followRedirects = true;
+
+ int _maxRedirects = 5;
+
+ List<RedirectInfo> _responseRedirects = [];
+
+ _HttpClientRequest(_HttpOutgoing outgoing,
+ Uri this.uri,
+ String this.method,
+ bool this._usingProxy,
+ _HttpClient this._httpClient,
+ _HttpClientConnection this._httpClientConnection)
+ : super("1.1", outgoing) {
+ // GET and HEAD have 'content-length: 0' by default.
+ if (method == "GET" || method == "HEAD") {
+ contentLength = 0;
}
- _writeSP();
- data = _statusCode.toString().charCodes;
- _httpConnection._write(data);
- _writeSP();
- data = reasonPhrase.charCodes;
- _httpConnection._write(data);
- _writeCRLF();
+ }
- var session = _httpConnection._request._session;
- if (session != null && !session._destroyed) {
- // Make sure we only send the current session id.
- bool found = false;
- for (int i = 0; i < cookies.length; i++) {
- if (cookies[i].name.toUpperCase() == _DART_SESSION_ID) {
- cookies[i].value = session.id;
- cookies[i].httpOnly = true;
- found = true;
- break;
+ Future<HttpClientResponse> get response => _responseCompleter.future;
+
+ Future<HttpClientResponse> close() {
+ super.close();
+ return response;
+ }
+
+ int get maxRedirects => _maxRedirects;
+ void set maxRedirects(int maxRedirects) {
+ if (_headersWritten) throw new StateError("Request already sent");
+ _maxRedirects = maxRedirects;
+ }
+
+ bool get followRedirects => _followRedirects;
+ void set followRedirects(bool followRedirects) {
+ if (_headersWritten) throw new StateError("Request already sent");
+ _followRedirects = followRedirects;
+ }
+
+ HttpConnectionInfo get connectionInfo => _httpClientConnection.connectionInfo;
+
+ void _onIncoming(_HttpIncoming incoming) {
+ var response = new _HttpClientResponse(incoming,
+ this,
+ _httpClient);
+ Future<HttpClientResponse> future;
+ if (followRedirects && response.isRedirect) {
+ if (response.redirects.length < maxRedirects) {
+ // Redirect and drain response.
+ future = response.reduce(null, (x, y) {})
+ .then((_) => response.redirect());
+ } else {
+ // End with exception, too many redirects.
+ future = response.reduce(null, (x, y) {})
+ .then((_) => new Future.immediateError(
+ new RedirectLimitExceededException(response.redirects)));
+ }
+ } else if (response._shouldAuthenticate) {
+ future = response._authenticate();
+ } else {
+ future = new Future<HttpClientResponse>.immediate(response);
+ }
+ future.then(
+ (v) => _responseCompleter.complete(v),
+ onError: (e) {
+ _responseCompleter.completeError(e);
+ });
+ }
+
+ void _onError(AsyncError error) {
+ _responseCompleter.completeError(error);
+ }
+
+ void _writeHeader() {
+ writeSP() => add([_CharCode.SP]);
+ writeCRLF() => add([_CharCode.CR, _CharCode.LF]);
+
+ addString(method);
+ writeSP();
+ // Send the path for direct connections and the whole URL for
+ // proxy connections.
+ if (!_usingProxy) {
+ String path = uri.path;
+ if (path.length == 0) path = "/";
+ if (uri.query != "") {
+ if (uri.fragment != "") {
+ path = "${path}?${uri.query}#${uri.fragment}";
+ } else {
+ path = "${path}?${uri.query}";
}
}
- if (!found) {
- cookies.add(new Cookie(_DART_SESSION_ID, session.id)..httpOnly = true);
+ addString(path);
+ } else {
+ addString(uri.toString());
+ }
+ writeSP();
+ add(_Const.HTTP11);
+ writeCRLF();
+
+ // Add the cookies to the headers.
+ if (!cookies.isEmpty) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < cookies.length; i++) {
+ if (i > 0) sb.add("; ");
+ sb.add(cookies[i].name);
+ sb.add("=");
+ sb.add(cookies[i].value);
}
+ headers.add("cookie", sb.toString());
}
- // Add all the cookies set to the headers.
- if (_cookies != null) {
- _cookies.forEach((cookie) {
- _headers.add("set-cookie", cookie);
- });
- }
+
+ headers._finalize();
// Write headers.
- _headers._finalize(_protocolVersion);
- bool allWritten = _writeHeaders();
- _state = _HttpRequestResponseBase.HEADER_SENT;
- return allWritten;
+ headers._write(this);
+ writeCRLF();
}
-
- int _statusCode; // Response status code.
- String _reasonPhrase; // Response reason phrase.
- _HttpOutputStream _outputStream;
- Function _streamClosedHandler;
- Function _streamErrorHandler;
}
-class _HttpInputStream extends _BaseDataInputStream implements InputStream {
- _HttpInputStream(_HttpRequestResponseBase this._requestOrResponse) {
- _checkScheduleCallbacks();
+// Transformer that transforms data to HTTP Chunked Encoding.
+class _ChunkedTransformer implements StreamTransformer<List<int>, List<int>> {
+ final StreamController<List<int>> _controller
+ = new StreamController<List<int>>();
+
+ Stream<List<int>> bind(Stream<List<int>> stream) {
+ var subscription = stream.listen(
+ (data) {
+ if (data.length == 0) return; // Avoid close on 0-bytes payload.
+ _addChunk(data, _controller.add);
+ },
+ onDone: () {
+ _addChunk([], _controller.add);
+ _controller.close();
+ });
+ return _controller.stream;
}
- int available() {
- return _requestOrResponse._streamAvailable();
+ static void _addChunk(List<int> data, void add(List<int> data)) {
+ add(_chunkHeader(data.length));
+ if (data.length > 0) add(data);
+ add(_chunkFooter);
}
- void pipe(OutputStream output, {bool close: true}) {
- _pipe(this, output, close: close);
+ static List<int> _chunkHeader(int length) {
+ const hexDigits = const [0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46];
+ var header = [];
+ if (length == 0) {
+ header.add(hexDigits[length]);
+ } else {
+ while (length > 0) {
+ header.insertRange(0, 1, hexDigits[length % 16]);
+ length = length >> 4;
+ }
+ }
+ header.add(_CharCode.CR);
+ header.add(_CharCode.LF);
+ return header;
}
- List<int> _read(int bytesToRead) {
- List<int> result = _requestOrResponse._streamRead(bytesToRead);
- _checkScheduleCallbacks();
- return result;
- }
-
- void set onError(void callback(e)) {
- _requestOrResponse._streamSetErrorHandler(callback);
- }
-
- int _readInto(List<int> buffer, int offset, int len) {
- int result = _requestOrResponse._streamReadInto(buffer, offset, len);
- _checkScheduleCallbacks();
- return result;
- }
-
- void _close() {
- // TODO(sgjesse): Handle this.
- }
-
- void _dataReceived() {
- super._dataReceived();
- }
-
- _HttpRequestResponseBase _requestOrResponse;
+ // Footer is just a CRLF.
+ static List<int> get _chunkFooter => const [_CharCode.CR, _CharCode.LF];
}
-class _HttpOutputStream extends _BaseOutputStream implements OutputStream {
- _HttpOutputStream(_HttpRequestResponseBase this._requestOrResponse);
+// Transformer that invokes [_onDone] when completed.
+class _DoneTransformer implements StreamTransformer<List<int>, List<int>> {
+ final StreamController<List<int>> _controller
+ = new StreamController<List<int>>();
+ final Function _onDone;
- bool write(List<int> buffer, [bool copyBuffer = true]) {
- return _requestOrResponse._streamWrite(buffer, copyBuffer);
+ _DoneTransformer(this._onDone);
+
+ Stream<List<int>> bind(Stream<List<int>> stream) {
+ var subscription = stream.listen(
+ _controller.add,
+ onError: _controller.signalError,
+ onDone: () {
+ _onDone();
+ _controller.close();
+ });
+ return _controller.stream;
+ }
+}
+
+// Transformer that validates the data written.
+class _DataValidatorTransformer
+ implements StreamTransformer<List<int>, List<int>> {
+ final StreamController<List<int>> _controller
+ = new StreamController<List<int>>();
+ int _bytesWritten = 0;
+ Completer _completer = new Completer();
+
+ int expectedTransferLength;
+
+ _DataValidatorTransformer();
+
+ Future get validatorFuture => _completer.future;
+
+ Stream<List<int>> bind(Stream<List<int>> stream) {
+ var subscription;
+ subscription = stream.listen(
+ (data) {
+ if (expectedTransferLength != null) {
+ _bytesWritten += data.length;
+ if (_bytesWritten > expectedTransferLength) {
+ _controller.close();
+ subscription.cancel();
+ if (_completer != null) {
+ _completer.completeError(new HttpException(
+ "Content size exceeds specified contentLength. "
+ "$_bytesWritten bytes written while expected "
+ "$expectedTransferLength."));
+ _completer = null;
+ }
+ return;
+ }
+ }
+ _controller.add(data);
+ },
+ onError: (error) {
+ _controller.close();
+ if (_completer != null) {
+ _completer.completeError(error);
+ _completer = null;
+ }
+ },
+ onDone: () {
+ _controller.close();
+ if (expectedTransferLength != null) {
+ if (_bytesWritten < expectedTransferLength) {
+ if (_completer != null) {
+ _completer.completeError(new HttpException(
+ "Content size below specified contentLength. "
+ " $_bytesWritten bytes written while expected "
+ "$expectedTransferLength."));
+ _completer = null;
+ return;
+ }
+ }
+ }
+ if (_completer != null) {
+ _completer.complete(this);
+ _completer = null;
+ }
+ },
+ unsubscribeOnError: true);
+ return _controller.stream;
+ }
+}
+
+// Extends StreamConsumer as this is an internal type, only used to pipe to.
+class _HttpOutgoing implements StreamConsumer<List<int>, dynamic> {
+ final Completer _dataCompleter = new Completer();
+ final Completer _streamCompleter = new Completer();
+ final _DataValidatorTransformer _validator = new _DataValidatorTransformer();
+
+ // Future that completes when all data is written.
+ Future get dataDone => _dataCompleter.future;
+
+ // Future that completes with the Stream, once the _HttpClientConnection is
+ // bound to one.
+ Future<Stream<List<int>>> get stream => _streamCompleter.future;
+
+ void setTransferLength(int transferLength) {
+ _validator.expectedTransferLength = transferLength;
}
- bool writeFrom(List<int> buffer, [int offset = 0, int len]) {
- if (offset < 0 || offset >= buffer.length) throw new ArgumentError();
- len = len != null ? len : buffer.length - offset;
- if (len < 0) throw new ArgumentError();
- return _requestOrResponse._streamWriteFrom(buffer, offset, len);
+ Future consume(Stream<List<int>> stream) {
+ stream = stream.transform(_validator);
+ _streamCompleter.complete(stream);
+ _validator.validatorFuture.catchError((e) {
+ _dataCompleter.completeError(e);
+ });
+ return _validator.validatorFuture.then((v) {
+ _dataCompleter.complete();
+ return v;
+ });
+ }
+}
+
+
+class _HttpClientConnection {
+ final String key;
+ final Socket _socket;
+ final _HttpParser _httpParser;
+ StreamSubscription _subscription;
+ final _HttpClient _httpClient;
+
+ Completer<_HttpIncoming> _nextResponseCompleter;
+ Future _writeDoneFuture;
+
+ _HttpClientConnection(String this.key,
+ Socket this._socket,
+ _HttpClient this._httpClient)
+ : _httpParser = new _HttpParser.responseParser() {
+ _socket.pipe(_httpParser);
+ _socket.done.catchError((e) { destroy(); });
+
+ // Set up handlers on the parser here, so we are sure to get 'onDone' from
+ // the parser.
+ _subscription = _httpParser.listen(
+ (incoming) {
+ // Only handle one incoming response at the time. Keep the
+ // stream paused until the response have been processed.
+ _subscription.pause();
+ // We assume the response is not here, until we have send the request.
+ assert(_nextResponseCompleter != null);
+ _nextResponseCompleter.complete(incoming);
+ },
+ onError: (error) {
+ if (_nextResponseCompleter != null) {
+ _nextResponseCompleter.completeError(error);
+ }
+ },
+ onDone: () {
+ close();
+ });
}
- void flush() {
- _requestOrResponse._streamFlush();
+ Future<_HttpIncoming> sendRequest(_HttpOutgoing outgoing) {
+ return outgoing.stream
+ .then((stream) {
+ // Close socket if output data is invalid.
+ outgoing.dataDone.catchError((e) {
+ close();
+ });
+ // Sending request, set up response completer.
+ _nextResponseCompleter = new Completer();
+ _writeDoneFuture = _socket.addStream(stream);
+ // Listen for response.
+ return _nextResponseCompleter.future
+ .whenComplete(() {
+ _nextResponseCompleter = null;
+ })
+ .then((incoming) {
+ incoming.dataDone.then((_) {
+ if (!incoming.headers.persistentConnection) {
+ close();
+ } else {
+ // Wait for the socket to be done with writing, before we
+ // continue.
+ _writeDoneFuture.then((_) {
+ _subscription.resume();
+ // Return connection, now we are done.
+ _httpClient._returnConnection(this);
+ });
+ }
+ });
+ // TODO(ajohnsen): Can there be an error on dataDone?
+ return incoming;
+ })
+ // If we see a state error, we failed to get the 'first' element.
+ // Transform the error to a HttpParserException, for consistency.
+ .catchError((error) {
+ throw new HttpParserException(
+ "Connection closed before data was received");
+ }, test: (error) => error is StateError)
+ .catchError((error) {
+ // We are done with the socket.
+ destroy();
+ throw error;
+ });
+ });
}
- void close() {
- _requestOrResponse._streamClose();
+ Future<Socket> detachSocket() {
+ return _writeDoneFuture.then((_) =>
+ new _DetachedSocket(_socket, _httpParser.detachIncoming()));
}
- bool get closed => _requestOrResponse._done;
-
void destroy() {
- throw "Not implemented";
- }
-
- void set onNoPendingWrites(void callback()) {
- _requestOrResponse._streamSetNoPendingWriteHandler(callback);
- }
-
- void set onClosed(void callback()) {
- _requestOrResponse._streamSetClosedHandler(callback);
- }
-
- void set onError(void callback(e)) {
- _requestOrResponse._streamSetErrorHandler(callback);
- }
-
- _HttpRequestResponseBase _requestOrResponse;
-}
-
-
-abstract class _HttpConnectionBase {
- static const int IDLE = 0;
- static const int ACTIVE = 1;
- static const int CLOSING = 2;
- static const int REQUEST_DONE = 4;
- static const int RESPONSE_DONE = 8;
- static const int ALL_DONE = REQUEST_DONE | RESPONSE_DONE;
- static const int READ_CLOSED = 16;
- static const int WRITE_CLOSED = 32;
- static const int FULLY_CLOSED = READ_CLOSED | WRITE_CLOSED;
-
- _HttpConnectionBase() : hashCode = _nextHashCode {
- _nextHashCode = (_nextHashCode + 1) & 0xFFFFFFF;
- }
-
- bool get _isIdle => (_state & ACTIVE) == 0;
- bool get _isActive => (_state & ACTIVE) == ACTIVE;
- bool get _isClosing => (_state & CLOSING) == CLOSING;
- bool get _isRequestDone => (_state & REQUEST_DONE) == REQUEST_DONE;
- bool get _isResponseDone => (_state & RESPONSE_DONE) == RESPONSE_DONE;
- bool get _isAllDone => (_state & ALL_DONE) == ALL_DONE;
- bool get _isReadClosed => (_state & READ_CLOSED) == READ_CLOSED;
- bool get _isWriteClosed => (_state & WRITE_CLOSED) == WRITE_CLOSED;
- bool get _isFullyClosed => (_state & FULLY_CLOSED) == FULLY_CLOSED;
-
- void _connectionEstablished(Socket socket) {
- _socket = socket;
- // Register handlers for socket events. All socket events are
- // passed to the HTTP parser.
- _socket.onData = () {
- List<int> buffer = _socket.read();
- if (buffer != null) {
- _httpParser.streamData(buffer);
- }
- };
- _socket.onClosed = _httpParser.streamDone;
- _socket.onError = _httpParser.streamError;
- _socket.outputStream.onError = _httpParser.streamError;
- }
-
- bool _write(List<int> data, [bool copyBuffer = false]);
- bool _writeFrom(List<int> buffer, [int offset, int len]);
- bool _flush();
- bool _close();
- bool _destroy();
- DetachedSocket _detachSocket();
-
- HttpConnectionInfo get connectionInfo {
- if (_socket == null) return null;
- try {
- _HttpConnectionInfo info = new _HttpConnectionInfo();
- info.remoteHost = _socket.remoteHost;
- info.remotePort = _socket.remotePort;
- info.localPort = _socket.port;
- return info;
- } catch (e) { }
- return null;
- }
-
- void set _onNoPendingWrites(void callback()) {
- _socket.outputStream.onNoPendingWrites = callback;
- }
-
- int _state = IDLE;
-
- Socket _socket;
- _HttpParser _httpParser;
-
- // Callbacks.
- Function onDetach;
- Function onClosed;
-
- // Hash code for HTTP connection. Currently this is just a counter.
- final int hashCode;
- static int _nextHashCode = 0;
-}
-
-
-// HTTP server connection over a socket.
-class _HttpConnection extends _HttpConnectionBase {
- _HttpConnection(HttpServer this._server) {
- _httpParser = new _HttpParser.requestParser();
- // Register HTTP parser callbacks.
- _httpParser.requestStart = _onRequestReceived;
- _httpParser.dataReceived = _onDataReceived;
- _httpParser.dataEnd = _onDataEnd;
- _httpParser.error = _onError;
- _httpParser.closed = _onClosed;
- _httpParser.responseStart = (statusCode, reasonPhrase, version) {
- assert(false);
- };
- }
-
- void _bufferData(List<int> data, [bool copyBuffer = false]) {
- if (_buffer == null) _buffer = new _BufferList();
- if (copyBuffer) data = data.getRange(0, data.length);
- _buffer.add(data);
- }
-
- void _writeBufferedResponse() {
- if (_buffer != null) {
- while (!_buffer.isEmpty) {
- var data = _buffer.first;
- _socket.outputStream.write(data, false);
- _buffer.removeBytes(data.length);
- }
- _buffer = null;
- }
- }
-
- bool _write(List<int> data, [bool copyBuffer = false]) {
- if (_isRequestDone || !_hasBody || _httpParser.upgrade) {
- return _socket.outputStream.write(data, copyBuffer);
- } else {
- _bufferData(data, copyBuffer);
- return false;
- }
- }
-
- bool _writeFrom(List<int> data, [int offset, int len]) {
- if (_isRequestDone || !_hasBody || _httpParser.upgrade) {
- return _socket.outputStream.writeFrom(data, offset, len);
- } else {
- if (offset == null) offset = 0;
- if (len == null) len = buffer.length - offset;
- _bufferData(data.getRange(offset, len), false);
- return false;
- }
- }
-
- bool _flush() {
- _socket.outputStream.flush();
- }
-
- bool _close() {
- _socket.outputStream.close();
- }
-
- bool _destroy() {
- _socket.close();
- }
-
- void _onClosed() {
- _state |= _HttpConnectionBase.READ_CLOSED;
- _checkDone();
- }
-
- DetachedSocket _detachSocket() {
- _socket.onData = null;
- _socket.onClosed = null;
- _socket.onError = null;
- _socket.outputStream.onNoPendingWrites = null;
- _writeBufferedResponse();
- Socket socket = _socket;
- _socket = null;
- if (onDetach != null) onDetach();
- return new _DetachedSocket(socket, _httpParser.readUnparsedData());
- }
-
- void _onError(e) {
- // Don't report errors for a request parser when HTTP parser is in
- // idle state. Clients can close the connection and cause a
- // connection reset by peer error which is OK.
- _onClosed();
- if (_state == _HttpConnectionBase.IDLE) return;
-
- // Propagate the error to the streams.
- if (_request != null &&
- !_isRequestDone &&
- _request._streamErrorHandler != null) {
- _request._streamErrorHandler(e);
- } else if (_response != null &&
- !_isResponseDone &&
- _response._streamErrorHandler != null) {
- _response._streamErrorHandler(e);
- } else {
- onError(e);
- }
- if (_socket != null) _socket.close();
- }
-
- void _onRequestReceived(String method,
- String uri,
- String version,
- _HttpHeaders headers,
- bool hasBody) {
- _state = _HttpConnectionBase.ACTIVE;
- // Create new request and response objects for this request.
- _request = new _HttpRequest(this);
- _response = new _HttpResponse(this);
- _request._onRequestReceived(method, uri, version, headers);
- _request._protocolVersion = version;
- _response._protocolVersion = version;
- _response._headResponse = method == "HEAD";
- _response.persistentConnection = _httpParser.persistentConnection;
- _hasBody = hasBody;
- if (onRequestReceived != null) {
- onRequestReceived(_request, _response);
- }
- _checkDone();
- }
-
- void _onDataReceived(List<int> data) {
- _request._onDataReceived(data);
- _checkDone();
- }
-
- void _checkDone() {
- if (_isReadClosed) {
- // If the client closes the conversation is ended.
- _server._closeQueue.add(this);
- } else if (_isAllDone) {
- // If we are done writing the response, and the connection is
- // not persistent, we must close. Also if using HTTP 1.0 and the
- // content length was not known we must close to indicate end of
- // body.
- bool close =
- !_response.persistentConnection ||
- (_response._protocolVersion == "1.0" && _response.contentLength < 0);
- _request = null;
- _response = null;
- if (close) {
- _httpParser.cancel();
- _server._closeQueue.add(this);
- } else {
- _state = _HttpConnectionBase.IDLE;
- }
- } else if (_isResponseDone && _hasBody) {
- // If the response is closed before the request is fully read
- // close this connection. If there is buffered output
- // (e.g. error response for invalid request where the server did
- // not care to read the request body) this is send.
- assert(!_isRequestDone);
- _writeBufferedResponse();
- _httpParser.cancel();
- _server._closeQueue.add(this);
- }
- }
-
- void _onDataEnd(bool close) {
- // Start sending queued response if any.
- _state |= _HttpConnectionBase.REQUEST_DONE;
- _writeBufferedResponse();
- _request._onDataEnd();
- _checkDone();
- }
-
- void _responseClosed() {
- _state |= _HttpConnectionBase.RESPONSE_DONE;
- }
-
- HttpServer _server;
- HttpRequest _request;
- HttpResponse _response;
- bool _hasBody = false;
-
- // Buffer for data written before full response has been processed.
- _BufferList _buffer;
-
- // Callbacks.
- Function onRequestReceived;
- Function onError;
-}
-
-
-class _RequestHandlerRegistration {
- _RequestHandlerRegistration(Function this._matcher, Function this._handler);
- Function _matcher;
- Function _handler;
-}
-
-// HTTP server waiting for socket connections. The connections are
-// managed by the server and as requests are received the request.
-// HTTPS connections are also supported, if the _HttpServer.httpsServer
-// constructor is used and a certificate name is provided in listen,
-// or a SecureServerSocket is provided to listenOn.
-class _HttpServer implements HttpServer, HttpsServer {
- _HttpServer() : this._internal(isSecure: false);
-
- _HttpServer.httpsServer() : this._internal(isSecure: true);
-
- _HttpServer._internal({ bool isSecure: false })
- : _secure = isSecure,
- _connections = new Set<_HttpConnection>(),
- _handlers = new List<_RequestHandlerRegistration>(),
- _closeQueue = new _CloseQueue();
-
- void listen(String host,
- int port,
- {int backlog: 128,
- String certificate_name,
- bool requestClientCertificate: false}) {
- if (_secure) {
- listenOn(new SecureServerSocket(
- host,
- port,
- backlog,
- certificate_name,
- requestClientCertificate: requestClientCertificate));
- } else {
- listenOn(new ServerSocket(host, port, backlog));
- }
- _closeServer = true;
- }
-
- void listenOn(ServerSocket serverSocket) {
- if (_secure && serverSocket is! SecureServerSocket) {
- throw new HttpException(
- 'HttpsServer.listenOn was called with non-secure server socket');
- } else if (!_secure && serverSocket is SecureServerSocket) {
- throw new HttpException(
- 'HttpServer.listenOn was called with a secure server socket');
- }
- void onConnection(Socket socket) {
- // Accept the client connection.
- _HttpConnection connection = new _HttpConnection(this);
- connection._connectionEstablished(socket);
- _connections.add(connection);
- connection.onRequestReceived = _handleRequest;
- connection.onClosed = () => _connections.remove(connection);
- connection.onDetach = () => _connections.remove(connection);
- connection.onError = (e) {
- _connections.remove(connection);
- if (_onError != null) {
- _onError(e);
- } else {
- throw(e);
- }
- };
- }
- serverSocket.onConnection = onConnection;
- _server = serverSocket;
- _closeServer = false;
- }
-
- addRequestHandler(bool matcher(HttpRequest request),
- void handler(HttpRequest request, HttpResponse response)) {
- _handlers.add(new _RequestHandlerRegistration(matcher, handler));
- }
-
- void set defaultRequestHandler(
- void handler(HttpRequest request, HttpResponse response)) {
- _defaultHandler = handler;
+ _socket.destroy();
+ _httpClient._connectionClosed(this);
}
void close() {
- _closeQueue.shutdown();
+ var future = _writeDoneFuture;
+ if (future == null) future = new Future.immediate(null);
+ _httpClient._connectionClosed(this);
+ future.then((_) {
+ _socket.close();
+ // TODO(ajohnsen): Add timeout.
+ // Delay destroy until socket is actually done writing.
+ _socket.done.then((_) => _socket.destroy(),
+ onError: (_) => _socket.destroy());
+ });
+ }
+
+ HttpConnectionInfo get connectionInfo => _HttpConnectionInfo.create(_socket);
+}
+
+class _ConnnectionInfo {
+ _ConnnectionInfo(_HttpClientConnection this.connection, _Proxy this.proxy);
+ final _HttpClientConnection connection;
+ final _Proxy proxy;
+}
+
+
+class _HttpClient implements HttpClient {
+ // TODO(ajohnsen): Use eviction timeout.
+ static const int DEFAULT_EVICTION_TIMEOUT = 60000;
+ bool _closing = false;
+
+ final Map<String, Queue<_HttpClientConnection>> _idleConnections
+ = new Map<String, Queue<_HttpClientConnection>>();
+ final Set<_HttpClientConnection> _activeConnections
+ = new Set<_HttpClientConnection>();
+ final List<_Credentials> _credentials = [];
+ Function _authenticate;
+ Function _findProxy;
+
+ Future<HttpClientRequest> open(String method,
+ String host,
+ int port,
+ String path) {
+ // TODO(sgjesse): The path set here can contain both query and
+ // fragment. They should be cracked and set correctly.
+ return _openUrl(method, new Uri.fromComponents(
+ scheme: "http", domain: host, port: port, path: path));
+ }
+
+ Future<HttpClientRequest> openUrl(String method, Uri url) {
+ return _openUrl(method, url);
+ }
+
+ Future<HttpClientRequest> get(String host,
+ int port,
+ String path) {
+ return open("get", host, port, path);
+ }
+
+ Future<HttpClientRequest> getUrl(Uri url) {
+ return _openUrl("get", url);
+ }
+
+ Future<HttpClientRequest> post(String host,
+ int port,
+ String path) {
+ return open("post", host, port, path);
+ }
+
+ Future<HttpClientRequest> postUrl(Uri url) {
+ return _openUrl("post", url);
+ }
+
+ void close({bool force: false}) {
+ _closing = true;
+ // Create flattened copy of _idleConnections, as 'destory' will manipulate
+ // it.
+ var idle = _idleConnections.values.reduce(
+ [],
+ (l, e) {
+ l.addAll(e);
+ return l;
+ });
+ idle.forEach((e) {
+ e.close();
+ });
+ assert(_idleConnections.isEmpty);
+ if (force) {
+ for (var connection in _activeConnections.toList()) {
+ connection.destroy();
+ }
+ assert(_activeConnections.isEmpty);
+ _activeConnections.clear();
+ }
+ }
+
+ set authenticate(Future<bool> f(Uri url, String scheme, String realm)) {
+ _authenticate = f;
+ }
+
+ void addCredentials(Uri url, String realm, HttpClientCredentials cr) {
+ _credentials.add(new _Credentials(url, realm, cr));
+ }
+
+ set findProxy(String f(Uri uri)) => _findProxy = f;
+
+ Future<HttpClientRequest> _openUrl(String method, Uri uri) {
+ if (method == null) {
+ throw new ArgumentError(method);
+ }
+ if (uri.domain.isEmpty || (uri.scheme != "http" && uri.scheme != "https")) {
+ throw new ArgumentError("Unsupported scheme '${uri.scheme}' in $uri");
+ }
+
+ bool isSecure = (uri.scheme == "https");
+ int port = uri.port;
+ if (port == 0) {
+ port = isSecure ?
+ HttpClient.DEFAULT_HTTPS_PORT :
+ HttpClient.DEFAULT_HTTP_PORT;
+ }
+ // Check to see if a proxy server should be used for this connection.
+ var proxyConf = const _ProxyConfiguration.direct();
+ if (_findProxy != null) {
+ // TODO(sgjesse): Keep a map of these as normally only a few
+ // configuration strings will be used.
+ try {
+ proxyConf = new _ProxyConfiguration(_findProxy(uri));
+ } catch (error, stackTrace) {
+ return new Future.immediateError(error, stackTrace);
+ }
+ }
+ return _getConnection(uri.domain, port, proxyConf, isSecure).then((info) {
+ // Create new internal outgoing connection.
+ var outgoing = new _HttpOutgoing();
+ // Create new request object, wrapping the outgoing connection.
+ var request = new _HttpClientRequest(outgoing,
+ uri,
+ method.toUpperCase(),
+ !info.proxy.isDirect,
+ this,
+ info.connection);
+ request.headers.host = uri.domain;
+ request.headers.port = port;
+ if (uri.userInfo != null && !uri.userInfo.isEmpty) {
+ // If the URL contains user information use that for basic
+ // authorization
+ String auth =
+ CryptoUtils.bytesToBase64(_encodeString(uri.userInfo));
+ request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth");
+ } else {
+ // Look for credentials.
+ _Credentials cr = _findCredentials(uri);
+ if (cr != null) {
+ cr.authorize(request);
+ }
+ }
+ // Start sending the request (lazy, delayed until the user provides
+ // data).
+ info.connection._httpParser.responseToMethod = method;
+ info.connection.sendRequest(outgoing)
+ .then((incoming) {
+ // The full request have been sent and a response is received
+ // containing status-code, headers and etc.
+ request._onIncoming(incoming);
+ })
+ .catchError((error) {
+ // An error occoured before the http-header was parsed. This
+ // could be either a socket-error or parser-error.
+ request._onError(error);
+ });
+ // Return the request to the user. Immediate socket errors are not
+ // handled, thus forwarded to the user.
+ return request;
+ });
+ }
+
+ Future<HttpClientRequest> _openUrlFromRequest(String method,
+ Uri uri,
+ _HttpClientRequest previous) {
+ return openUrl(method, uri).then((request) {
+ // Only follow redirects if initial request did.
+ request.followRedirects = previous.followRedirects;
+ // Allow same number of redirects.
+ request.maxRedirects = previous.maxRedirects;
+ // Copy headers
+ for (var header in previous.headers._headers.keys) {
+ if (request.headers[header] == null) {
+ request.headers.set(header, previous.headers[header]);
+ }
+ }
+ request.headers.chunkedTransferEncoding = false;
+ request.contentLength = 0;
+ return request;
+ });
+ }
+
+ // Return a live connection to the idle pool.
+ void _returnConnection(_HttpClientConnection connection) {
+ _activeConnections.remove(connection);
+ if (_closing) {
+ connection.close();
+ return;
+ }
+ // TODO(ajohnsen): Listen for socket close events.
+ if (!_idleConnections.containsKey(connection.key)) {
+ _idleConnections[connection.key] = new Queue();
+ }
+ _idleConnections[connection.key].addLast(connection);
+ }
+
+ // Remove a closed connnection from the active set.
+ void _connectionClosed(_HttpClientConnection connection) {
+ _activeConnections.remove(connection);
+ if (_idleConnections.containsKey(connection.key)) {
+ _idleConnections[connection.key].remove(connection);
+ if (_idleConnections[connection.key].isEmpty) {
+ _idleConnections.remove(connection.key);
+ }
+ }
+ }
+
+ // Get a new _HttpClientConnection, either from the idle pool or created from
+ // a new Socket.
+ Future<_ConnnectionInfo> _getConnection(String uriHost,
+ int uriPort,
+ _ProxyConfiguration proxyConf,
+ bool isSecure) {
+ Iterator<_Proxy> proxies = proxyConf.proxies.iterator;
+
+ Future<_ConnnectionInfo> connect(error) {
+ if (!proxies.moveNext()) return new Future.immediateError(error);
+ _Proxy proxy = proxies.current;
+ String host = proxy.isDirect ? uriHost: proxy.host;
+ int port = proxy.isDirect ? uriPort: proxy.port;
+ String key = isSecure ? "ssh:$host:$port" : "$host:$port";
+ if (_idleConnections.containsKey(key)) {
+ var connection = _idleConnections[key].removeFirst();
+ if (_idleConnections[key].isEmpty) {
+ _idleConnections.remove(key);
+ }
+ _activeConnections.add(connection);
+ return new Future.immediate(new _ConnnectionInfo(connection, proxy));
+ }
+ return (isSecure && proxy.isDirect
+ ? SecureSocket.connect(host,
+ port,
+ sendClientCertificate: true)
+ : Socket.connect(host, port))
+ .then((socket) {
+ var connection = new _HttpClientConnection(key, socket, this);
+ _activeConnections.add(connection);
+ return new _ConnnectionInfo(connection, proxy);
+ }, onError: (error) {
+ // Continue with next proxy.
+ return connect(error.error);
+ });
+ }
+ return connect(new HttpException("No proxies given"));
+ }
+
+ _Credentials _findCredentials(Uri url, [_AuthenticationScheme scheme]) {
+ // Look for credentials.
+ _Credentials cr =
+ _credentials.reduce(null, (_Credentials prev, _Credentials value) {
+ if (value.applies(url, scheme)) {
+ if (prev == null) return value;
+ return value.uri.path.length > prev.uri.path.length ? value : prev;
+ } else {
+ return prev;
+ }
+ });
+ return cr;
+ }
+
+ void _removeCredentials(_Credentials cr) {
+ int index = _credentials.indexOf(cr);
+ if (index != -1) {
+ _credentials.removeAt(index);
+ }
+ }
+}
+
+
+class _HttpConnection {
+ static const _ACTIVE = 0;
+ static const _IDLE = 1;
+ static const _CLOSING = 2;
+ static const _DETACHED = 3;
+
+ int _state = _IDLE;
+
+ final Socket _socket;
+ final _HttpServer _httpServer;
+ final _HttpParser _httpParser;
+ StreamSubscription _subscription;
+
+ Future _writeDoneFuture;
+
+ _HttpConnection(Socket this._socket, _HttpServer this._httpServer)
+ : _httpParser = new _HttpParser.requestParser() {
+ _socket.pipe(_httpParser);
+ _socket.done.catchError((e) => destroy());
+ _subscription = _httpParser.listen(
+ (incoming) {
+ // Only handle one incoming request at the time. Keep the
+ // stream paused until the request has been send.
+ _subscription.pause();
+ _state = _ACTIVE;
+ var outgoing = new _HttpOutgoing();
+ _writeDoneFuture = outgoing.stream.then(_socket.addStream);
+ var response = new _HttpResponse(
+ incoming.headers.protocolVersion,
+ outgoing);
+ var request = new _HttpRequest(response, incoming, _httpServer, this);
+ response._ignoreBody = request.method == "HEAD";
+ response._httpRequest = request;
+ outgoing.dataDone.then((_) {
+ if (_state == _DETACHED) return;
+ if (response.headers.persistentConnection &&
+ incoming.fullBodyRead) {
+ // Wait for the socket to be done with writing, before we
+ // continue.
+ _writeDoneFuture.then((_) {
+ _state = _IDLE;
+ // Resume the subscription for incoming requests as the
+ // request is now processed.
+ _subscription.resume();
+ });
+ } else {
+ // Close socket, keep-alive not used or body sent before received
+ // data was handled.
+ close();
+ }
+ }).catchError((e) {
+ close();
+ });
+ _httpServer._handleRequest(request);
+ },
+ onDone: () {
+ close();
+ },
+ onError: (error) {
+ _httpServer._handleError(error);
+ destroy();
+ });
+ }
+
+ void destroy() {
+ if (_state == _CLOSING || _state == _DETACHED) return;
+ _state = _CLOSING;
+ _socket.destroy();
+ _httpServer._connectionClosed(this);
+ }
+
+ void close() {
+ if (_state == _CLOSING || _state == _DETACHED) return;
+ _state = _CLOSING;
+ var future = _writeDoneFuture;
+ if (future == null) future = new Future.immediate(null);
+ _httpServer._connectionClosed(this);
+ future.then((_) {
+ _socket.close();
+ // TODO(ajohnsen): Add timeout.
+ // Delay destroy until socket is actually done writing.
+ _socket.done.then((_) => _socket.destroy(),
+ onError: (_) => _socket.destroy());
+ });
+ }
+
+ Future<Socket> detachSocket() {
+ _state = _DETACHED;
+ // Remove connection from server.
+ _httpServer._connectionClosed(this);
+
+ _HttpDetachedIncoming detachedIncoming = _httpParser.detachIncoming();
+
+ return _writeDoneFuture.then((_) {
+ return new _DetachedSocket(_socket, detachedIncoming);
+ });
+ }
+
+ HttpConnectionInfo get connectionInfo => _HttpConnectionInfo.create(_socket);
+
+ bool get _isActive => _state == _ACTIVE;
+ bool get _isIdle => _state == _IDLE;
+ bool get _isClosing => _state == _CLOSING;
+ bool get _isDetached => _state == _DETACHED;
+}
+
+
+// HTTP server waiting for socket connections.
+class _HttpServer extends Stream<HttpRequest> implements HttpServer {
+
+ static Future<HttpServer> bind(String host, int port, int backlog) {
+ return ServerSocket.bind(host, port, backlog).then((socket) {
+ return new _HttpServer._(socket, true);
+ });
+ }
+
+ static Future<HttpServer> bindSecure(String host,
+ int port,
+ int backlog,
+ String certificate_name,
+ bool requestClientCertificate) {
+ return SecureServerSocket.bind(
+ host,
+ port,
+ backlog,
+ certificate_name,
+ requestClientCertificate: requestClientCertificate)
+ .then((socket) {
+ return new _HttpServer._(socket, true);
+ });
+ }
+
+ _HttpServer._(this._serverSocket, this._closeServer);
+
+ _HttpServer.listenOn(ServerSocket this._serverSocket)
+ : _closeServer = false;
+
+ StreamSubscription<HttpRequest> listen(void onData(HttpRequest event),
+ {void onError(AsyncError error),
+ void onDone(),
+ bool unsubscribeOnError}) {
+ _serverSocket.listen(
+ (Socket socket) {
+ // Accept the client connection.
+ _HttpConnection connection = new _HttpConnection(socket, this);
+ _connections.add(connection);
+ },
+ onError: _controller.signalError,
+ onDone: _controller.close);
+ return _controller.stream.listen(onData,
+ onError: onError,
+ onDone: onDone,
+ unsubscribeOnError: unsubscribeOnError);
+ }
+
+ void close() {
+ closed = true;
+ if (_serverSocket != null && _closeServer) {
+ _serverSocket.close();
+ }
if (_sessionManagerInstance != null) {
_sessionManagerInstance.close();
_sessionManagerInstance = null;
}
- if (_server != null && _closeServer) {
- _server.close();
- }
- _server = null;
- for (_HttpConnection connection in _connections) {
- connection._destroy();
+ for (_HttpConnection connection in _connections.toList()) {
+ connection.destroy();
}
_connections.clear();
}
int get port {
- if (_server == null) {
- throw new HttpException("The HttpServer is not listening on a port.");
- }
- return _server.port;
- }
-
- void set onError(void callback(e)) {
- _onError = callback;
+ if (closed) throw new HttpException("HttpServer is not bound to a socket");
+ return _serverSocket.port;
}
set sessionTimeout(int timeout) {
_sessionManager.sessionTimeout = timeout;
}
- void _handleRequest(HttpRequest request, HttpResponse response) {
- for (int i = 0; i < _handlers.length; i++) {
- if (_handlers[i]._matcher(request)) {
- Function handler = _handlers[i]._handler;
- try {
- handler(request, response);
- } catch (e) {
- if (_onError != null) {
- _onError(e);
- } else {
- throw e;
- }
- }
- return;
- }
- }
+ void _handleRequest(HttpRequest request) {
+ _controller.add(request);
+ }
- if (_defaultHandler != null) {
- _defaultHandler(request, response);
- } else {
- response.statusCode = HttpStatus.NOT_FOUND;
- response.contentLength = 0;
- response.outputStream.close();
- }
+ void _handleError(AsyncError error) {
+ if (!closed) _controller.signalError(error);
+ }
+
+ void _connectionClosed(_HttpConnection connection) {
+ _connections.remove(connection);
}
_HttpSessionManager get _sessionManager {
@@ -1134,671 +1415,31 @@
} else if (conn._isIdle) {
result.idle++;
} else {
- assert(result._isClosing);
+ assert(conn._isClosing);
result.closing++;
}
});
return result;
}
- ServerSocket _server; // The server listen socket.
- bool _closeServer = false;
- bool _secure;
- Set<_HttpConnection> _connections; // Set of currently connected clients.
- List<_RequestHandlerRegistration> _handlers;
- Object _defaultHandler;
- Function _onError;
- _CloseQueue _closeQueue;
_HttpSessionManager _sessionManagerInstance;
+
+ // Indicated if the http server has been closed.
+ bool closed = false;
+
+ // The server listen socket.
+ final ServerSocket _serverSocket;
+ final bool _closeServer;
+
+ // Set of currently connected clients.
+ final Set<_HttpConnection> _connections = new Set<_HttpConnection>();
+ final StreamController<HttpRequest> _controller
+ = new StreamController<HttpRequest>();
+
+ // TODO(ajohnsen): Use close queue?
}
-class _HttpClientRequest
- extends _HttpRequestResponseBase implements HttpClientRequest {
- _HttpClientRequest(String this._method,
- Uri this._uri,
- _HttpClientConnection connection)
- : super(connection) {
- _headers = new _HttpHeaders();
- _connection = connection;
- // Default GET and HEAD requests to have no content.
- if (_method == "GET" || _method == "HEAD") {
- contentLength = 0;
- }
- }
-
- void set contentLength(int contentLength) {
- if (_state >= _HttpRequestResponseBase.HEADER_SENT) {
- throw new HttpException("Header already sent");
- }
- _headers.contentLength = contentLength;
- }
-
- List<Cookie> get cookies {
- if (_cookies == null) _cookies = new List<Cookie>();
- return _cookies;
- }
-
- OutputStream get outputStream {
- if (_done) throw new HttpException("Request closed");
- if (_outputStream == null) {
- _outputStream = new _HttpOutputStream(this);
- }
- return _outputStream;
- }
-
- // Delegate functions for the HttpOutputStream implementation.
- bool _streamWrite(List<int> buffer, bool copyBuffer) {
- if (_done) throw new HttpException("Request closed");
- _emptyBody = _emptyBody && buffer.length == 0;
- return _write(buffer, copyBuffer);
- }
-
- bool _streamWriteFrom(List<int> buffer, int offset, int len) {
- if (_done) throw new HttpException("Request closed");
- _emptyBody = _emptyBody && buffer.length == 0;
- return _writeList(buffer, offset, len);
- }
-
- void _streamFlush() {
- _httpConnection._flush();
- }
-
- void _streamClose() {
- _ensureHeadersSent();
- _state = _HttpRequestResponseBase.DONE;
- // Stop tracking no pending write events.
- _httpConnection._onNoPendingWrites = null;
- // Ensure that any trailing data is written.
- _writeDone();
- _connection._requestClosed();
- if (_streamClosedHandler != null) {
- Timer.run(_streamClosedHandler);
- }
- }
-
- void _streamSetNoPendingWriteHandler(callback()) {
- if (_state != _HttpRequestResponseBase.DONE) {
- _httpConnection._onNoPendingWrites = callback;
- }
- }
-
- void _streamSetClosedHandler(callback()) {
- _streamClosedHandler = callback;
- }
-
- void _streamSetErrorHandler(callback(e)) {
- _streamErrorHandler = callback;
- }
-
- void _writeHeader() {
- List<int> data;
-
- // Write request line.
- data = _method.toString().charCodes;
- _httpConnection._write(data);
- _writeSP();
- // Send the path for direct connections and the whole URL for
- // proxy connections.
- if (!_connection._usingProxy) {
- String path = _uri.path;
- if (path.length == 0) path = "/";
- if (_uri.query != "") {
- if (_uri.fragment != "") {
- path = "${path}?${_uri.query}#${_uri.fragment}";
- } else {
- path = "${path}?${_uri.query}";
- }
- }
- data = path.charCodes;
- } else {
- data = _uri.toString().charCodes;
- }
- _httpConnection._write(data);
- _writeSP();
- _httpConnection._write(_Const.HTTP11);
- _writeCRLF();
-
- // Add the cookies to the headers.
- if (_cookies != null) {
- StringBuffer sb = new StringBuffer();
- for (int i = 0; i < _cookies.length; i++) {
- if (i > 0) sb.add("; ");
- sb.add(_cookies[i].name);
- sb.add("=");
- sb.add(_cookies[i].value);
- }
- _headers.add("cookie", sb.toString());
- }
-
- // Write headers.
- _headers._finalize("1.1");
- _writeHeaders();
- _state = _HttpRequestResponseBase.HEADER_SENT;
- }
-
- String _method;
- Uri _uri;
- _HttpClientConnection _connection;
- _HttpOutputStream _outputStream;
- Function _streamClosedHandler;
- Function _streamErrorHandler;
- bool _emptyBody = true;
-}
-
-class _HttpClientResponse
- extends _HttpRequestResponseBase
- implements HttpClientResponse {
- _HttpClientResponse(_HttpClientConnection connection)
- : super(connection) {
- _connection = connection;
- }
-
- int get statusCode => _statusCode;
- String get reasonPhrase => _reasonPhrase;
-
- bool get isRedirect {
- var method = _connection._request._method;
- if (method == "GET" || method == "HEAD") {
- return statusCode == HttpStatus.MOVED_PERMANENTLY ||
- statusCode == HttpStatus.FOUND ||
- statusCode == HttpStatus.SEE_OTHER ||
- statusCode == HttpStatus.TEMPORARY_REDIRECT;
- } else if (method == "POST") {
- return statusCode == HttpStatus.SEE_OTHER;
- }
- return false;
- }
-
- List<Cookie> get cookies {
- if (_cookies != null) return _cookies;
- _cookies = new List<Cookie>();
- List<String> values = _headers["set-cookie"];
- if (values != null) {
- values.forEach((value) {
- _cookies.add(new Cookie.fromSetCookieValue(value));
- });
- }
- return _cookies;
- }
-
- InputStream get inputStream {
- if (_inputStream == null) {
- _inputStream = new _HttpInputStream(this);
- }
- return _inputStream;
- }
-
- void _onResponseReceived(int statusCode,
- String reasonPhrase,
- String version,
- _HttpHeaders headers,
- bool hasBody) {
- _statusCode = statusCode;
- _reasonPhrase = reasonPhrase;
- _headers = headers;
-
- // Prepare for receiving data.
- _buffer = new _BufferList();
- if (isRedirect && _connection.followRedirects) {
- if (_connection._redirects == null ||
- _connection._redirects.length < _connection.maxRedirects) {
- // Check the location header.
- List<String> location = headers[HttpHeaders.LOCATION];
- if (location == null || location.length > 1) {
- throw new RedirectException("Invalid redirect",
- _connection._redirects);
- }
- // Check for redirect loop
- if (_connection._redirects != null) {
- Uri redirectUrl = Uri.parse(location[0]);
- for (int i = 0; i < _connection._redirects.length; i++) {
- if (_connection._redirects[i].location.toString() ==
- redirectUrl.toString()) {
- throw new RedirectLoopException(_connection._redirects);
- }
- }
- }
- if (!persistentConnection) {
- throw new RedirectException(
- "Non-persistent connections are currently not supported for "
- "redirects", _connection._redirects);
- }
- // Drain body and redirect.
- inputStream.onData = inputStream.read;
- if (_statusCode == HttpStatus.SEE_OTHER &&
- _connection._method == "POST") {
- _connection.redirect("GET");
- } else {
- _connection.redirect();
- }
- } else {
- throw new RedirectLimitExceededException(_connection._redirects);
- }
- } else if (statusCode == HttpStatus.UNAUTHORIZED) {
- _handleUnauthorized();
- } else if (_connection._onResponse != null) {
- _connection._onResponse(this);
- }
- }
-
- void _handleUnauthorized() {
-
- void retryRequest(_Credentials cr) {
- if (cr != null) {
- // Drain body and retry.
- // TODO(sgjesse): Support digest.
- if (cr.scheme == _AuthenticationScheme.BASIC) {
- inputStream.onData = inputStream.read;
- _connection._retry();
- return;
- }
- }
-
- // Fall through to here to perform normal response handling if
- // there is no sensible authorization handling.
- if (_connection._onResponse != null) {
- _connection._onResponse(this);
- }
- }
-
- // Only try to authenticate if there is a challenge in the response.
- List<String> challenge = _headers[HttpHeaders.WWW_AUTHENTICATE];
- if (challenge != null && challenge.length == 1) {
- _HeaderValue header =
- new _HeaderValue.fromString(challenge[0], parameterSeparator: ",");
- _AuthenticationScheme scheme =
- new _AuthenticationScheme.fromString(header.value);
- String realm = header.parameters["realm"];
-
- // See if any credentials are available.
- _Credentials cr =
- _connection._client._findCredentials(
- _connection._request._uri, scheme);
-
- // Ask for more credentials if none found or the one found has
- // already been used. If it has already been used it must now be
- // invalid and is removed.
- if (cr == null || cr.used) {
- if (cr != null) {
- _connection._client._removeCredentials(cr);
- }
- cr = null;
- if (_connection._client._authenticate != null) {
- Future authComplete =
- _connection._client._authenticate(
- _connection._request._uri, scheme.toString(), realm);
- authComplete.then((credsAvailable) {
- if (credsAvailable) {
- cr = _connection._client._findCredentials(
- _connection._request._uri, scheme);
- retryRequest(cr);
- } else {
- if (_connection._onResponse != null) {
- _connection._onResponse(this);
- }
- }
- });
- return;
- }
- } else {
- // If credentials found prepare for retrying the request.
- retryRequest(cr);
- return;
- }
- }
-
- // Fall through to here to perform normal response handling if
- // there is no sensible authorization handling.
- if (_connection._onResponse != null) {
- _connection._onResponse(this);
- }
- }
-
- void _onDataReceived(List<int> data) {
- _buffer.add(data);
- if (_inputStream != null) _inputStream._dataReceived();
- }
-
- void _onDataEnd() {
- if (_inputStream != null) {
- _inputStream._closeReceived();
- } else {
- inputStream._streamMarkedClosed = true;
- }
- }
-
- // Delegate functions for the HttpInputStream implementation.
- int _streamAvailable() {
- return _buffer.length;
- }
-
- List<int> _streamRead(int bytesToRead) {
- return _buffer.readBytes(bytesToRead);
- }
-
- int _streamReadInto(List<int> buffer, int offset, int len) {
- List<int> data = _buffer.readBytes(len);
- buffer.setRange(offset, data.length, data);
- return data.length;
- }
-
- void _streamSetErrorHandler(callback(e)) {
- _streamErrorHandler = callback;
- }
-
- int _statusCode;
- String _reasonPhrase;
-
- _HttpClientConnection _connection;
- _HttpInputStream _inputStream;
- _BufferList _buffer;
-
- Function _streamErrorHandler;
-}
-
-
-class _HttpClientConnection
- extends _HttpConnectionBase implements HttpClientConnection {
-
- _HttpClientConnection(_HttpClient this._client) {
- _httpParser = new _HttpParser.responseParser();
- }
-
- bool _write(List<int> data, [bool copyBuffer = false]) {
- return _socket.outputStream.write(data, copyBuffer);
- }
-
- bool _writeFrom(List<int> data, [int offset, int len]) {
- return _socket.outputStream.writeFrom(data, offset, len);
- }
-
- bool _flush() {
- _socket.outputStream.flush();
- }
-
- bool _close() {
- _socket.outputStream.close();
- }
-
- bool _destroy() {
- _socket.close();
- }
-
- DetachedSocket _detachSocket() {
- _socket.onData = null;
- _socket.onClosed = null;
- _socket.onError = null;
- _socket.outputStream.onNoPendingWrites = null;
- Socket socket = _socket;
- _socket = null;
- if (onDetach != null) onDetach();
- return new _DetachedSocket(socket, _httpParser.readUnparsedData());
- }
-
- void _connectionEstablished(_SocketConnection socketConn) {
- super._connectionEstablished(socketConn._socket);
- _socketConn = socketConn;
- // Register HTTP parser callbacks.
- _httpParser.responseStart = _onResponseReceived;
- _httpParser.dataReceived = _onDataReceived;
- _httpParser.dataEnd = _onDataEnd;
- _httpParser.error = _onError;
- _httpParser.closed = _onClosed;
- _httpParser.requestStart = (method, uri, version) { assert(false); };
- _state = _HttpConnectionBase.ACTIVE;
- }
-
- void _checkSocketDone() {
- if (_isAllDone) {
- // If we are done writing the response, and either the server
- // has closed or the connection is not persistent, we must
- // close.
- if (_isReadClosed || !_response.persistentConnection) {
- this.onClosed = () {
- _client._closedSocketConnection(_socketConn);
- };
- _client._closeQueue.add(this);
- } else if (_socket != null) {
- _client._returnSocketConnection(_socketConn);
- _socket = null;
- _socketConn = null;
- assert(_pendingRedirect == null || _pendingRetry == null);
- if (_pendingRedirect != null) {
- _doRedirect(_pendingRedirect);
- _pendingRedirect = null;
- } else if (_pendingRetry != null) {
- _doRetry(_pendingRetry);
- _pendingRetry = null;
- }
- }
- }
- }
-
- void _requestClosed() {
- _state |= _HttpConnectionBase.REQUEST_DONE;
- _checkSocketDone();
- }
-
- HttpClientRequest open(String method, Uri uri) {
- _method = method;
- // Tell the HTTP parser the method it is expecting a response to.
- _httpParser.responseToMethod = method;
- // If the connection already have a request this is a retry of a
- // request. In this case the request object is reused to ensure
- // that the same headers are send.
- if (_request != null) {
- _request._method = method;
- _request._uri = uri;
- _request._headers._mutable = true;
- _request._state = _HttpRequestResponseBase.START;
- } else {
- _request = new _HttpClientRequest(method, uri, this);
- }
- _response = new _HttpClientResponse(this);
- return _request;
- }
-
- DetachedSocket detachSocket() {
- return _detachSocket();
- }
-
- void _onClosed() {
- _state |= _HttpConnectionBase.READ_CLOSED;
- _checkSocketDone();
- }
-
- void _onError(e) {
- // Cancel any pending data in the HTTP parser.
- _httpParser.cancel();
- if (_socketConn != null) {
- _client._closeSocketConnection(_socketConn);
- }
-
- // If it looks as if we got a bad connection from the connection
- // pool and the request can be retried do a retry.
- if (_socketConn != null && _socketConn._fromPool && _request._emptyBody) {
- String method = _request._method;
- Uri uri = _request._uri;
- _socketConn = null;
-
- // Retry the URL using the same connection instance.
- _httpParser.restart();
- _client._openUrl(method, uri, this);
- } else {
- // Report the error.
- if (_response != null && _response._streamErrorHandler != null) {
- _response._streamErrorHandler(e);
- } else if (_onErrorCallback != null) {
- _onErrorCallback(e);
- } else {
- throw e;
- }
- }
- }
-
- void _onResponseReceived(int statusCode,
- String reasonPhrase,
- String version,
- _HttpHeaders headers,
- bool hasBody) {
- _response._onResponseReceived(
- statusCode, reasonPhrase, version, headers, hasBody);
- }
-
- void _onDataReceived(List<int> data) {
- _response._onDataReceived(data);
- }
-
- void _onDataEnd(bool close) {
- _state |= _HttpConnectionBase.RESPONSE_DONE;
- _response._onDataEnd();
- _checkSocketDone();
- }
-
- void _onClientShutdown() {
- if (!_isResponseDone) {
- _onError(new HttpException("Client shutdown"));
- }
- }
-
- void set onRequest(void handler(HttpClientRequest request)) {
- _onRequest = handler;
- }
-
- void set onResponse(void handler(HttpClientResponse response)) {
- _onResponse = handler;
- }
-
- void set onError(void callback(e)) {
- _onErrorCallback = callback;
- }
-
- void _doRetry(_RedirectInfo retry) {
- assert(_socketConn == null);
-
- // Retry the URL using the same connection instance.
- _state = _HttpConnectionBase.IDLE;
- _client._openUrl(retry.method, retry.location, this);
- }
-
- void _retry() {
- var retry = new _RedirectInfo(_response.statusCode, _method, _request._uri);
- // The actual retry is postponed until both response and request
- // are done.
- if (_isAllDone) {
- _doRetry(retry);
- } else {
- // Prepare for retry.
- assert(_pendingRedirect == null);
- _pendingRetry = retry;
- }
- }
-
- void _doRedirect(_RedirectInfo redirect) {
- assert(_socketConn == null);
-
- if (_redirects == null) {
- _redirects = new List<_RedirectInfo>();
- }
- _redirects.add(redirect);
- _doRetry(redirect);
- }
-
- void redirect([String method, Uri url]) {
- if (method == null) method = _method;
- if (url == null) {
- url = Uri.parse(_response.headers.value(HttpHeaders.LOCATION));
- }
- // Always set the content length to 0 for redirects.
- var mutable = _request._headers._mutable;
- _request._headers._mutable = true;
- _request._headers.contentLength = 0;
- _request._headers._mutable = mutable;
- _request._bodyBytesWritten = 0;
- var redirect = new _RedirectInfo(_response.statusCode, method, url);
- // The actual redirect is postponed until both response and
- // request are done.
- assert(_pendingRetry == null);
- _pendingRedirect = redirect;
- }
-
- List<RedirectInfo> get redirects => _redirects;
-
- Function _onRequest;
- Function _onResponse;
- Function _onErrorCallback;
-
- _HttpClient _client;
- _SocketConnection _socketConn;
- HttpClientRequest _request;
- HttpClientResponse _response;
- String _method;
- bool _usingProxy;
-
- // Redirect handling
- bool followRedirects = true;
- int maxRedirects = 5;
- List<_RedirectInfo> _redirects;
- _RedirectInfo _pendingRedirect;
- _RedirectInfo _pendingRetry;
-
- // Callbacks.
- var requestReceived;
-}
-
-
-// Class for holding keep-alive sockets in the cache for the HTTP
-// client together with the connection information.
-class _SocketConnection {
- _SocketConnection(String this._host,
- int this._port,
- Socket this._socket);
-
- void _markReturned() {
- // Any activity on the socket while waiting in the pool will
- // invalidate the connection os that it is not reused.
- _socket.onData = _invalidate;
- _socket.onClosed = _invalidate;
- _socket.onError = (_) => _invalidate();
- _returnTime = new DateTime.now();
- _httpClientConnection = null;
- }
-
- void _markRetrieved() {
- _socket.onData = null;
- _socket.onClosed = null;
- _socket.onError = null;
- _httpClientConnection = null;
- }
-
- void _close() {
- _socket.onData = null;
- _socket.onClosed = null;
- _socket.onError = null;
- _httpClientConnection = null;
- _socket.close();
- }
-
- Duration _idleTime(DateTime now) => now.difference(_returnTime);
-
- bool get _fromPool => _returnTime != null;
-
- void _invalidate() {
- _valid = false;
- _close();
- }
-
- int get hashCode => _socket.hashCode;
-
- String _host;
- int _port;
- Socket _socket;
- DateTime _returnTime;
- bool _valid = true;
- HttpClientConnection _httpClientConnection;
-}
-
class _ProxyConfiguration {
static const String PROXY_PREFIX = "PROXY ";
static const String DIRECT_PREFIX = "DIRECT";
@@ -1844,6 +1485,7 @@
final List<_Proxy> proxies;
}
+
class _Proxy {
const _Proxy(this.host, this.port) : isDirect = false;
const _Proxy.direct() : host = null, port = null, isDirect = true;
@@ -1853,376 +1495,59 @@
final bool isDirect;
}
-class _HttpClient implements HttpClient {
- static const int DEFAULT_EVICTION_TIMEOUT = 60000;
-
- _HttpClient() : _openSockets = new Map(),
- _activeSockets = new Set(),
- _closeQueue = new _CloseQueue(),
- credentials = new List<_Credentials>(),
- _shutdown = false;
-
- HttpClientConnection open(
- String method, String host, int port, String path) {
- // TODO(sgjesse): The path set here can contain both query and
- // fragment. They should be cracked and set correctly.
- return _open(method, new Uri.fromComponents(
- scheme: "http", domain: host, port: port, path: path));
- }
-
- HttpClientConnection _open(String method,
- Uri uri,
- [_HttpClientConnection connection]) {
- if (_shutdown) throw new HttpException("HttpClient shutdown");
- if (method == null || uri.domain.isEmpty) {
- throw new ArgumentError(null);
- }
- return _prepareHttpClientConnection(method, uri, connection);
- }
-
- HttpClientConnection openUrl(String method, Uri url) {
- return _openUrl(method, url);
- }
-
- HttpClientConnection _openUrl(String method,
- Uri url,
- [_HttpClientConnection connection]) {
- if (url.scheme != "http" && url.scheme != "https") {
- throw new HttpException("Unsupported URL scheme ${url.scheme}");
- }
- return _open(method, url, connection);
- }
-
- HttpClientConnection get(String host, int port, String path) {
- return open("GET", host, port, path);
- }
-
- HttpClientConnection getUrl(Uri url) => _openUrl("GET", url);
-
- HttpClientConnection post(String host, int port, String path) {
- return open("POST", host, port, path);
- }
-
- HttpClientConnection postUrl(Uri url) => _openUrl("POST", url);
-
- set authenticate(Future<bool> f(Uri url, String scheme, String realm)) {
- _authenticate = f;
- }
-
- void addCredentials(
- Uri url, String realm, HttpClientCredentials cr) {
- credentials.add(new _Credentials(url, realm, cr));
- }
-
- set sendClientCertificate(bool send) => _sendClientCertificate = send;
-
- set clientCertificate(String nickname) => _clientCertificate = nickname;
-
- set findProxy(String f(Uri uri)) => _findProxy = f;
-
- void shutdown({bool force: false}) {
- if (force) _closeQueue.shutdown();
- new Map.from(_openSockets).forEach(
- (String key, Queue<_SocketConnection> connections) {
- while (!connections.isEmpty) {
- _SocketConnection socketConn = connections.removeFirst();
- socketConn._socket.close();
- }
- });
- if (force) {
- _activeSockets.toList().forEach((_SocketConnection socketConn) {
- socketConn._httpClientConnection._onClientShutdown();
- socketConn._close();
- });
- }
- if (_evictionTimer != null) _cancelEvictionTimer();
- _shutdown = true;
- }
-
- void _cancelEvictionTimer() {
- _evictionTimer.cancel();
- _evictionTimer = null;
- }
-
- String _connectionKey(String host, int port) {
- return "$host:$port";
- }
-
- HttpClientConnection _prepareHttpClientConnection(
- String method,
- Uri url,
- [_HttpClientConnection connection]) {
-
- void _establishConnection(String host,
- int port,
- _ProxyConfiguration proxyConfiguration,
- int proxyIndex,
- bool reusedConnection,
- bool secure) {
-
- void _connectionOpened(_SocketConnection socketConn,
- _HttpClientConnection connection,
- bool usingProxy) {
- socketConn._httpClientConnection = connection;
- connection._usingProxy = usingProxy;
- connection._connectionEstablished(socketConn);
- HttpClientRequest request = connection.open(method, url);
- request.headers.host = host;
- request.headers.port = port;
- if (url.userInfo != null && !url.userInfo.isEmpty) {
- // If the URL contains user information use that for basic
- // authorization
- _UTF8Encoder encoder = new _UTF8Encoder();
- String auth =
- CryptoUtils.bytesToBase64(encoder.encodeString(url.userInfo));
- request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth");
- } else {
- // Look for credentials.
- _Credentials cr = _findCredentials(url);
- if (cr != null) {
- cr.authorize(request);
- }
- }
- // A reused connection is indicating either redirect or retry
- // where the onRequest callback should not be issued again.
- if (connection._onRequest != null && !reusedConnection) {
- connection._onRequest(request);
- } else {
- request.outputStream.close();
- }
- }
-
- assert(proxyIndex < proxyConfiguration.proxies.length);
-
- // Determine the actual host to connect to.
- String connectHost;
- int connectPort;
- _Proxy proxy = proxyConfiguration.proxies[proxyIndex];
- if (proxy.isDirect) {
- connectHost = host;
- connectPort = port;
- } else {
- connectHost = proxy.host;
- connectPort = proxy.port;
- }
-
- // If there are active connections for this key get the first one
- // otherwise create a new one.
- String key = _connectionKey(connectHost, connectPort);
- Queue socketConnections = _openSockets[key];
- // Remove active connections that are not valid any more or of
- // the wrong type (HTTP or HTTPS).
- if (socketConnections != null) {
- while (!socketConnections.isEmpty) {
- if (socketConnections.first._valid) {
- // If socket has the same properties, exit loop with found socket.
- var socket = socketConnections.first._socket;
- if (!secure && socket is! SecureSocket) break;
- if (secure && socket is SecureSocket &&
- _sendClientCertificate == socket.sendClientCertificate &&
- _clientCertificate == socket.certificateName) break;
- }
- socketConnections.removeFirst()._close();
- }
- }
- if (socketConnections == null || socketConnections.isEmpty) {
- Socket socket = secure ?
- new SecureSocket(connectHost,
- connectPort,
- sendClientCertificate: _sendClientCertificate,
- certificateName: _clientCertificate) :
- new Socket(connectHost, connectPort);
- // Until the connection is established handle connection errors
- // here as the HttpClientConnection object is not yet associated
- // with the socket.
- socket.onError = (e) {
- proxyIndex++;
- if (proxyIndex < proxyConfiguration.proxies.length) {
- // Try the next proxy in the list.
- _establishConnection(
- host, port, proxyConfiguration, proxyIndex, false, secure);
- } else {
- // Report the error through the HttpClientConnection object to
- // the client.
- connection._onError(e);
- }
- };
- socket.onConnect = () {
- // When the connection is established, clear the error
- // callback as it will now be handled by the
- // HttpClientConnection object which will be associated with
- // the connected socket.
- socket.onError = null;
- _SocketConnection socketConn =
- new _SocketConnection(connectHost, connectPort, socket);
- _activeSockets.add(socketConn);
- _connectionOpened(socketConn, connection, !proxy.isDirect);
- };
- } else {
- _SocketConnection socketConn = socketConnections.removeFirst();
- socketConn._markRetrieved();
- _activeSockets.add(socketConn);
- Timer.run(() =>
- _connectionOpened(socketConn, connection, !proxy.isDirect));
-
- // Get rid of eviction timer if there are no more active connections.
- if (socketConnections.isEmpty) _openSockets.remove(key);
- if (_openSockets.isEmpty) _cancelEvictionTimer();
- }
- }
-
- // Find out if we want a secure socket.
- bool is_secure = (url.scheme == "https");
-
- // Find the TCP host and port.
- String host = url.domain;
- int port = url.port;
- if (port == 0) {
- port = is_secure ?
- HttpClient.DEFAULT_HTTPS_PORT :
- HttpClient.DEFAULT_HTTP_PORT;
- }
- // Create a new connection object if we are not re-using an existing one.
- var reusedConnection = false;
- if (connection == null) {
- connection = new _HttpClientConnection(this);
- } else {
- reusedConnection = true;
- }
- connection.onDetach = () => _activeSockets.remove(connection._socketConn);
-
- // Check to see if a proxy server should be used for this connection.
- _ProxyConfiguration proxyConfiguration = const _ProxyConfiguration.direct();
- if (_findProxy != null) {
- // TODO(sgjesse): Keep a map of these as normally only a few
- // configuration strings will be used.
- proxyConfiguration = new _ProxyConfiguration(_findProxy(url));
- }
-
- // Establish the connection starting with the first proxy configured.
- _establishConnection(host,
- port,
- proxyConfiguration,
- 0,
- reusedConnection,
- is_secure);
-
- return connection;
- }
-
- void _returnSocketConnection(_SocketConnection socketConn) {
- // If the HTTP client is being shutdown don't return the connection.
- if (_shutdown) {
- socketConn._close();
- return;
- };
-
- // Mark socket as returned to unregister from the old connection.
- socketConn._markReturned();
-
- String key = _connectionKey(socketConn._host, socketConn._port);
-
- // Get or create the connection list for this key.
- Queue sockets = _openSockets[key];
- if (sockets == null) {
- sockets = new Queue();
- _openSockets[key] = sockets;
- }
-
- // If there is currently no eviction timer start one.
- if (_evictionTimer == null) {
- void _handleEviction(Timer timer) {
- DateTime now = new DateTime.now();
- List<String> emptyKeys = new List<String>();
- _openSockets.forEach(
- (String key, Queue<_SocketConnection> connections) {
- // As returned connections are added at the head of the
- // list remove from the tail.
- while (!connections.isEmpty) {
- _SocketConnection socketConn = connections.last;
- if (socketConn._idleTime(now).inMilliseconds >
- DEFAULT_EVICTION_TIMEOUT) {
- connections.removeLast();
- socketConn._socket.close();
- if (connections.isEmpty) emptyKeys.add(key);
- } else {
- break;
- }
- }
- });
-
- // Remove the keys for which here are no more open connections.
- emptyKeys.forEach((String key) => _openSockets.remove(key));
-
- // If all connections where evicted cancel the eviction timer.
- if (_openSockets.isEmpty) _cancelEvictionTimer();
- }
- _evictionTimer = new Timer.repeating(const Duration(seconds: 10),
- _handleEviction);
- }
-
- // Return connection.
- _activeSockets.remove(socketConn);
- sockets.addFirst(socketConn);
- }
-
- void _closeSocketConnection(_SocketConnection socketConn) {
- socketConn._close();
- _activeSockets.remove(socketConn);
- }
-
- void _closedSocketConnection(_SocketConnection socketConn) {
- _activeSockets.remove(socketConn);
- }
-
- _Credentials _findCredentials(Uri url, [_AuthenticationScheme scheme]) {
- // Look for credentials.
- _Credentials cr =
- credentials.reduce(null, (_Credentials prev, _Credentials value) {
- if (value.applies(url, scheme)) {
- if (prev == null) return value;
- return value.uri.path.length > prev.uri.path.length ? value : prev;
- } else {
- return prev;
- }
- });
- return cr;
- }
-
- void _removeCredentials(_Credentials cr) {
- int index = credentials.indexOf(cr);
- if (index != -1) {
- credentials.removeAt(index);
- }
- }
-
- Function _onOpen;
- Map<String, Queue<_SocketConnection>> _openSockets;
- Set<_SocketConnection> _activeSockets;
- _CloseQueue _closeQueue;
- List<_Credentials> credentials;
- Timer _evictionTimer;
- Function _findProxy;
- Function _authenticate;
- bool _sendClientCertificate = false;
- String _clientCertificate;
- bool _shutdown; // Has this HTTP client been shutdown?
-}
-
class _HttpConnectionInfo implements HttpConnectionInfo {
+ static _HttpConnectionInfo create(Socket socket) {
+ if (socket == null) return null;
+ try {
+ _HttpConnectionInfo info = new _HttpConnectionInfo._();
+ info.remoteHost = socket.remoteHost;
+ info.remotePort = socket.remotePort;
+ info.localPort = socket.port;
+ return info;
+ } catch (e) { }
+ return null;
+ }
+
+ _HttpConnectionInfo._();
+
String remoteHost;
int remotePort;
int localPort;
}
-class _DetachedSocket implements DetachedSocket {
- _DetachedSocket(this._socket, this._unparsedData);
- Socket get socket => _socket;
- List<int> get unparsedData => _unparsedData;
- Socket _socket;
- List<int> _unparsedData;
+class _DetachedSocket implements Socket {
+ final Stream<List<int>> _incoming;
+ final Socket _socket;
+
+ _DetachedSocket(this._socket, this._incoming);
+
+ StreamSubscription<List<int>> listen(void onData(List<int> event),
+ {void onError(AsyncError error),
+ void onDone(),
+ bool unsubscribeOnError}) {
+ return _incoming.listen(onData,
+ onError: onError,
+ onDone: onDone,
+ unsubscribeOnError: unsubscribeOnError);
+ }
+
+ Future<Socket> consume(Stream<List<int>> stream) {
+ return _socket.consume(stream);
+ }
+
+ Future<Socket> addStream(Stream<List<int>> stream) {
+ return _socket.addStream(stream);
+ }
+
+ void addString(String string, [Encoding encoding = Encoding.UTF_8]) {
+ return _socket.addString(string, encoding);
+ }
+
+ void destroy() => _socket.destroy();
+ void add(List<int> data) => _socket.add(data);
+ Future<Socket> close() => _socket.close();
}
@@ -2272,7 +1597,7 @@
bool used = false;
Uri uri;
String realm;
- HttpClientCredentials credentials;
+ _HttpClientCredentials credentials;
// Digest specific fields.
String nonce;
@@ -2283,11 +1608,13 @@
abstract class _HttpClientCredentials implements HttpClientCredentials {
_AuthenticationScheme get scheme;
- void authorize(HttpClientRequest request);
+ void authorize(_Credentials credentials, HttpClientRequest request);
}
-class _HttpClientBasicCredentials implements HttpClientBasicCredentials {
+class _HttpClientBasicCredentials
+ extends _HttpClientCredentials
+ implements HttpClientBasicCredentials {
_HttpClientBasicCredentials(this.username,
this.password);
@@ -2300,10 +1627,8 @@
// Proxy-Authenticate headers, see
// http://tools.ietf.org/html/draft-reschke-basicauth-enc-06. For
// now always use UTF-8 encoding.
- _UTF8Encoder encoder = new _UTF8Encoder();
String auth =
- CryptoUtils.bytesToBase64(encoder.encodeString(
- "$username:$password"));
+ CryptoUtils.bytesToBase64(_encodeString("$username:$password"));
request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth");
}
@@ -2312,7 +1637,9 @@
}
-class _HttpClientDigestCredentials implements HttpClientDigestCredentials {
+class _HttpClientDigestCredentials
+ extends _HttpClientCredentials
+ implements HttpClientDigestCredentials {
_HttpClientDigestCredentials(this.username,
this.password);
@@ -2328,7 +1655,6 @@
}
-
class _RedirectInfo implements RedirectInfo {
const _RedirectInfo(int this.statusCode,
String this.method,
diff --git a/sdk/lib/io/http_parser.dart b/sdk/lib/io/http_parser.dart
index c0a2819..7dc258e 100644
--- a/sdk/lib/io/http_parser.dart
+++ b/sdk/lib/io/http_parser.dart
@@ -74,11 +74,9 @@
static const int BODY = 24;
static const int CLOSED = 25;
static const int UPGRADED = 26;
- static const int CANCELED = 27;
- static const int FAILURE = 28;
+ static const int FAILURE = 27;
static const int FIRST_BODY_STATE = CHUNK_SIZE_STARTING_CR;
- static const int FIRST_PARSE_STOP_STATE = CLOSED;
}
// HTTP version of the request or response being parsed.
@@ -95,22 +93,87 @@
static const int RESPONSE = 0;
}
+class _HttpDetachedIncoming extends Stream<List<int>> {
+ StreamController<List<int>> controller;
+ final StreamSubscription subscription;
+
+ List<int> carryOverData;
+ bool paused;
+
+ Completer resumeCompleter;
+
+ _HttpDetachedIncoming(StreamSubscription this.subscription,
+ List<int> this.carryOverData,
+ Completer oldResumeCompleter) {
+ controller = new StreamController<List<int>>(
+ onSubscriptionStateChange: onSubscriptionStateChange,
+ onPauseStateChange: onPauseStateChange);
+ pause();
+ if (oldResumeCompleter != null) oldResumeCompleter.complete();
+ subscription.resume();
+ subscription.onData(controller.add);
+ subscription.onDone(controller.close);
+ subscription.onError(controller.signalError);
+ }
+
+ StreamSubscription<List<int>> listen(void onData(List<int> event),
+ {void onError(AsyncError error),
+ void onDone(),
+ bool unsubscribeOnError}) {
+ return controller.stream.listen(
+ onData,
+ onError: onError,
+ onDone: onDone,
+ unsubscribeOnError: unsubscribeOnError);
+ }
+
+ void resume() {
+ paused = false;
+ if (carryOverData != null) {
+ var data = carryOverData;
+ carryOverData = null;
+ controller.add(data);
+ // If the consumer pauses again after the carry-over data, we'll not
+ // continue our subscriber until the next resume.
+ if (paused) return;
+ }
+ if (resumeCompleter != null) {
+ resumeCompleter.complete();
+ resumeCompleter = null;
+ }
+ }
+
+ void pause() {
+ paused = true;
+ if (resumeCompleter == null) {
+ resumeCompleter = new Completer();
+ subscription.pause(resumeCompleter.future);
+ }
+ }
+
+ void onPauseStateChange() {
+ if (controller.isPaused) {
+ pause();
+ } else {
+ resume();
+ }
+ }
+
+ void onSubscriptionStateChange() {
+ if (controller.hasSubscribers) {
+ resume();
+ } else {
+ subscription.cancel();
+ }
+ }
+}
+
/**
- * HTTP parser which parses the HTTP stream as data is supplied
- * through the [:streamData:] and [:streamDone:] methods. As the
- * data is parsed the following callbacks are called:
+ * HTTP parser which parses the data stream given to [consume].
*
- * [:requestStart:]
- * [:responseStart:]
- * [:dataReceived:]
- * [:dataEnd:]
- * [:closed:]
- * [:error:]
- *
- * If an HTTP parser error occours it is possible to get an exception
- * thrown from the [:streamData:] and [:streamDone:] methods if
- * the error callback is not set.
+ * If an HTTP parser error occours, the parser will signal an error to either
+ * the current _HttpIncoming or the _parser itself.
*
* The connection upgrades (e.g. switching from HTTP/1.1 to the
* WebSocket protocol) is handled in a special way. If connection
@@ -126,14 +189,51 @@
* and should be handled according to whatever protocol is being
* upgraded to.
*/
-class _HttpParser {
- _HttpParser.requestParser() {
- _requestParser = true;
+class _HttpParser
+ extends Stream<_HttpIncoming>
+ implements StreamConsumer<List<int>, _HttpParser> {
+
+ factory _HttpParser.requestParser() {
+ return new _HttpParser._(true);
+ }
+
+ factory _HttpParser.responseParser() {
+ return new _HttpParser._(false);
+ }
+
+ _HttpParser._(this._requestParser) {
+ _controller = new StreamController<_HttpIncoming>(
+ onSubscriptionStateChange: _updateParsePauseState,
+ onPauseStateChange: _updateParsePauseState);
_reset();
}
- _HttpParser.responseParser() {
- _requestParser = false;
- _reset();
+
+
+ StreamSubscription<_HttpIncoming> listen(void onData(List<int> event),
+ {void onError(AsyncError error),
+ void onDone(),
+ bool unsubscribeOnError}) {
+ return _controller.stream.listen(onData,
+ onError: onError,
+ onDone: onDone,
+ unsubscribeOnError: unsubscribeOnError);
+ }
+
+ Future<_HttpParser> consume(Stream<List<int>> stream) {
+ // Listen to the stream and handle data accordingly. When a
+ // _HttpIncoming is created, _dataPause, _dataResume, _dataDone is
+ // given to provide a way of controlling the parser.
+ // TODO(ajohnsen): Remove _dataPause, _dataResume and _dataDone and clean up
+ // how the _HttpIncoming signals the parser.
+ var completer = new Completer();
+ _socketSubscription = stream.listen(
+ _onData,
+ onError: _onError,
+ onDone: () {
+ _onDone();
+ completer.complete(this);
+ });
+ return completer.future;
}
// From RFC 2616.
@@ -146,22 +246,23 @@
// Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
// message-header = field-name ":" [ field-value ]
void _parse() {
+ assert(!_parserCalled);
+ _parserCalled = true;
try {
if (_state == _State.CLOSED) {
throw new HttpParserException("Data on closed connection");
}
- if (_state == _State.UPGRADED) {
- throw new HttpParserException("Data on upgraded connection");
- }
if (_state == _State.FAILURE) {
throw new HttpParserException("Data on failed connection");
}
- if (_state == _State.CANCELED) {
- throw new HttpParserException("Data on canceled connection");
- }
while (_buffer != null &&
- _index < _lastIndex &&
- _state <= _State.FIRST_PARSE_STOP_STATE) {
+ _index < _buffer.length &&
+ _state != _State.FAILURE &&
+ _state != _State.UPGRADED) {
+ if (_paused) {
+ _parserCalled = false;
+ return;
+ }
int byte = _buffer[_index++];
switch (_state) {
case _State.START:
@@ -328,15 +429,20 @@
case _State.RESPONSE_LINE_ENDING:
_expect(byte, _CharCode.LF);
_messageType == _MessageType.RESPONSE;
- _statusCode = int.parse(
- new String.fromCharCodes(_method_or_status_code));
+ _statusCode = int.parse(
+ new String.fromCharCodes(_method_or_status_code));
if (_statusCode < 100 || _statusCode > 599) {
throw new HttpParserException("Invalid response status code");
+ } else {
+ // Check whether this response will never have a body.
+ _noMessageBody = _statusCode <= 199 || _statusCode == 204 ||
+ _statusCode == 304;
}
_state = _State.HEADER_START;
break;
case _State.HEADER_START:
+ _headers = new _HttpHeaders(version);
if (byte == _CharCode.CR) {
_state = _State.HEADER_ENDING;
} else {
@@ -386,27 +492,19 @@
} else {
String headerField = new String.fromCharCodes(_headerField);
String headerValue = new String.fromCharCodes(_headerValue);
- bool reportHeader = true;
- if (headerField == "connection") {
- List<String> tokens = _tokenizeFieldValue(headerValue);
- for (int i = 0; i < tokens.length; i++) {
- String token = tokens[i].toLowerCase();
- if (token == "keep-alive") {
- _persistentConnection = true;
- } else if (token == "close") {
- _persistentConnection = false;
- } else if (token == "upgrade") {
- _connectionUpgrade = true;
- }
- _headers.add(headerField, token);
-
- }
- reportHeader = false;
- } else if (headerField == "transfer-encoding" &&
+ if (headerField == "transfer-encoding" &&
headerValue.toLowerCase() == "chunked") {
_chunked = true;
}
- if (reportHeader) {
+ if (headerField == "connection") {
+ List<String> tokens = _tokenizeFieldValue(headerValue);
+ for (int i = 0; i < tokens.length; i++) {
+ if (tokens[i].toLowerCase() == "upgrade") {
+ _connectionUpgrade = true;
+ }
+ _headers.add(headerField, tokens[i]);
+ }
+ } else {
_headers.add(headerField, headerValue);
}
_headerField.clear();
@@ -426,62 +524,62 @@
_expect(byte, _CharCode.LF);
_headers._mutable = false;
- _contentLength = _headers.contentLength;
+ _transferLength = _headers.contentLength;
// Ignore the Content-Length header if Transfer-Encoding
// is chunked (RFC 2616 section 4.4)
- if (_chunked) _contentLength = -1;
+ if (_chunked) _transferLength = -1;
// If a request message has neither Content-Length nor
// Transfer-Encoding the message must not have a body (RFC
// 2616 section 4.3).
if (_messageType == _MessageType.REQUEST &&
- _contentLength < 0 &&
+ _transferLength < 0 &&
_chunked == false) {
- _contentLength = 0;
+ _transferLength = 0;
}
if (_connectionUpgrade) {
_state = _State.UPGRADED;
+ _transferLength = 0;
}
- var noBody;
+ _createIncoming(_transferLength);
if (_requestParser) {
- noBody = _contentLength == 0;
- requestStart(new String.fromCharCodes(_method_or_status_code),
- new String.fromCharCodes(_uri_or_reason_phrase),
- version,
- _headers,
- !noBody);
+ _incoming.method =
+ new String.fromCharCodes(_method_or_status_code);
+ _incoming.uri =
+ Uri.parse(
+ new String.fromCharCodes(_uri_or_reason_phrase));
} else {
- // Check whether this response will never have a body.
- noBody = _contentLength == 0 ||
- _statusCode <= 199 ||
- _statusCode == HttpStatus.NO_CONTENT ||
- _statusCode == HttpStatus.NOT_MODIFIED ||
- _responseToMethod == "HEAD";
- responseStart(_statusCode,
- new String.fromCharCodes(_uri_or_reason_phrase),
- version,
- _headers,
- !noBody);
+ _incoming.statusCode = _statusCode;
+ _incoming.reasonPhrase =
+ new String.fromCharCodes(_uri_or_reason_phrase);
}
_method_or_status_code.clear();
_uri_or_reason_phrase.clear();
- if (_state == _State.CANCELED) continue;
- if (!_connectionUpgrade) {
- if (noBody) {
- _bodyEnd();
- _reset();
- } else if (_chunked) {
- _state = _State.CHUNK_SIZE;
- _remainingContent = 0;
- } else if (_contentLength > 0) {
- _remainingContent = _contentLength;
- _state = _State.BODY;
- } else {
- // Neither chunked nor content length. End of body
- // indicated by close.
- _state = _State.BODY;
- }
+ if (_connectionUpgrade) {
+ _incoming.upgraded = true;
+ _controller.add(_incoming);
+ break;
}
+ if (_chunked) {
+ _state = _State.CHUNK_SIZE;
+ _remainingContent = 0;
+ } else if (_transferLength == 0 ||
+ (_messageType == _MessageType.RESPONSE &&
+ (_noMessageBody || _responseToMethod == "HEAD"))) {
+ _reset();
+ var tmp = _incoming;
+ _closeIncoming();
+ _controller.add(tmp);
+ break;
+ } else if (_transferLength > 0) {
+ _remainingContent = _transferLength;
+ _state = _State.BODY;
+ } else {
+ // Neither chunked nor content length. End of body
+ // indicated by close.
+ _state = _State.BODY;
+ }
+ _controller.add(_incoming);
break;
case _State.CHUNK_SIZE_STARTING_CR:
@@ -527,15 +625,14 @@
case _State.CHUNKED_BODY_DONE_LF:
_expect(byte, _CharCode.LF);
- _bodyEnd();
- if (_state == _State.CANCELED) continue;
_reset();
+ _closeIncoming();
break;
case _State.BODY:
// The body is not handled one byte at a time but in blocks.
_index--;
- int dataAvailable = _lastIndex - _index;
+ int dataAvailable = _buffer.length - _index;
List<int> data;
if (_remainingContent == null ||
dataAvailable <= _remainingContent) {
@@ -546,17 +643,15 @@
data.setRange(0, _remainingContent, _buffer, _index);
}
- dataReceived(data);
- if (_state == _State.CANCELED) continue;
+ _bodyController.add(data);
if (_remainingContent != null) {
_remainingContent -= data.length;
}
_index += data.length;
if (_remainingContent == 0) {
if (!_chunked) {
- _bodyEnd();
- if (_state == _State.CANCELED) continue;
_reset();
+ _closeIncoming();
} else {
_state = _State.CHUNK_SIZE_STARTING_CR;
}
@@ -574,36 +669,62 @@
break;
}
}
- } catch (e) {
+ } catch (e, s) {
_state = _State.FAILURE;
- error(e);
+ error(new AsyncError(e, s));
}
- // If all data is parsed or not needed due to failure there is no
- // need to hold on to the buffer.
- if (_state != _State.UPGRADED) _releaseBuffer();
+ _parserCalled = false;
+ if (_buffer != null && _index == _buffer.length) {
+ // If all data is parsed release the buffer and resume receiving
+ // data.
+ _releaseBuffer();
+ if (_state != _State.UPGRADED && _state != _State.FAILURE) {
+ _socketSubscription.resume();
+ }
+ }
}
- void streamData(List<int> buffer) {
+ void _onData(List<int> buffer) {
+ _socketSubscription.pause();
assert(_buffer == null);
_buffer = buffer;
_index = 0;
- _lastIndex = buffer.length;
_parse();
}
- void streamDone() {
- String type() => _requestParser ? "request" : "response";
+ void _onDone() {
+ // onDone cancles the subscription.
+ _socketSubscription = null;
+ if (_state == _State.CLOSED || _state == _State.FAILURE) return;
+ if (_incoming != null) {
+ if (_state != _State.UPGRADED &&
+ !(_state == _State.START && !_requestParser) &&
+ !(_state == _State.BODY && !_chunked && _transferLength == -1)) {
+ _bodyController.signalError(
+ new AsyncError(
+ new HttpParserException(
+ "Connection closed while receiving data")));
+ }
+ _closeIncoming();
+ _controller.close();
+ return;
+ }
// If the connection is idle the HTTP stream is closed.
if (_state == _State.START) {
- if (_requestParser) {
- closed();
- } else {
+ if (!_requestParser) {
error(
- new HttpParserException(
- "Connection closed before full ${type()} header was received"));
+ new AsyncError(
+ new HttpParserException(
+ "Connection closed before full header was received")));
}
+ _controller.close();
+ return;
+ }
+
+ if (_state == _State.UPGRADED) {
+ _controller.close();
return;
}
@@ -612,27 +733,29 @@
// Report the error through the error callback if any. Otherwise
// throw the error.
error(
- new HttpParserException(
- "Connection closed before full ${type()} header was received"));
+ new AsyncError(
+ new HttpParserException(
+ "Connection closed before full header was received")));
+ _controller.close();
return;
}
- if (!_chunked && _contentLength == -1) {
- dataEnd(true);
+ if (!_chunked && _transferLength == -1) {
_state = _State.CLOSED;
- closed();
} else {
_state = _State.FAILURE;
// Report the error through the error callback if any. Otherwise
// throw the error.
error(
- new HttpParserException(
- "Connection closed before full ${type()} body was received"));
+ new AsyncError(
+ new HttpParserException(
+ "Connection closed before full body was received")));
}
+ _controller.close();
}
- void streamError(e) {
- error(e);
+ void _onError(e) {
+ _controller.signalError(e);
}
String get version {
@@ -645,34 +768,31 @@
return null;
}
- void cancel() {
- _state = _State.CANCELED;
- }
-
- void restart() {
- _reset();
- }
-
int get messageType => _messageType;
- int get contentLength => _contentLength;
+ int get transferLength => _transferLength;
bool get upgrade => _connectionUpgrade && _state == _State.UPGRADED;
bool get persistentConnection => _persistentConnection;
void set responseToMethod(String method) { _responseToMethod = method; }
+ _HttpDetachedIncoming detachIncoming() {
+ var completer = _pauseCompleter;
+ _pauseCompleter = null;
+ return new _HttpDetachedIncoming(_socketSubscription,
+ readUnparsedData(),
+ completer);
+ }
+
List<int> readUnparsedData() {
- if (_buffer == null) return [];
- if (_index == _lastIndex) return [];
- var result = _buffer.getRange(_index, _lastIndex - _index);
+ if (_buffer == null) return null;
+ if (_index == _buffer.length) return null;
+ var result = _buffer.getRange(_index, _buffer.length - _index);
_releaseBuffer();
return result;
}
- void _bodyEnd() {
- dataEnd(_messageType == _MessageType.RESPONSE && !_persistentConnection);
- }
-
_reset() {
+ if (_state == _State.UPGRADED) return;
_state = _State.START;
_messageType = _MessageType.UNDETERMINED;
_headerField = new List();
@@ -681,21 +801,21 @@
_uri_or_reason_phrase = new List();
_httpVersion = _HttpVersion.UNDETERMINED;
- _contentLength = -1;
+ _transferLength = -1;
_persistentConnection = false;
_connectionUpgrade = false;
_chunked = false;
+ _noMessageBody = false;
_responseToMethod = null;
_remainingContent = null;
- _headers = new _HttpHeaders();
+ _headers = null;
}
_releaseBuffer() {
_buffer = null;
_index = null;
- _lastIndex = null;
}
bool _isTokenChar(int byte) {
@@ -744,12 +864,69 @@
}
}
+ void _createIncoming(int transferLength) {
+ assert(_incoming == null);
+ assert(_bodyController == null);
+ _bodyController = new StreamController<List<int>>(
+ onSubscriptionStateChange: _updateParsePauseState,
+ onPauseStateChange: _updateParsePauseState);
+ _incoming = new _HttpIncoming(
+ _headers, transferLength, _bodyController.stream);
+ _pauseParsing(); // Needed to handle detaching - don't start on the body!
+ }
+
+ void _closeIncoming() {
+ assert(_incoming != null);
+ var tmp = _incoming;
+ _incoming = null;
+ tmp.close();
+ if (_bodyController != null) {
+ _bodyController.close();
+ _bodyController = null;
+ }
+ _updateParsePauseState();
+ }
+
+ void _continueParsing() {
+ _paused = false;
+ if (!_parserCalled && _buffer != null) _parse();
+ }
+
+ void _pauseParsing() {
+ _paused = true;
+ }
+
+ void _updateParsePauseState() {
+ if (_bodyController != null) {
+ if (_bodyController.hasSubscribers && !_bodyController.isPaused) {
+ _continueParsing();
+ } else {
+ _pauseParsing();
+ }
+ } else {
+ if (_controller.hasSubscribers && !_controller.isPaused) {
+ _continueParsing();
+ } else {
+ _pauseParsing();
+ }
+ }
+ }
+
+ void error(error) {
+ if (_socketSubscription != null) _socketSubscription.cancel();
+ _state = _State.FAILURE;
+ _controller.signalError(error);
+ _controller.close();
+ }
+
+ // State.
+ bool _parserCalled = false;
+
// The data that is currently being parsed.
List<int> _buffer;
int _index;
- int _lastIndex;
- bool _requestParser;
+ final bool _requestParser;
int _state;
int _httpVersionIndex;
int _messageType;
@@ -760,23 +937,24 @@
List _headerValue;
int _httpVersion;
- int _contentLength;
+ int _transferLength;
bool _persistentConnection;
bool _connectionUpgrade;
bool _chunked;
+ bool _noMessageBody;
String _responseToMethod; // Indicates the method used for the request.
int _remainingContent;
- _HttpHeaders _headers = new _HttpHeaders();
+ _HttpHeaders _headers;
- // Callbacks.
- Function requestStart;
- Function responseStart;
- Function dataReceived;
- Function dataEnd;
- Function error;
- Function closed;
+ // The current incoming connection.
+ _HttpIncoming _incoming;
+ StreamSubscription _socketSubscription;
+ bool _paused = false;
+ Completer _pauseCompleter;
+ StreamController<_HttpIncoming> _controller;
+ StreamController<List<int>> _bodyController;
}
diff --git a/sdk/lib/io/http_session.dart b/sdk/lib/io/http_session.dart
index 99bf13c..4cde1be 100644
--- a/sdk/lib/io/http_session.dart
+++ b/sdk/lib/io/http_session.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
@@ -9,6 +9,18 @@
// A _HttpSession is a node in a double-linked list, with _next and _prev being
// the previous and next pointers.
class _HttpSession implements HttpSession {
+ // Destroyed marked. Used by the http connection to see if a session is valid.
+ bool _destroyed = false;
+ bool _isNew = true;
+ DateTime _lastSeen;
+ Function _timeoutCallback;
+ _HttpSessionManager _sessionManager;
+ // Pointers in timeout queue.
+ _HttpSession _prev;
+ _HttpSession _next;
+
+ final Map _data = new Map();
+
_HttpSession(_HttpSessionManager this._sessionManager, String this.id)
: _lastSeen = new DateTime.now();
@@ -25,24 +37,29 @@
_sessionManager._bumpToEnd(this);
}
- dynamic data;
-
DateTime get lastSeen => _lastSeen;
+ bool get isNew => _isNew;
+
final String id;
void set onTimeout(void callback()) {
_timeoutCallback = callback;
}
- // Destroyed marked. Used by the http connection to see if a session is valid.
- bool _destroyed = false;
- DateTime _lastSeen;
- Function _timeoutCallback;
- _HttpSessionManager _sessionManager;
- // Pointers in timeout queue.
- _HttpSession _prev;
- _HttpSession _next;
+ // Map implementation:
+ bool containsValue(value) => _data.containsValue(value);
+ bool containsKey(key) => _data.containsKey(key);
+ operator [](key) => _data[key];
+ void operator []=(key, value) { _data[key] = value; }
+ putIfAbsent(key, ifAbsent) => _data.putIfAbsent(key, ifAbsent);
+ remove(key) => _data.remove(key);
+ void clear() => _data.clear();
+ void forEach(void f(key, value)) => _data.forEach(f);
+ Iterable get keys => _data.keys;
+ Iterable get values => _data.values;
+ int get length => _data.length;
+ bool get isEmpty => _data.isEmpty;
}
// Private class used to manage all the active sessions. The sessions are stored
@@ -63,7 +80,7 @@
return _sessions[id];
}
- _HttpSession createSession(init(HttpSession session)) {
+ _HttpSession createSession() {
var id = createSessionId();
// TODO(ajohnsen): Consider adding a limit and throwing an exception.
// Should be very unlikely however.
@@ -71,7 +88,6 @@
id = createSessionId();
}
var session = _sessions[id] = new _HttpSession(this, id);
- if (init != null) init(session);
_addToTimeoutQueue(session);
return session;
}
diff --git a/sdk/lib/io/input_stream.dart b/sdk/lib/io/input_stream.dart
deleted file mode 100644
index 8db5e19..0000000
--- a/sdk/lib/io/input_stream.dart
+++ /dev/null
@@ -1,258 +0,0 @@
-// 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.
-
-part of dart.io;
-
-/**
- * Basic input stream which supplies binary data.
- *
- * Input streams are used to read data sequentially from some data
- * source. All input streams are non-blocking. They each have a number
- * of read calls which will always return without any IO related
- * blocking. If the requested data is not available a read call will
- * return `null`. All input streams have one or more handlers which
- * will trigger when data is available.
- *
- * The following example shows a data handler in an ordinary input
- * stream which will be called when some data is available and a call
- * to read will not return `null`.
- *
- * InputStream input = ...
- * input.onData = () {
- * var data = input.read();
- * ...
- * };
- *
- * If for some reason the data from an input stream cannot be handled
- * by the application immediately setting the data handler to `null`
- * will avoid further callbacks until it is set to a function
- * again. While the data handler is not active system flow control
- * will be used to avoid buffering more data than needed.
- *
- * Always set up appropriate handlers when using input streams.
- *
- */
-abstract class InputStream {
- /**
- * Reads data from the stream. Returns a system allocated buffer
- * with up to [len] bytes. If no value is passed for [len] all
- * available data will be returned. If no data is available null will
- * be returned.
- */
- List<int> read([int len]);
-
- /**
- * Reads up to [len] bytes into buffer [buffer] starting at offset
- * [offset]. Returns the number of bytes actually read which might
- * be zero. If [offset] is not specified 0 is used. If [len] is not
- * specified the length of [buffer] is used.
- */
- int readInto(List<int> buffer, [int offset, int len]);
-
- /**
- * Returns the number of bytes available for immediate reading.
- */
- int available();
-
- /**
- * Pipe the content of this input stream directly to the output
- * stream [output]. The default behavior is to close the output when
- * all the data from the input stream have been written. Specifying
- * `false` for the optional argument [close] keeps the output
- * stream open after writing all data from the input stream.
- */
- void pipe(OutputStream output, {bool close: true});
-
- /**
- * Close the underlying communication channel to avoid getting any
- * more data. In normal situations, where all data is read from the
- * stream until the close handler is called, calling [close] is not
- * required. When [close] is used the close handler will still be
- * called.
- */
- void close();
-
- /**
- * Returns whether the stream is closed. There will be no more data
- * to read.
- */
- bool get closed;
-
- /**
- * Sets the handler that gets called when data is available.
- */
- void set onData(void callback());
-
- /**
- * Sets the handler that gets called when there will be no more data
- * available in the stream.
- */
- void set onClosed(void callback());
-
- /**
- * Sets the handler that gets called when the underlying
- * communication channel gets into some kind of error situation.
- */
- void set onError(void callback(e));
-}
-
-
-/**
- * String encodings.
- */
-class Encoding {
- static const Encoding UTF_8 = const Encoding._internal("UTF-8");
- static const Encoding ISO_8859_1 = const Encoding._internal("ISO-8859-1");
- static const Encoding ASCII = const Encoding._internal("ASCII");
- /**
- * SYSTEM encoding is the current code page on Windows and UTF-8 on
- * Linux and Mac.
- */
- static const Encoding SYSTEM = const Encoding._internal("SYSTEM");
- const Encoding._internal(String this.name);
- final String name;
-}
-
-
-/**
- * A string input stream wraps a basic input stream and supplies
- * string data. This data can be read either as string chunks or as
- * lines separated by line termination character sequences.
- */
-abstract class StringInputStream {
- /**
- * Decodes a binary input stream into characters using the specified
- * encoding.
- */
- factory StringInputStream(InputStream input,
- [Encoding encoding = Encoding.UTF_8]) {
- return new _StringInputStream(input, encoding);
- }
-
- /**
- * Reads up to [len] characters from the stream. if [len] is not
- * specified reads as many characters as is available from the
- * stream. If no data is available null will be returned.
- */
- String read([int len]);
-
- /**
- * Reads the next line from the stream. The line ending characters
- * will not be part of the returned string. If a full line is not
- * available null will be returned.
- */
- String readLine();
-
- /**
- * Returns the number of characters available for immediate
- * reading. Note that this includes all characters that will be in
- * the String returned from [read] this includes line breaking
- * characters. If [readLine] is used for reading one can observe
- * less characters being returned as the line breaking characters
- * are discarded.
- */
- int available();
-
- /**
- * Returns whether the stream has been closed. There might still be
- * more data to read.
- */
- bool get closed;
-
- /**
- * Returns the encoding used to decode the binary data into characters.
- */
- Encoding get encoding;
-
- /**
- * Sets the handler that gets called when data is available. The two
- * handlers [onData] and [onLine] are mutually exclusive
- * and setting one will remove the other.
- */
- void set onData(void callback());
-
- /**
- * Sets the handler that gets called when a line is available. The
- * two handlers [onData] and [onLine] are mutually
- * exclusive and setting one will remove the other.
- */
- void set onLine(void callback());
-
- /**
- * Sets the handler that gets called when there will be no more data
- * available in the stream.
- */
- void set onClosed(void callback());
-
- /**
- * Sets the handler that gets called when the underlying
- * communication channel gets into some kind of error situation.
- */
- void set onError(void callback(e));
-}
-
-
-/**
- * A chunked input stream wraps a basic input stream and supplies
- * binary data in configurable chunk sizes.
- */
-abstract class ChunkedInputStream {
- /**
- * Adds buffering to an input stream and provide the ability to read
- * the data in known size chunks.
- */
- factory ChunkedInputStream(InputStream input, [int chunkSize = 0]) {
- return new _ChunkedInputStream(input, chunkSize);
- }
-
- /**
- * Reads [chunkSize] bytes from the stream. If [chunkSize] bytes are
- * not currently available null is returned. When the stream is
- * closed the last call can return with less than [chunkSize] bytes.
- */
- List<int> read();
-
- /**
- * Returns whether the stream has been closed. There might still be
- * more data to read.
- */
- bool get closed;
-
- /**
- * Returns the chunk size used by this stream.
- */
- int get chunkSize;
-
- /**
- * Sets the chunk size used by this stream.
- */
- void set chunkSize(int chunkSize);
-
- /**
- * Sets the handler that gets called when at least [chunkSize] bytes
- * of data is available or the underlying stream has been closed and
- * there is still unread data.
- */
- void set onData(void callback());
-
- /**
- * Sets the handler that gets called when there will be no more data
- * available in the stream.
- */
- void set onClosed(void callback());
-
- /**
- * Sets the handler that gets called when the underlying
- * communication channel gets into some kind of error situation.
- */
- void set onError(void callback(e));
-}
-
-
-class StreamException implements Exception {
- const StreamException([String this.message = ""]);
- const StreamException.streamClosed() : message = "Stream closed";
- String toString() => "StreamException: $message";
- final String message;
-}
diff --git a/sdk/lib/io/io.dart b/sdk/lib/io/io.dart
index a9fa17a..b7d2843 100644
--- a/sdk/lib/io/io.dart
+++ b/sdk/lib/io/io.dart
@@ -25,34 +25,29 @@
part 'base64.dart';
part 'buffer_list.dart';
-part 'chunked_stream.dart';
part 'common.dart';
part 'directory.dart';
part 'directory_impl.dart';
part 'eventhandler.dart';
part 'file.dart';
part 'file_impl.dart';
+part 'file_system_entity.dart';
part 'http.dart';
part 'http_headers.dart';
part 'http_impl.dart';
part 'http_parser.dart';
part 'http_session.dart';
part 'http_utils.dart';
-part 'input_stream.dart';
-part 'list_stream.dart';
-part 'list_stream_impl.dart';
+part 'io_stream_consumer.dart';
part 'mime_multipart_parser.dart';
-part 'output_stream.dart';
part 'path.dart';
part 'path_impl.dart';
part 'platform.dart';
part 'platform_impl.dart';
part 'process.dart';
part 'socket.dart';
-part 'socket_stream_impl.dart';
part 'stdio.dart';
-part 'stream_util.dart';
-part 'string_stream.dart';
+part 'string_transformer.dart';
part 'timer_impl.dart';
part 'secure_socket.dart';
part 'secure_server_socket.dart';
diff --git a/sdk/lib/io/io_stream_consumer.dart b/sdk/lib/io/io_stream_consumer.dart
new file mode 100644
index 0000000..51b59ab
--- /dev/null
+++ b/sdk/lib/io/io_stream_consumer.dart
@@ -0,0 +1,160 @@
+// 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.
+
+part of dart.io;
+
+/**
+ * Helper class to wrap a [StreamConsumer<List<int>, T>] and provide utility
+ * functions for writing to the StreamConsumer directly. The [IOSink]
+ * buffers the input given by [add] and [addString] and will delay a [consume]
+ * or [addStream] until the buffer is flushed.
+ *
+ * When the [IOSink] is bound to a stream (through either [comsume]
+ * or [addStream]) any call to the [IOSink] will throw a
+ * [StateError].
+ */
+class IOSink<T> implements StreamConsumer<List<int>, T> {
+ final StreamConsumer<List<int>, T> _target;
+
+ StreamController<List<int>> _controllerInstance;
+ Future<T> _pipeFuture;
+ StreamSubscription<List<int>> _bindSubscription;
+ bool _paused = true;
+
+ IOSink(StreamConsumer<List<int>, T> target) : _target = target;
+
+ /**
+ * Provide functionality for piping to the [IOSink].
+ */
+ Future<T> consume(Stream<List<int>> stream) {
+ if (_isBound) {
+ throw new StateError("IOSink is already bound to a stream");
+ }
+ return _fillFromStream(stream);
+ }
+
+ /**
+ * Like [consume], but will not close the target when done.
+ */
+ Future<T> addStream(Stream<List<int>> stream) {
+ if (_isBound) {
+ throw new StateError("IOSink is already bound to a stream");
+ }
+ return _fillFromStream(stream, unbind: true);
+ }
+
+ /**
+ * Write a list of bytes to the target.
+ */
+ void add(List<int> data) {
+ if (_isBound) {
+ throw new StateError("IOSink is already bound to a stream");
+ }
+ _controller.add(data);
+ }
+
+ /**
+ * Write a String to the target.
+ */
+ void addString(String string, [Encoding encoding = Encoding.UTF_8]) {
+ add(_encodeString(string, encoding));
+ }
+
+ /**
+ * Close the target.
+ */
+ void close() {
+ if (_isBound) {
+ throw new StateError("IOSink is already bound to a stream");
+ }
+ _controller.close();
+ }
+
+ /**
+ * Get future that will complete when all data has been written to
+ * the IOSink and it has been closed.
+ */
+ Future<T> get done {
+ _controller;
+ return _pipeFuture.then((_) => this);
+ }
+
+ StreamController<List<int>> get _controller {
+ if (_controllerInstance == null) {
+ _controllerInstance = new StreamController<List<int>>(
+ onPauseStateChange: _onPauseStateChange,
+ onSubscriptionStateChange: _onSubscriptionStateChange);
+ _pipeFuture = _controller.stream.pipe(_target);
+ }
+ return _controllerInstance;
+ }
+
+ bool get _isBound => _bindSubscription != null;
+
+ void _onPauseStateChange() {
+ _paused = _controller.isPaused;
+ if (_controller.isPaused) {
+ _pause();
+ } else {
+ _resume();
+ }
+ }
+
+ void _pause() {
+ if (_bindSubscription != null) {
+ try {
+ // The subscription can be canceled at this point.
+ _bindSubscription.pause();
+ } catch (e) {
+ }
+ }
+ }
+
+ void _resume() {
+ if (_bindSubscription != null) {
+ try {
+ // The subscription can be canceled at this point.
+ _bindSubscription.resume();
+ } catch (e) {
+ }
+ }
+ }
+
+ void _onSubscriptionStateChange() {
+ if (_controller.hasSubscribers) {
+ _paused = false;
+ _resume();
+ } else {
+ if (_bindSubscription != null) {
+ _bindSubscription.cancel();
+ _bindSubscription = null;
+ }
+ }
+ }
+
+ Future<T> _fillFromStream(Stream<List<int>> stream, {unbind: false}) {
+ _controller;
+ Completer<T> unbindCompleter;
+ if (unbind) {
+ unbindCompleter = new Completer<T>();
+ }
+ _bindSubscription = stream.listen(
+ _controller.add,
+ onDone: () {
+ _bindSubscription = null;
+ if (unbind) {
+ unbindCompleter.complete(null);
+ } else {
+ _controller.close();
+ }
+ },
+ onError: _controller.signalError);
+ if (_paused) _pause();
+ if (unbind) {
+ return unbindCompleter.future;
+ } else {
+ return _pipeFuture;
+ }
+ }
+}
diff --git a/sdk/lib/io/iolib_sources.gypi b/sdk/lib/io/iolib_sources.gypi
index b4ac1bb..b695556 100644
--- a/sdk/lib/io/iolib_sources.gypi
+++ b/sdk/lib/io/iolib_sources.gypi
@@ -6,34 +6,29 @@
'sources': [
'base64.dart',
'buffer_list.dart',
- 'chunked_stream.dart',
'common.dart',
'directory.dart',
'directory_impl.dart',
'eventhandler.dart',
'file.dart',
'file_impl.dart',
+ 'file_system_entity.dart',
'http.dart',
'http_headers.dart',
'http_impl.dart',
'http_parser.dart',
'http_session.dart',
'http_utils.dart',
- 'input_stream.dart',
- 'list_stream.dart',
- 'list_stream_impl.dart',
+ 'io_stream_consumer.dart',
'mime_multipart_parser.dart',
- 'output_stream.dart',
'path.dart',
'path_impl.dart',
'platform.dart',
'platform_impl.dart',
'process.dart',
'socket.dart',
- 'socket_stream_impl.dart',
'stdio.dart',
- 'stream_util.dart',
- 'string_stream.dart',
+ 'string_transformer.dart',
'timer_impl.dart',
'secure_socket.dart',
'secure_server_socket.dart',
diff --git a/sdk/lib/io/list_stream.dart b/sdk/lib/io/list_stream.dart
deleted file mode 100644
index a56476d..0000000
--- a/sdk/lib/io/list_stream.dart
+++ /dev/null
@@ -1,57 +0,0 @@
-// 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.
-
-part of dart.io;
-
-/**
- * [ListInputStream] makes it possible to use the [InputStream]
- * interface to stream over data that is received in chunks as lists
- * of integers.
- *
- * When a new list of integers is received it can be written to the
- * [ListInputStream] using the [write] method. The [markEndOfStream]
- * method must be called when the last data has been written to the
- * [ListInputStream].
- */
-abstract class ListInputStream implements InputStream {
- /**
- * Create an empty [ListInputStream] to which data can be written
- * using the [write] method.
- */
- factory ListInputStream() => new _ListInputStream();
-
- /**
- * Write more data to be streamed over to the [ListInputStream].
- */
- void write(List<int> data);
-
- /**
- * Notify the [ListInputStream] that no more data will be written to
- * it.
- */
- void markEndOfStream();
-}
-
-
-/**
- * [ListOutputStream] makes it possible to use the [OutputStream]
- * interface to write data to a [List] of integers.
- */
-abstract class ListOutputStream implements OutputStream {
- /**
- * Create a [ListOutputStream].
- */
- factory ListOutputStream() => new _ListOutputStream();
-
- /**
- * Reads all available data from the stream. If no data is available `null`
- * will be returned.
- */
- List<int> read();
-
- /**
- * Sets the handler that gets called when data is available.
- */
- void set onData(void callback());
-}
diff --git a/sdk/lib/io/list_stream_impl.dart b/sdk/lib/io/list_stream_impl.dart
deleted file mode 100644
index e593384..0000000
--- a/sdk/lib/io/list_stream_impl.dart
+++ /dev/null
@@ -1,161 +0,0 @@
-// 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.
-
-part of dart.io;
-
-/**
- * Default implementation of [ListInputStream].
- */
-class _ListInputStream extends _BaseDataInputStream implements ListInputStream {
- _ListInputStream() : _bufferList = new _BufferList();
-
- void write(List<int> data) {
- if (_streamMarkedClosed) {
- throw new StreamException.streamClosed();
- }
- _bufferList.add(data);
- _checkScheduleCallbacks();
- }
-
- void markEndOfStream() {
- _streamMarkedClosed = true;
- _checkScheduleCallbacks();
- }
-
- int available() => _bufferList.length;
-
- List<int> _read(int bytesToRead) {
- return _bufferList.readBytes(bytesToRead);
- }
-
- int _readInto(List<int> buffer, int offset, int bytesToRead) {
- List<int> tmp = _bufferList.readBytes(bytesToRead);
- buffer.setRange(offset, bytesToRead, tmp, 0);
- return bytesToRead;
- }
-
- void _close() {
- _streamMarkedClosed = true;
- _bufferList.clear();
- }
-
- _BufferList _bufferList;
-}
-
-
-class _ListOutputStream extends _BaseOutputStream implements ListOutputStream {
- _ListOutputStream() : _bufferList = new _BufferList();
-
- List<int> read() => _bufferList.readBytes(_bufferList.length);
-
- bool write(List<int> buffer, [bool copyBuffer = true]) {
- if (_streamMarkedClosed) throw new StreamException.streamClosed();
- if (copyBuffer) {
- _bufferList.add(buffer.getRange(0, buffer.length));
- } else {
- _bufferList.add(buffer);
- }
- _checkScheduleCallbacks();
- return true;
- }
-
- bool writeFrom(List<int> buffer, [int offset = 0, int len]) {
- return write(
- buffer.getRange(offset, (len == null) ? buffer.length - offset : len),
- false);
- }
-
- void flush() {
- // Nothing to do on a list output stream.
- }
-
- void close() {
- if (_streamMarkedClosed) throw new StreamException.streamClosed();
- _streamMarkedClosed = true;
- _checkScheduleCallbacks();
- }
-
- void destroy() {
- close();
- }
-
- void set onData(void callback()) {
- _clientDataHandler = callback;
- _checkScheduleCallbacks();
- }
-
- void set onNoPendingWrites(void callback()) {
- _clientNoPendingWriteHandler = callback;
- _checkScheduleCallbacks();
- }
-
- void set onClosed(void callback()) {
- _clientCloseHandler = callback;
- }
-
- void set onError(void callback(e)) {
- // No errors emitted.
- }
-
- void _checkScheduleCallbacks() {
- void issueDataCallback() {
- _scheduledDataCallback = null;
- if (_clientDataHandler != null) {
- _clientDataHandler();
- _checkScheduleCallbacks();
- }
- }
-
- void issueNoPendingWriteCallback() {
- _scheduledNoPendingWriteCallback = null;
- if (_clientNoPendingWriteHandler != null &&
- !_streamMarkedClosed) {
- _clientNoPendingWriteHandler();
- _checkScheduleCallbacks();
- }
- }
-
- void issueCloseCallback() {
- _scheduledCloseCallback = null;
- if (_clientCloseHandler != null) _clientCloseHandler();
- }
-
- // Schedule no pending callback if there is a callback set as this
- // output stream does not wait for any transmission. Schedule
- // close callback once when the stream is closed. Only schedule a
- // new callback if the previous one has actually been called.
- if (_closeCallbackCalled) return;
-
- if (!_streamMarkedClosed) {
- if (!_bufferList.isEmpty &&
- _clientDataHandler != null &&
- _scheduledDataCallback == null) {
- _scheduledDataCallback = Timer.run(issueDataCallback);
- }
-
- if (_clientNoPendingWriteHandler != null &&
- _scheduledNoPendingWriteCallback == null &&
- _scheduledDataCallback == null) {
- _scheduledNoPendingWriteCallback =
- Timer.run(issueNoPendingWriteCallback);
- }
-
- } else if (_clientCloseHandler != null) {
- _scheduledCloseCallback = Timer.run(issueCloseCallback);
- _closeCallbackCalled = true;
- }
- }
-
- bool get closed => _streamMarkedClosed;
-
- _BufferList _bufferList;
- bool _streamMarkedClosed = false;
- bool _closeCallbackCalled = false;
- Timer _scheduledDataCallback;
- Timer _scheduledNoPendingWriteCallback;
- Timer _scheduledCloseCallback;
- Function _clientDataHandler;
- Function _clientNoPendingWriteHandler;
- Function _clientCloseHandler;
-}
diff --git a/sdk/lib/io/output_stream.dart b/sdk/lib/io/output_stream.dart
deleted file mode 100644
index ecccced..0000000
--- a/sdk/lib/io/output_stream.dart
+++ /dev/null
@@ -1,103 +0,0 @@
-// 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.
-
-part of dart.io;
-
-/**
- * Output streams are used to write data sequentially to a data
- * destination e.g. a connected socket or an open file.
- *
- * An output stream provides internal buffering of the data written
- * through all calls to [write] and [writeFrom] if data cannot be
- * written immediately to the communication channel. The callback set
- * through [onNoPendingWrites] can be used to to keep the rate of
- * writing in sync with the rate the system can actually write data to
- * the underlying communication channel.
- */
-abstract class OutputStream {
- /**
- * Writes the content of [buffer] to the stream. If [copyBuffer] is
- * false ownership of the specified buffer is passed to the system
- * and the caller should not change it afterwards. The default value
- * for [copyBuffer] is true.
- *
- * Returns true if the data could be written to the underlying
- * communication channel immediately. Otherwise the data is buffered
- * by the output stream and will be sent as soon as possible.
- */
- bool write(List<int> buffer, [bool copyBuffer]);
-
- /**
- * Writes [len] bytes from buffer [buffer] starting at offset
- * [offset] to the output stream. If [offset] is not specified the
- * default is 0. If [len] is not specified the default is the length
- * of the buffer minus [offset] (i.e. writing from offset to the end
- * of the buffer). The system will copy the data to be written so
- * the caller can safely change [buffer] afterwards.
- *
- * Returns true if the data could be written to the underlying
- * communication channel immediately. Otherwise the data is buffered
- * by the output stream and will be sent as soon as possible.
- */
- bool writeFrom(List<int> buffer, [int offset, int len]);
-
- /**
- * Write a string to the stream using the given [encoding].The
- * default encoding is UTF-8 - [:Encoding.UTF_8:].
- *
- * Returns true if the data could be written to the underlying
- * communication channel immediately. Otherwise the data is buffered
- * by the output stream and will be sent as soon as possible.
- */
- bool writeString(String string, [Encoding encoding]);
-
- /**
- * Flushes data from any internal buffers as soon as possible. Note
- * that the actual meaning of calling [flush] will depend on the
- * actual type of the underlying communication channel.
- */
- void flush();
-
- /**
- * Signal that no more data will be written to the output stream. When all
- * buffered data has been written out to the communication channel, the
- * channel will be closed and the [onClosed] callback will be called.
- */
- void close();
-
- /**
- * Close the communication channel immediately ignoring any buffered
- * data.
- */
- void destroy();
-
- /**
- * Returns whether the stream has been closed by calling close(). If true, no
- * more data may be written to the output stream, but there still may be
- * buffered data that has not been written to the communication channel. The
- * onClosed handler will only be called once all data has been written out.
- */
- bool get closed;
-
- /**
- * Sets the handler that gets called when the internal OS buffers
- * have been flushed. This callback can be used to keep the rate of
- * writing in sync with the rate the system can write data to the
- * underlying communication channel.
- */
- void set onNoPendingWrites(void callback());
-
- /**
- * Sets the handler that gets called when the underlying communication channel
- * has been closed and all the buffered data has been sent.
- */
- void set onClosed(void callback());
-
- /**
- * Sets the handler that gets called when the underlying
- * communication channel gets into some kind of error situation.
- */
- void set onError(void callback(e));
-}
-
diff --git a/sdk/lib/io/process.dart b/sdk/lib/io/process.dart
index e146c61..deb3c83 100644
--- a/sdk/lib/io/process.dart
+++ b/sdk/lib/io/process.dart
@@ -84,7 +84,7 @@
* Throws an [UnsupportedError] if the process is
* non-interactive.
*/
- InputStream get stdout;
+ Stream<List<int>> get stdout;
/**
* Returns an input stream of the process stderr.
@@ -92,7 +92,7 @@
* Throws an [UnsupportedError] if the process is
* non-interactive.
*/
- InputStream get stderr;
+ Stream<List<int>> get stderr;
/**
* Returns an output stream to the process stdin.
@@ -100,7 +100,7 @@
* Throws an [UnsupportedError] if the process is
* non-interactive.
*/
- OutputStream get stdin;
+ IOSink<Process> get stdin;
/**
* Sets an exit handler which gets invoked when the process
@@ -109,7 +109,7 @@
* Throws an [UnsupportedError] if the process is
* non-interactive.
*/
- void set onExit(void callback(int exitCode));
+ Future<int> exitCode;
/**
* On Windows, [kill] kills the process, ignoring the [signal]
diff --git a/sdk/lib/io/secure_server_socket.dart b/sdk/lib/io/secure_server_socket.dart
index 7c8d78f..7a9b34d 100644
--- a/sdk/lib/io/secure_server_socket.dart
+++ b/sdk/lib/io/secure_server_socket.dart
@@ -4,16 +4,135 @@
part of dart.io;
-abstract class SecureServerSocket implements ServerSocket {
+/**
+ * The [SecureServerSocket] is a server socket, providing a stream of high-level
+ * [Socket]s.
+ *
+ * See [SecureSocket] for more info.
+ */
+class SecureServerSocket extends Stream<SecureSocket> implements ServerSocket {
+ final RawSecureServerSocket _socket;
+
+ SecureServerSocket._(RawSecureServerSocket this._socket);
+
/**
- * Constructs a new secure server socket, binds it to a given address
- * and port, and listens on it. Incoming client connections are
- * promoted to secure connections, using the server certificate given by
- * certificate_name. The bindAddress must be given as a numeric address,
- * not a host name. The certificate name is the distinguished name (DN) of
- * the certificate, such as "CN=localhost" or "CN=myserver.mydomain.com".
- * The certificate is looked up in the NSS certificate database set by
- * SecureSocket.setCertificateDatabase.
+ * Returns a future for a [SecureServerSocket]. When the future
+ * completes the server socket is bound to the given [address] and
+ * [port] and has started listening on it.
+ *
+ * If [port] has the value [:0:] (the default) an ephemeral port will
+ * be chosen by the system. The actual port used can be retrieved
+ * using the [port] getter.
+ *
+ * If [backlog] has the value of [:0:] a reasonable value will be
+ * chosen by the system.
+ *
+ * Incoming client connections are promoted to secure connections, using
+ * the server certificate given by [certificateName].
+ *
+ * [address] must be given as a numeric address, not a host name.
+ *
+ * [certificateName] is the nickname or the distinguished name (DN) of
+ * the certificate in the certificate database. It is looked up in the
+ * NSS certificate database set by SecureSocket.setCertificateDatabase.
+ * If [certificateName] contains "CN=", it is assumed to be a distinguished
+ * name. Otherwise, it is looked up as a nickname.
+ *
+ * To request or require that clients authenticate by providing an SSL (TLS)
+ * client certificate, set the optional parameter [requestClientCertificate]
+ * or [requireClientCertificate] to true. Requiring a certificate implies
+ * requesting a certificate, so one doesn't need to set both to true.
+ * To check whether a client certificate was received, check
+ * SecureSocket.peerCertificate after connecting. If no certificate
+ * was received, the result will be null.
+ */
+ static Future<SecureServerSocket> bind(
+ String address,
+ int port,
+ int backlog,
+ String certificateName,
+ {bool requestClientCertificate: false,
+ bool requireClientCertificate: false}) {
+ return RawSecureServerSocket.bind(
+ address,
+ port,
+ backlog,
+ certificateName,
+ requestClientCertificate: requestClientCertificate,
+ requireClientCertificate: requireClientCertificate).then(
+ (serverSocket) => new SecureServerSocket._(serverSocket));
+ }
+
+ StreamSubscription<SecureSocket> listen(void onData(SecureSocket socket),
+ {void onError(AsyncError error),
+ void onDone(),
+ bool unsubscribeOnError}) {
+ return _socket.map((rawSocket) => new SecureSocket._(rawSocket))
+ .listen(onData,
+ onError: onError,
+ onDone: onDone,
+ unsubscribeOnError: unsubscribeOnError);
+ }
+
+ /**
+ * Returns the port used by this socket.
+ */
+ int get port => _socket.port;
+
+ /**
+ * Closes the socket.
+ */
+ void close() => _socket.close();
+}
+
+
+/**
+ * The RawSecureServerSocket is a server socket, providing a stream of low-level
+ * [RawSecureSocket]s.
+ *
+ * See [RawSecureSocket] for more info.
+ */
+class RawSecureServerSocket extends Stream<RawSecureSocket> {
+ RawServerSocket _socket;
+ StreamController<RawSecureSocket> _controller;
+ StreamSubscription<RawSocket> _subscription;
+ final String certificateName;
+ final bool requestClientCertificate;
+ final bool requireClientCertificate;
+ bool _closed = false;
+
+ RawSecureServerSocket._(RawServerSocket serverSocket,
+ String this.certificateName,
+ bool this.requestClientCertificate,
+ bool this.requireClientCertificate) {
+ _socket = serverSocket;
+ _controller = new StreamController<RawSecureSocket>(
+ onPauseStateChange: _onPauseStateChange,
+ onSubscriptionStateChange: _onSubscriptionStateChange);
+ }
+
+ /**
+ * Returns a future for a [RawSecureServerSocket]. When the future
+ * completes the server socket is bound to the given [address] and
+ * [port] and has started listening on it.
+ *
+ * If [port] has the value [:0:] (the default) an ephemeral port will
+ * be chosen by the system. The actual port used can be retrieved
+ * using the [port] getter.
+ *
+ * If [backlog] has the value of [:0:] a reasonable value will be
+ * chosen by the system.
+ *
+ * Incoming client connections are promoted to secure connections,
+ * using the server certificate given by [certificateName].
+ *
+ * [address] must be given as a numeric address, not a host name.
+ *
+ * [certificateName] is the nickname or the distinguished name (DN) of
+ * the certificate in the certificate database. It is looked up in the
+ * NSS certificate database set by SecureSocket.setCertificateDatabase.
+ * If [certificateName] contains "CN=", it is assumed to be a distinguished
+ * name. Otherwise, it is looked up as a nickname.
*
* To request or require that clients authenticate by providing an SSL (TLS)
* client certificate, set the optional parameters requestClientCertificate or
@@ -22,79 +141,95 @@
* check SecureSocket.peerCertificate after connecting. If no certificate
* was received, the result will be null.
*/
- factory SecureServerSocket(String bindAddress,
- int port,
- int backlog,
- String certificate_name,
- {bool requestClientCertificate: false,
- bool requireClientCertificate: false}) {
- return new _SecureServerSocket(bindAddress,
- port,
- backlog,
- certificate_name,
- requestClientCertificate,
- requireClientCertificate);
- }
-}
-
-
-class _SecureServerSocket implements SecureServerSocket {
-
- _SecureServerSocket(String bindAddress,
- int port,
- int backlog,
- String this.certificate_name,
- bool this.requestClientCertificate,
- bool this.requireClientCertificate) {
- socket = new ServerSocket(bindAddress, port, backlog);
- socket.onConnection = this._onConnectionHandler;
+ static Future<RawSecureServerSocket> bind(
+ String address,
+ int port,
+ int backlog,
+ String certificateName,
+ {bool requestClientCertificate: false,
+ bool requireClientCertificate: false}) {
+ return RawServerSocket.bind(address, port, backlog)
+ .then((serverSocket) => new RawSecureServerSocket._(
+ serverSocket,
+ certificateName,
+ requestClientCertificate,
+ requireClientCertificate));
}
- void set onConnection(void callback(Socket connection)) {
- _onConnectionCallback = callback;
- }
-
- void set onError(void callback(e)) {
- socket.onError = callback;
+ StreamSubscription<RawSecureSocket> listen(void onData(RawSecureSocket s),
+ {void onError(AsyncError error),
+ void onDone(),
+ bool unsubscribeOnError}) {
+ return _controller.stream.listen(onData,
+ onError: onError,
+ onDone: onDone,
+ unsubscribeOnError: unsubscribeOnError);
}
/**
* Returns the port used by this socket.
*/
- int get port => socket.port;
+ int get port => _socket.port;
/**
* Closes the socket.
*/
void close() {
- socket.close();
+ _closed = true;
+ _socket.close();
}
- void _onConnectionHandler(Socket connection) {
- if (_onConnectionCallback == null) {
- connection.close();
- throw new SocketIOException(
- "SecureServerSocket with no onConnection callback connected to");
- }
- if (certificate_name == null) {
- connection.close();
- throw new SocketIOException(
- "SecureServerSocket with server certificate not set connected to");
- }
- var secure_connection = new _SecureSocket(
+ void _onData(RawSocket connection) {
+ _RawSecureSocket.connect(
connection.remoteHost,
connection.remotePort,
- certificate_name,
+ certificateName,
is_server: true,
socket: connection,
requestClientCertificate: requestClientCertificate,
- requireClientCertificate: requireClientCertificate);
- _onConnectionCallback(secure_connection);
+ requireClientCertificate: requireClientCertificate)
+ .then((RawSecureSocket secureConnection) {
+ if (_closed) {
+ secureConnection.close();
+ } else {
+ _controller.add(secureConnection);
+ }
+ }).catchError((e) {
+ if (_closed) {
+ throw e;
+ } else {
+ _controller.signalError(e);
+ close();
+ }
+ });
}
- ServerSocket socket;
- var _onConnectionCallback;
- final String certificate_name;
- final bool requestClientCertificate;
- final bool requireClientCertificate;
+ void _onError(e) {
+ _controller.signalError(e);
+ close();
+ }
+
+ void _onDone() {
+ _controller.close();
+ }
+
+ void _onPauseStateChange() {
+ if (_controller.isPaused) {
+ _subscription.pause();
+ } else {
+ _subscription.resume();
+ }
+ }
+
+ void _onSubscriptionStateChange() {
+ if (_controller.hasSubscribers) {
+ _subscription = _socket.listen(_onData,
+ onDone: _onDone,
+ onError: _onError);
+ } else {
+ close();
+ }
+ }
}
+
+
diff --git a/sdk/lib/io/secure_socket.dart b/sdk/lib/io/secure_socket.dart
index 6dc1d6d..333edd1 100644
--- a/sdk/lib/io/secure_socket.dart
+++ b/sdk/lib/io/secure_socket.dart
@@ -1,54 +1,63 @@
-// Copyright (c) 2012, 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.
part of dart.io;
/**
- * SecureSocket provides a secure (SSL or TLS) client connection to a server.
- * The certificate provided by the server is checked
- * using the certificate database (optionally) provided in initialize().
+ * A high-level class for communicating securely over a TCP socket, using
+ * TLS and SSL. The [SecureSocket] exposes both a [Stream] and an
+ * [IOSink] interface, making it ideal for using together with
+ * other [Stream]s.
*/
abstract class SecureSocket implements Socket {
+ external factory SecureSocket._(RawSecureSocket rawSocket);
+
/**
* Constructs a new secure client socket and connect it to the given
- * host on the given port. The returned socket is not yet connected
- * but ready for registration of callbacks. If sendClientCertificate is
- * set to true, the socket will send a client certificate if one is
- * requested by the server. If clientCertificate is the nickname of
- * a certificate in the certificate database, that certificate will be sent.
- * If clientCertificate is null, which is the usual use case, an
+ * [host] on port [port]. The returned Future will complete with a
+ * [SecureSocket] that is connected and ready for subscription.
+ *
+ * If [sendClientCertificate] is set to true, the socket will send a client
+ * certificate if one is requested by the server.
+ *
+ * If [certificateName] is the nickname of a certificate in the certificate
+ * database, that certificate will be sent.
+ *
+ * If [certificateName] is null, which is the usual use case, an
* appropriate certificate will be searched for in the database and
* sent automatically, based on what the server says it will accept.
+ *
+ * [onBadCertificate] is an optional handler for unverifiable certificates.
+ * The handler receives the [X509Certificate], and can inspect it and
+ * decide (or let the user decide) whether to accept
+ * the connection or not. The handler should return true
+ * to continue the [SecureSocket] connection.
*/
- factory SecureSocket(String host,
- int port,
- {bool sendClientCertificate: false,
- String certificateName}) {
- return new _SecureSocket(host,
- port,
- certificateName,
- is_server: false,
- sendClientCertificate: sendClientCertificate);
+ static Future<SecureSocket> connect(
+ String host,
+ int port,
+ {bool sendClientCertificate: false,
+ String certificateName,
+ bool onBadCertificate(X509Certificate certificate)}) {
+ return RawSecureSocket.connect(host,
+ port,
+ sendClientCertificate: sendClientCertificate,
+ certificateName: certificateName,
+ onBadCertificate: onBadCertificate)
+ .then((rawSocket) => new SecureSocket._(rawSocket));
}
/**
- * Install a handler for unverifiable certificates. The handler can inspect
- * the certificate, and decide (or let the user decide) whether to accept
- * the connection or not. The callback should return true
- * to continue the SecureSocket connection.
- */
- void set onBadCertificate(bool callback(X509Certificate certificate));
-
- /**
- * Get the peerCertificate for a connected secure socket. For a server
- * socket, this will return the client certificate, or null, if no
- * client certificate was received. For a client socket, this
- * will return the server's certificate.
+ * Get the peer certificate for a connected SecureSocket. If this
+ * SecureSocket is the server end of a secure socket connection,
+ * [peerCertificate] will return the client certificate, or null, if no
+ * client certificate was received. If it is the client end,
+ * [peerCertificate] will return the server's certificate.
*/
X509Certificate get peerCertificate;
- /**
+ /**
* Initializes the NSS library. If [initialize] is not called, the library
* is automatically initialized as if [initialize] were called with no
* arguments.
@@ -88,6 +97,64 @@
/**
+ * RawSecureSocket provides a secure (SSL or TLS) network connection.
+ * Client connections to a server are provided by calling
+ * RawSecureSocket.connect. A secure server, created with
+ * RawSecureServerSocket, also returns RawSecureSocket objects representing
+ * the server end of a secure connection.
+ * The certificate provided by the server is checked
+ * using the certificate database provided in SecureSocket.initialize, and/or
+ * the default built-in root certificates.
+ */
+abstract class RawSecureSocket implements RawSocket {
+ /**
+ * Constructs a new secure client socket and connect it to the given
+ * host on the given port. The returned Future is completed with the
+ * RawSecureSocket when it is connected and ready for subscription.
+ *
+ * The certificate provided by the server is checked using the certificate
+ * database provided in [SecureSocket.initialize], and/or the default built-in
+ * root certificates. If [sendClientCertificate] is
+ * set to true, the socket will send a client certificate if one is
+ * requested by the server. If [certificateName] is the nickname of
+ * a certificate in the certificate database, that certificate will be sent.
+ * If [certificateName] is null, which is the usual use case, an
+ * appropriate certificate will be searched for in the database and
+ * sent automatically, based on what the server says it will accept.
+ *
+ * [onBadCertificate] is an optional handler for unverifiable certificates.
+ * The handler receives the [X509Certificate], and can inspect it and
+ * decide (or let the user decide) whether to accept
+ * the connection or not. The handler should return true
+ * to continue the [RawSecureSocket] connection.
+ */
+ static Future<RawSecureSocket> connect(
+ String host,
+ int port,
+ {bool sendClientCertificate: false,
+ String certificateName,
+ bool onBadCertificate(X509Certificate certificate)}) {
+ return _RawSecureSocket.connect(
+ host,
+ port,
+ certificateName,
+ is_server: false,
+ sendClientCertificate: sendClientCertificate,
+ onBadCertificate: onBadCertificate);
+ }
+
+ /**
+ * Get the peer certificate for a connected RawSecureSocket. If this
+ * RawSecureSocket is the server end of a secure socket connection,
+ * [peerCertificate] will return the client certificate, or null, if no
+ * client certificate was received. If it is the client end,
+ * [peerCertificate] will return the server's certificate.
+ */
+ X509Certificate get peerCertificate;
+}
+
+
+/**
* X509Certificate represents an SSL certificate, with accessors to
* get the fields of the certificate.
*/
@@ -103,7 +170,8 @@
}
-class _SecureSocket implements SecureSocket {
+class _RawSecureSocket extends Stream<RawSocketEvent>
+ implements RawSecureSocket {
// Status states
static final int NOT_CONNECTED = 200;
static final int HANDSHAKE = 201;
@@ -118,187 +186,234 @@
static final int WRITE_ENCRYPTED = 3;
static final int NUM_BUFFERS = 4;
- _SecureSocket(String this.host,
- int requestedPort,
- String this.certificateName,
- {bool this.is_server,
- Socket this.socket,
- bool this.requestClientCertificate: false,
- bool this.requireClientCertificate: false,
- bool this.sendClientCertificate: false})
- : secureFilter = new _SecureFilter() {
- // Throw an ArgumentError if any field is invalid.
+ RawSocket _socket;
+ final Completer<_RawSecureSocket> _handshakeComplete =
+ new Completer<_RawSecureSocket>();
+ StreamController<RawSocketEvent> _controller;
+ Stream<RawSocketEvent> _stream;
+ StreamSubscription<RawSocketEvent> _socketSubscription;
+ final String host;
+ final bool is_server;
+ final String certificateName;
+ final bool requestClientCertificate;
+ final bool requireClientCertificate;
+ final bool sendClientCertificate;
+ final Function onBadCertificate;
+
+ var _status = NOT_CONNECTED;
+ bool _writeEventsEnabled = true;
+ bool _readEventsEnabled = true;
+ bool _socketClosedRead = false; // The network socket is closed for reading.
+ bool _socketClosedWrite = false; // The network socket is closed for writing.
+ bool _closedRead = false; // The secure socket has fired an onClosed event.
+ bool _closedWrite = false; // The secure socket has been closed for writing.
+ bool _filterReadEmpty = true; // There is no buffered data to read.
+ bool _filterWriteEmpty = true; // There is no buffered data to be written.
+ bool _connectPending = false;
+ _SecureFilter _secureFilter = new _SecureFilter();
+
+ static Future<_RawSecureSocket> connect(
+ String host,
+ int requestedPort,
+ String certificateName,
+ {bool is_server,
+ RawSocket socket,
+ bool requestClientCertificate: false,
+ bool requireClientCertificate: false,
+ bool sendClientCertificate: false,
+ bool onBadCertificate(X509Certificate certificate)}){
+ return new _RawSecureSocket(host,
+ requestedPort,
+ certificateName,
+ is_server,
+ socket,
+ requestClientCertificate,
+ requireClientCertificate,
+ sendClientCertificate,
+ onBadCertificate)
+ ._handshakeComplete.future;
+ }
+
+ _RawSecureSocket(
+ String this.host,
+ int requestedPort,
+ String this.certificateName,
+ bool this.is_server,
+ RawSocket socket,
+ bool this.requestClientCertificate,
+ bool this.requireClientCertificate,
+ bool this.sendClientCertificate,
+ bool this.onBadCertificate(X509Certificate certificate)) {
+ _controller = new StreamController<RawSocketEvent>(
+ onPauseStateChange: _onPauseStateChange,
+ onSubscriptionStateChange: _onSubscriptionStateChange);
+ _stream = _controller.stream;
+ // Throw an ArgumentError if any field is invalid. After this, all
+ // errors will be reported through the future or the stream.
_verifyFields();
- if (socket == null) {
- socket = new Socket(host, requestedPort);
- }
- socket.onConnect = _secureConnectHandler;
- socket.onData = _secureDataHandler;
- socket.onClosed = _secureCloseHandler;
- socket.onError = _secureErrorHandler;
- secureFilter.init();
- secureFilter.registerHandshakeCompleteCallback(
+ _secureFilter.init();
+ _secureFilter.registerHandshakeCompleteCallback(
_secureHandshakeCompleteHandler);
+ if (onBadCertificate != null) {
+ _secureFilter.registerBadCertificateCallback(onBadCertificate);
+ }
+ var futureSocket;
+ if (socket == null) {
+ futureSocket = RawSocket.connect(host, requestedPort);
+ } else {
+ futureSocket = new Future.immediate(socket);
+ }
+ futureSocket.then((rawSocket) {
+ rawSocket.writeEventsEnabled = false;
+ _socket = rawSocket;
+ _socketSubscription = _socket.listen(_eventDispatcher,
+ onError: _errorHandler,
+ onDone: _doneHandler);
+ _connectPending = true;
+ _secureFilter.connect(host,
+ port,
+ is_server,
+ certificateName,
+ requestClientCertificate ||
+ requireClientCertificate,
+ requireClientCertificate,
+ sendClientCertificate);
+ _status = HANDSHAKE;
+ _secureHandshake();
+ })
+ .catchError((error) {
+ _handshakeComplete.completeError(error);
+ close();
+ });
+ }
+
+ StreamSubscription listen(void onData(RawSocketEvent data),
+ {void onError(AsyncError error),
+ void onDone(),
+ bool unsubscribeOnError}) {
+ if (_writeEventsEnabled) {
+ _writeEventsEnabled = false;
+ _controller.add(RawSocketEvent.WRITE);
+ }
+ return _stream.listen(onData,
+ onError: onError,
+ onDone: onDone,
+ unsubscribeOnError: unsubscribeOnError);
}
void _verifyFields() {
- if (host is! String) throw new ArgumentError(
- "SecureSocket constructor: host is not a String");
assert(is_server is bool);
- assert(socket == null || socket is Socket);
- if (certificateName != null && certificateName is! String) {
+ assert(_socket == null || _socket is RawSocket);
+ if (host is! String) {
throw new ArgumentError(
- "SecureSocket constructor: certificateName is not null or a String");
+ "RawSecureSocket constructor: host is not a String");
+ }
+ if (certificateName != null && certificateName is! String) {
+ throw new ArgumentError("certificateName is not null or a String");
}
if (certificateName == null && is_server) {
- throw new ArgumentError(
- "SecureSocket constructor: certificateName is null on a server");
+ throw new ArgumentError("certificateName is null on a server");
}
if (requestClientCertificate is! bool) {
- throw new ArgumentError(
- "SecureSocket constructor: requestClientCertificate is not a bool");
+ throw new ArgumentError("requestClientCertificate is not a bool");
}
if (requireClientCertificate is! bool) {
- throw new ArgumentError(
- "SecureSocket constructor: requireClientCertificate is not a bool");
+ throw new ArgumentError("requireClientCertificate is not a bool");
}
if (sendClientCertificate is! bool) {
- throw new ArgumentError(
- "SecureSocket constructor: sendClientCertificate is not a bool");
+ throw new ArgumentError("sendClientCertificate is not a bool");
}
- }
-
- int get port => socket.port;
-
- String get remoteHost => socket.remoteHost;
-
- int get remotePort => socket.remotePort;
-
- void set onClosed(void callback()) {
- if (_inputStream != null && callback != null) {
- throw new StreamException(
- "Cannot set close handler when input stream is used");
+ if (onBadCertificate != null && onBadCertificate is! Function) {
+ throw new ArgumentError("onBadCertificate is not null or a Function");
}
- _onClosed = callback;
- }
+ }
- void set _onClosed(void callback()) {
- _socketCloseHandler = callback;
- }
+ int get port => _socket.port;
- void set onConnect(void callback()) {
- if (_status == CONNECTED || _status == CLOSED) {
- throw new StreamException(
- "Cannot set connect handler when already connected");
- }
- _onConnect = callback;
- }
+ String get remoteHost => _socket.remoteHost;
- void set _onConnect(void callback()) {
- _socketConnectHandler = callback;
- }
-
- void set onData(void callback()) {
- if (_inputStream != null && callback != null) {
- throw new StreamException(
- "Cannot set data handler when input stream is used");
- }
- _onData = callback;
- }
-
- void set _onData(void callback()) {
- _socketDataHandler = callback;
- }
-
- void set onError(void callback(e)) {
- _socketErrorHandler = callback;
- }
-
- void set onWrite(void callback()) {
- if (_outputStream != null && callback != null) {
- throw new StreamException(
- "Cannot set write handler when output stream is used");
- }
- _onWrite = callback;
- }
-
- void set _onWrite(void callback()) {
- _socketWriteHandler = callback;
- // Reset the one-shot onWrite handler.
- socket.onWrite = _secureWriteHandler;
- }
-
- void set onBadCertificate(bool callback(X509Certificate certificate)) {
- if (callback is! Function && callback != null) {
- throw new SocketIOException(
- "Callback provided to onBadCertificate is not a function or null");
- }
- secureFilter.registerBadCertificateCallback(callback);
- }
-
- InputStream get inputStream {
- if (_inputStream == null) {
- if (_socketDataHandler != null || _socketCloseHandler != null) {
- throw new StreamException(
- "Cannot get input stream when socket handlers are used");
- }
- _inputStream = new _SocketInputStream(this);
- }
- return _inputStream;
- }
-
- OutputStream get outputStream {
- if (_outputStream == null) {
- if (_socketWriteHandler != null) {
- throw new StreamException(
- "Cannot get output stream when socket write handler is used");
- }
- _outputStream = new _SocketOutputStream(this);
- }
- return _outputStream;
- }
+ int get remotePort => _socket.remotePort;
int available() {
- throw new UnimplementedError("SecureSocket.available not implemented yet");
+ if (_status != CONNECTED) return 0;
+ _readEncryptedData();
+ return _secureFilter.buffers[READ_PLAINTEXT].length;
}
- void close([bool halfClose = false]) {
- if (_status == CLOSED) return;
- if (halfClose) {
+ void close() {
+ _closedWrite = true;
+ _closedRead = true;
+ if (_socket != null) {
+ _socket.close();
+ }
+ _socketClosedWrite = true;
+ _socketClosedRead = true;
+ if (_secureFilter != null) {
+ _secureFilter.destroy();
+ _secureFilter = null;
+ }
+ if (_socketSubscription != null) {
+ _socketSubscription.cancel();
+ }
+ _controller.close();
+ _status = CLOSED;
+ }
+
+ void shutdown(SocketDirection direction) {
+ if (direction == SocketDirection.BOTH) {
+ close();
+ } else if (direction == SocketDirection.SEND) {
_closedWrite = true;
_writeEncryptedData();
if (_filterWriteEmpty) {
- socket.close(true);
+ _socket.shutdown(SocketDirection.SEND);
_socketClosedWrite = true;
if (_closedRead) {
- close(false);
+ close();
}
}
- } else {
- _closedWrite = true;
+ } else if (direction == SocketDirection.RECEIVE) {
_closedRead = true;
- socket.close(false);
- _socketClosedWrite = true;
_socketClosedRead = true;
- secureFilter.destroy();
- secureFilter = null;
- if (scheduledDataEvent != null) {
- scheduledDataEvent.cancel();
+ _socket.shutdown(SocketDirection.RECEIVE);
+ if (_socketClosedWrite) {
+ close();
}
- _status = CLOSED;
}
}
- void _closeWrite() => close(true);
+ bool get writeEventsEnabled => _writeEventsEnabled;
+
+ void set writeEventsEnabled(bool value) {
+ if (value &&
+ _secureFilter != null &&
+ _secureFilter.buffers[WRITE_PLAINTEXT].free > 0) {
+ new Timer(0, (_) => _controller.add(RawSocketEvent.WRITE));
+ } else {
+ _writeEventsEnabled = value;
+ }
+ }
+
+ bool get readEventsEnabled => _readEventsEnabled;
+
+ void set readEventsEnabled(bool value) {
+ _readEventsEnabled = value;
+ if (_socketClosedRead) {
+ if (value) {
+ // We have no underlying socket to set off read events.
+ new Timer(0, (_) => _readHandler());
+ }
+ }
+ }
List<int> read([int len]) {
if (_closedRead) {
throw new SocketIOException("Reading from a closed socket");
}
if (_status != CONNECTED) {
- return new List<int>(0);
+ return null;
}
- var buffer = secureFilter.buffers[READ_PLAINTEXT];
+ var buffer = _secureFilter.buffers[READ_PLAINTEXT];
_readEncryptedData();
int toRead = buffer.length;
if (len != null) {
@@ -313,50 +428,41 @@
List<int> result = (toRead == 0) ? null :
buffer.data.getRange(buffer.start, toRead);
buffer.advanceStart(toRead);
- _setHandlersAfterRead();
- return result;
- }
- int readList(List<int> data, int offset, int bytes) {
- if (_closedRead) {
- throw new SocketIOException("Reading from a closed socket");
- }
- if (offset < 0 || bytes < 0 || offset + bytes > data.length) {
- throw new ArgumentError(
- "Invalid offset or bytes in SecureSocket.readList");
- }
- if (_status != CONNECTED && _status != CLOSED) {
- return 0;
+ // Set up a read event if the filter still has data.
+ if (!_filterReadEmpty) {
+ new Timer(0, (_) => _readHandler());
}
- int bytesRead = 0;
- var buffer = secureFilter.buffers[READ_PLAINTEXT];
- // TODO(whesse): Currently this fails if the if is turned into a while loop.
- // Fix it so that it can loop and read more than one buffer's worth of data.
- if (bytes > bytesRead) {
- _readEncryptedData();
- if (buffer.length > 0) {
- int toRead = min(bytes - bytesRead, buffer.length);
- data.setRange(offset, toRead, buffer.data, buffer.start);
- buffer.advanceStart(toRead);
- bytesRead += toRead;
- offset += toRead;
+ if (_socketClosedRead) { // An onClose event is pending.
+ // _closedRead is false, since we are in a read call.
+ if (!_filterReadEmpty) {
+ // _filterReadEmpty may be out of date since read empties
+ // the plaintext buffer after calling _readEncryptedData.
+ // TODO(whesse): Fix this as part of fixing read.
+ _readEncryptedData();
+ }
+ if (_filterReadEmpty) {
+ // This can't be an else clause: the value of _filterReadEmpty changes.
+ // This must be asynchronous, because we are in a read call.
+ new Timer(0, (_) => _closeHandler());
}
}
- _setHandlersAfterRead();
- return bytesRead;
+ return result;
}
// Write the data to the socket, and flush it as much as possible
// until it would block. If the write would block, _writeEncryptedData sets
// up handlers to flush the pipeline when possible.
- int writeList(List<int> data, int offset, int bytes) {
+ int write(List<int> data, [int offset, int bytes]) {
if (_closedWrite) {
- throw new SocketIOException("Writing to a closed socket");
+ _controller.signalError(new AsyncError(new SocketIOException(
+ "Writing to a closed socket")));
+ return 0;
}
if (_status != CONNECTED) return 0;
- var buffer = secureFilter.buffers[WRITE_PLAINTEXT];
+ var buffer = _secureFilter.buffers[WRITE_PLAINTEXT];
if (bytes > buffer.free) {
bytes = buffer.free;
}
@@ -368,162 +474,182 @@
return bytes;
}
- X509Certificate get peerCertificate => secureFilter.peerCertificate;
+ X509Certificate get peerCertificate => _secureFilter.peerCertificate;
- void _secureConnectHandler() {
- _connectPending = true;
- secureFilter.connect(host,
- port,
- is_server,
- certificateName,
- requestClientCertificate || requireClientCertificate,
- requireClientCertificate,
- sendClientCertificate);
- _status = HANDSHAKE;
- _secureHandshake();
- }
-
- void _secureWriteHandler() {
+ void _writeHandler() {
+ if (_status == CLOSED) return;
_writeEncryptedData();
if (_filterWriteEmpty && _closedWrite && !_socketClosedWrite) {
- close(true);
+ // Close _socket for write, by calling shutdown(), to avoid cloning the
+ // socket closing code in shutdown().
+ shutdown(SocketDirection.SEND);
}
if (_status == HANDSHAKE) {
- _secureHandshake();
- } else if (_status == CONNECTED &&
- _socketWriteHandler != null &&
- secureFilter.buffers[WRITE_PLAINTEXT].free > 0) {
- // We must be able to set onWrite from the onWrite callback.
- var handler = _socketWriteHandler;
- // Reset the one-shot handler.
- _socketWriteHandler = null;
- handler();
- }
- }
-
- void _secureDataHandler() {
- if (_status == HANDSHAKE) {
try {
_secureHandshake();
- } catch (e) { _reportError(e, "SecureSocket error"); }
- } else {
+ } catch (e) { _reportError(e, "RawSecureSocket error"); }
+ } else if (_status == CONNECTED &&
+ _writeEventsEnabled &&
+ _secureFilter.buffers[WRITE_PLAINTEXT].free > 0) {
+ // Reset the one-shot handler.
+ _writeEventsEnabled = false;
+ _controller.add(RawSocketEvent.WRITE);
+ }
+ }
+
+ void _eventDispatcher(RawSocketEvent event) {
+ if (event == RawSocketEvent.READ) {
+ _readHandler();
+ } else if (event == RawSocketEvent.WRITE) {
+ _writeHandler();
+ } else if (event == RawSocketEvent.READ_CLOSED) {
+ _closeHandler();
+ }
+ }
+
+ void _readHandler() {
+ if (_status == CLOSED) {
+ return;
+ } else if (_status == HANDSHAKE) {
try {
- _writeEncryptedData(); // TODO(whesse): Removing this causes a failure.
+ _secureHandshake();
+ if (_status != HANDSHAKE) _readHandler();
+ } catch (e) { _reportError(e, "RawSecureSocket error"); }
+ } else {
+ if (_status != CONNECTED) {
+ // Cannot happen.
+ throw new SocketIOException("Internal SocketIO Error");
+ }
+ try {
_readEncryptedData();
- } catch (e) { _reportError(e, "SecureSocket error"); }
+ } catch (e) { _reportError(e, "RawSecureSocket error"); }
if (!_filterReadEmpty) {
- // Call the onData event.
- if (scheduledDataEvent != null) {
- scheduledDataEvent.cancel();
- scheduledDataEvent = null;
- }
- if (_socketDataHandler != null) {
- _socketDataHandler();
+ if (_readEventsEnabled) {
+ if (_secureFilter.buffers[READ_PLAINTEXT].length > 0) {
+ _controller.add(RawSocketEvent.READ);
+ }
+ if (_socketClosedRead) {
+ // Keep firing read events until we are paused or buffer is empty.
+ new Timer(0, (_) => _readHandler());
+ }
}
} else if (_socketClosedRead) {
- _secureCloseHandler();
+ _closeHandler();
}
}
}
- void _secureErrorHandler(e) {
- _reportError(e, 'Error on underlying Socket');
+ void _doneHandler() {
+ if (_filterReadEmpty) {
+ close();
+ }
+ }
+
+ void _errorHandler(e) {
+ _reportError(e, 'Error on underlying RawSocket');
}
void _reportError(error, String message) {
// TODO(whesse): Call _reportError from all internal functions that throw.
var e;
- if (error is SocketIOException) {
+ if (error is AsyncError) {
+ e = error;
+ } else if (error is SocketIOException) {
e = new SocketIOException('$message (${error.message})', error.osError);
} else if (error is OSError) {
e = new SocketIOException(message, error);
} else {
e = new SocketIOException('$message (${error.toString()})', null);
}
- close(false);
- bool reported = false;
- if (_socketErrorHandler != null) {
- reported = true;
- _socketErrorHandler(e);
+ if (_connectPending) {
+ _handshakeComplete.completeError(e);
+ } else {
+ _controller.signalError(e);
}
- if (_inputStream != null) {
- reported = reported || _inputStream._onSocketError(e);
- }
- if (_outputStream != null) {
- reported = reported || _outputStream._onSocketError(e);
- }
- if (!reported) throw e;
+ close();
}
- void _secureCloseHandler() {
- if (_closedRead) return;
- _socketClosedRead = true;
- if (_filterReadEmpty) {
- _closedRead = true;
- if (scheduledDataEvent != null) {
- scheduledDataEvent.cancel();
+ void _closeHandler() {
+ if (_status == CONNECTED) {
+ if (_closedRead) return;
+ _socketClosedRead = true;
+ if (_filterReadEmpty) {
+ _closedRead = true;
+ _controller.add(RawSocketEvent.READ_CLOSED);
+ if (_socketClosedWrite) {
+ close();
+ }
}
- if (_socketCloseHandler != null) {
- _socketCloseHandler();
- }
- if (_socketClosedWrite) {
- close(false);
- }
+ } else if (_status == HANDSHAKE) {
+ _reportError(
+ new SocketIOException('Connection terminated during handshake'),
+ 'handshake error');
}
}
void _secureHandshake() {
_readEncryptedData();
- secureFilter.handshake();
+ _secureFilter.handshake();
_writeEncryptedData();
- if (secureFilter.buffers[WRITE_ENCRYPTED].length > 0) {
- socket.onWrite = _secureWriteHandler;
- }
}
void _secureHandshakeCompleteHandler() {
_status = CONNECTED;
- if (_connectPending && _socketConnectHandler != null) {
+ if (_connectPending) {
_connectPending = false;
- _socketConnectHandler();
- }
- if (_socketWriteHandler != null) {
- socket.onWrite = _secureWriteHandler;
+ // If we complete the future synchronously, user code will run here,
+ // and modify the state of the RawSecureSocket. For example, it
+ // could close the socket, and set _filter to null.
+ new Timer(0, (_) => _handshakeComplete.complete(this));
}
}
- // True if the underlying socket is closed, the filter has been emptied of
- // all data, and the close event has been fired.
- get _closed => _socketClosed;
+ void _onPauseStateChange() {
+ if (!_socketClosedRead || !_socketClosedWrite) {
+ if (_controller.isPaused) {
+ _socketSubscription.pause();
+ } else {
+ _socketSubscription.resume();
+ }
+ }
+ }
+
+ void _onSubscriptionStateChange() {
+ if (_controller.hasSubscribers) {
+ // TODO(ajohnsen): Do something here?
+ }
+ }
void _readEncryptedData() {
// Read from the socket, and push it through the filter as far as
// possible.
- var encrypted = secureFilter.buffers[READ_ENCRYPTED];
- var plaintext = secureFilter.buffers[READ_PLAINTEXT];
+ var encrypted = _secureFilter.buffers[READ_ENCRYPTED];
+ var plaintext = _secureFilter.buffers[READ_PLAINTEXT];
bool progress = true;
while (progress) {
progress = false;
// Do not try to read plaintext from the filter while handshaking.
if ((_status == CONNECTED) && plaintext.free > 0) {
- int bytes = secureFilter.processBuffer(READ_PLAINTEXT);
+ int bytes = _secureFilter.processBuffer(READ_PLAINTEXT);
if (bytes > 0) {
plaintext.length += bytes;
progress = true;
}
}
if (encrypted.length > 0) {
- int bytes = secureFilter.processBuffer(READ_ENCRYPTED);
+ int bytes = _secureFilter.processBuffer(READ_ENCRYPTED);
if (bytes > 0) {
encrypted.advanceStart(bytes);
progress = true;
}
}
- if (!_socketClosedRead) {
- int bytes = socket.readList(encrypted.data,
- encrypted.start + encrypted.length,
- encrypted.free);
- if (bytes > 0) {
+ if (!_socketClosedRead && encrypted.free > 0) {
+ List<int> data = _socket.read(encrypted.free);
+ if (data != null) {
+ int bytes = data.length;
+ encrypted.data.setRange(encrypted.start + encrypted.length,
+ bytes,
+ data);
encrypted.length += bytes;
progress = true;
}
@@ -538,29 +664,29 @@
void _writeEncryptedData() {
if (_socketClosedWrite) return;
- var encrypted = secureFilter.buffers[WRITE_ENCRYPTED];
- var plaintext = secureFilter.buffers[WRITE_PLAINTEXT];
+ var encrypted = _secureFilter.buffers[WRITE_ENCRYPTED];
+ var plaintext = _secureFilter.buffers[WRITE_PLAINTEXT];
while (true) {
if (encrypted.length > 0) {
// Write from the filter to the socket.
- int bytes = socket.writeList(encrypted.data,
- encrypted.start,
- encrypted.length);
- if (bytes == 0) {
+ int bytes = _socket.write(encrypted.data,
+ encrypted.start,
+ encrypted.length);
+ encrypted.advanceStart(bytes);
+ if (encrypted.length > 0) {
// The socket has blocked while we have data to write.
// We must be notified when it becomes unblocked.
- socket.onWrite = _secureWriteHandler;
+ _socket.writeEventsEnabled = true;
_filterWriteEmpty = false;
break;
}
- encrypted.advanceStart(bytes);
} else {
- var plaintext = secureFilter.buffers[WRITE_PLAINTEXT];
+ var plaintext = _secureFilter.buffers[WRITE_PLAINTEXT];
if (plaintext.length > 0) {
- int plaintext_bytes = secureFilter.processBuffer(WRITE_PLAINTEXT);
+ int plaintext_bytes = _secureFilter.processBuffer(WRITE_PLAINTEXT);
plaintext.advanceStart(plaintext_bytes);
}
- int bytes = secureFilter.processBuffer(WRITE_ENCRYPTED);
+ int bytes = _secureFilter.processBuffer(WRITE_ENCRYPTED);
if (bytes <= 0) {
// We know the WRITE_ENCRYPTED buffer is empty, and the
// filter wrote zero bytes to it, so the filter must be empty.
@@ -574,69 +700,6 @@
}
}
}
-
- /* After a read, the onData handler is enabled to fire again.
- * We may also have a close event waiting for the SecureFilter to empty.
- */
- void _setHandlersAfterRead() {
- // If the filter is empty, then we are guaranteed an event when it
- // becomes unblocked. Cancel any _secureDataHandler call.
- // Otherwise, schedule a _secureDataHandler call since there may data
- // available, and this read call enables the data event.
- if (_filterReadEmpty) {
- if (scheduledDataEvent != null) {
- scheduledDataEvent.cancel();
- scheduledDataEvent = null;
- }
- } else if (scheduledDataEvent == null) {
- scheduledDataEvent = Timer.run(_secureDataHandler);
- }
-
- if (_socketClosedRead) { // An onClose event is pending.
- // _closedRead is false, since we are in a read or readList call.
- if (!_filterReadEmpty) {
- // _filterReadEmpty may be out of date since read and readList empty
- // the plaintext buffer after calling _readEncryptedData.
- // TODO(whesse): Fix this as part of fixing read and readList.
- _readEncryptedData();
- }
- if (_filterReadEmpty) {
- // This can't be an else clause: the value of _filterReadEmpty changes.
- // This must be asynchronous, because we are in a read or readList call.
- Timer.run(_secureCloseHandler);
- }
- }
- }
-
- bool get _socketClosed => _closedRead;
-
- // _SecureSocket cannot extend _Socket and use _Socket's factory constructor.
- Socket socket;
- final String host;
- final bool is_server;
- final String certificateName;
- final bool requestClientCertificate;
- final bool requireClientCertificate;
- final bool sendClientCertificate;
-
- var _status = NOT_CONNECTED;
- bool _socketClosedRead = false; // The network socket is closed for reading.
- bool _socketClosedWrite = false; // The network socket is closed for writing.
- bool _closedRead = false; // The secure socket has fired an onClosed event.
- bool _closedWrite = false; // The secure socket has been closed for writing.
- bool _filterReadEmpty = true; // There is no buffered data to read.
- bool _filterWriteEmpty = true; // There is no buffered data to be written.
- _SocketInputStream _inputStream;
- _SocketOutputStream _outputStream;
- bool _connectPending = false;
- Function _socketConnectHandler;
- Function _socketWriteHandler;
- Function _socketDataHandler;
- Function _socketErrorHandler;
- Function _socketCloseHandler;
- Timer scheduledDataEvent;
-
- _SecureFilter secureFilter;
}
diff --git a/sdk/lib/io/socket.dart b/sdk/lib/io/socket.dart
index 84b5e75..b0c04b4 100644
--- a/sdk/lib/io/socket.dart
+++ b/sdk/lib/io/socket.dart
@@ -1,26 +1,31 @@
-// Copyright (c) 2012, 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.
part of dart.io;
-abstract class ServerSocket {
+/**
+ * The RawServerSocket is a server socket, providing a stream of low-level
+ * [RawSocket]s.
+ *
+ * See [RawSocket] for more info.
+ */
+abstract class RawServerSocket implements Stream<RawSocket> {
/**
- * Constructs a new server socket, binds it to a given address and port,
- * and listens on it.
+ * Returns a future for a [:RawServerSocket:]. When the future
+ * completes the server socket is bound to the given [address] and
+ * [port] and has started listening on it.
+ *
+ * If [port] has the value [:0:] (the default) an ephemeral port will
+ * be chosen by the system. The actual port used can be retrieved
+ * using the [:port:] getter.
+ *
+ * If [backlog] has the value of [:0:] a reasonable value will be
+ * chosen by the system.
*/
- external factory ServerSocket(String bindAddress, int port, int backlog);
-
- /**
- * The connection handler gets called when there is a new incoming
- * connection on the socket.
- */
- void set onConnection(void callback(Socket connection));
-
- /**
- * The error handler gets called when a socket error occurs.
- */
- void set onError(void callback(e));
+ external static Future<RawServerSocket> bind([String address = "127.0.0.1",
+ int port = 0,
+ int backlog = 0]);
/**
* Returns the port used by this socket.
@@ -34,13 +39,79 @@
}
-abstract class Socket {
+/**
+ * The [ServerSocket] is server socket, providing a stream of high-level
+ * [Socket]s.
+ *
+ * See [Socket] for more info.
+ */
+abstract class ServerSocket implements Stream<Socket> {
/**
- * Constructs a new socket and initiate connecting it to the given
- * host on the given port. The returned socket is not yet connected
- * but ready for registration of callbacks.
+ * Returns a future for a [:ServerSocket:]. When the future
+ * completes the server socket is bound to the given [address] and
+ * [port] and has started listening on it.
+ *
+ * If [port] has the value [:0:] (the default) an ephemeral port will
+ * be chosen by the system. The actual port used can be retrieved
+ * using the [port] getter.
+ *
+ * If [backlog] has the value of [:0:] a reasonable value will be
+ * chosen by the system.
*/
- external factory Socket(String host, int port);
+ external static Future<ServerSocket> bind([String address = "127.0.0.1",
+ int port = 0,
+ int backlog = 0]);
+
+ /**
+ * Returns the port used by this socket.
+ */
+ int get port;
+
+ /**
+ * Closes the socket.
+ */
+ void close();
+}
+
+/**
+ * The [SocketDirection] is used as a parameter to [Socket.close] and
+ * [RawSocket.close] to close a socket in the specified direction(s).
+ */
+class SocketDirection {
+ static const SocketDirection RECEIVE = const SocketDirection._(0);
+ static const SocketDirection SEND = const SocketDirection._(1);
+ static const SocketDirection BOTH = const SocketDirection._(2);
+ const SocketDirection._(this._value);
+ final _value;
+}
+
+/**
+ * Events for the [RawSocket].
+ */
+class RawSocketEvent {
+ static const RawSocketEvent READ = const RawSocketEvent._(0);
+ static const RawSocketEvent WRITE = const RawSocketEvent._(1);
+ static const RawSocketEvent READ_CLOSED = const RawSocketEvent._(2);
+ const RawSocketEvent._(this._value);
+ final int _value;
+ String toString() {
+ return ['RawSocketEvent:READ',
+ 'RawSocketEvent:WRITE',
+ 'RawSocketEvent:READ_CLOSED'][_value];
+ }
+}
+
+/**
+ * The [RawSocket] is a low-level interface to a socket, exposing the raw
+ * events signaled by the system. It's a [Stream] of [RawSocketEvent]s.
+ */
+abstract class RawSocket implements Stream<RawSocketEvent> {
+ /**
+ * Creats a new socket connection to the host and port and returns a [Future]
+ * that will complete with either a [RawSocket] once connected or an error
+ * if the host-lookup or connection failed.
+ */
+ external static Future<RawSocket> connect(String host, int port);
/**
* Returns the number of received and non-read bytes in the socket that
@@ -58,61 +129,12 @@
List<int> read([int len]);
/**
- * Reads up to [count] bytes of data from the socket and stores them into
- * buffer after buffer offset [offset]. The number of successfully read
- * bytes is returned. This function is non-blocking and will only read data
- * if data is available.
- */
- int readList(List<int> buffer, int offset, int count);
-
- /**
* Writes up to [count] bytes of the buffer from [offset] buffer offset to
* the socket. The number of successfully written bytes is returned. This
* function is non-blocking and will only write data if buffer space is
* available in the socket.
*/
- int writeList(List<int> buffer, int offset, int count);
-
- /**
- * The connect handler gets called when connection to a given host
- * succeeded.
- */
- void set onConnect(void callback());
-
- /**
- * The data handler gets called when data becomes available at the socket.
- */
- void set onData(void callback());
-
- /**
- * The write handler gets called once when the socket becomes
- * available for writing. Then the handler is automatically reset to null.
- * This handler is mainly used when writeList has reported an incomplete
- * write, to schedule writing the remaining data to the socket.
- */
- void set onWrite(void callback());
-
- /**
- * The close handler gets called when a the last byte have been read
- * from a socket. At this point the socket might still be open for
- * writing for sending more data.
- */
- void set onClosed(void callback());
-
- /**
- * The error handler gets called when a socket error occurs.
- */
- void set onError(void callback(e));
-
- /**
- * Returns input stream to the socket.
- */
- InputStream get inputStream;
-
- /**
- * Returns output stream of the socket.
- */
- OutputStream get outputStream;
+ int write(List<int> buffer, [int offset, int count]);
/**
* Returns the port used by this socket.
@@ -131,12 +153,61 @@
/**
* Closes the socket. Calling [close] will never throw an exception
- * and calling it several times is supported. If [halfClose] is true
- * the socket will only be closed for writing and it might still be
- * possible to read data. Calling [close] will not trigger a call to
- * [onClosed].
+ * and calling it several times is supported. Calling [close] can result in
+ * a [RawSocketEvent.READ_CLOSED] event.
*/
- void close([bool halfClose = false]);
+ void close();
+
+ /**
+ * Shutdown the socket in the [direction]. Calling [shutdown] will never
+ * throw an exception and calling it several times is supported. Calling
+ * shutdown with either [SocketDirection.BOTH] or [SocketDirection.RECEIVE]
+ * can result in a [RawSocketEvent.READ_CLOSED] event.
+ */
+ void shutdown(SocketDirection direction);
+
+ /**
+ * Set or get, if the [RawSocket] should listen for [RawSocketEvent.READ]
+ * events. Default is [true].
+ */
+ bool readEventsEnabled;
+
+ /**
+ * Set or get, if the [RawSocket] should listen for [RawSocketEvent.WRITE]
+ * events. Default is [true].
+ * This is a one-shot listener, and writeEventsEnabled must be set
+ * to true again to receive another write event.
+ */
+ bool writeEventsEnabled;
+}
+
+/**
+ * A high-level class for communicating over a TCP socket. The [Socket] exposes
+ * both a [Stream] and a [IOSink] interface, making it ideal for
+ * using together with other [Stream]s.
+ */
+abstract class Socket implements Stream<List<int>>,
+ IOSink<Socket> {
+ /**
+ * Creats a new socket connection to the host and port and returns a [Future]
+ * that will complete with either a [RawSocket] once connected or an error
+ * if the host-lookup or connection failed.
+ */
+ external static Future<Socket> connect(String host, int port);
+
+ /**
+ * Destroy the socket in both directions. Calling [destroy] will make the
+ * send a close event on the stream and will no longer react on data being
+ * piped to it.
+ *
+ * Call [close](inherited by [IOSink]) to only close the [Socket]
+ * for sending data.
+ */
+ void destroy();
+
+ int get port;
+ String get remoteHost;
+ int get remotePort;
}
diff --git a/sdk/lib/io/socket_stream_impl.dart b/sdk/lib/io/socket_stream_impl.dart
deleted file mode 100644
index d156bd7..0000000
--- a/sdk/lib/io/socket_stream_impl.dart
+++ /dev/null
@@ -1,232 +0,0 @@
-// 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.
-
-part of dart.io;
-
-class _SocketInputStream implements InputStream {
- _SocketInputStream(Socket socket) : _socket = socket {
- if (_socket._closed) _closed = true;
- _socket.onClosed = _onClosed;
- }
-
- List<int> read([int len]) {
- return _socket.read(len);
- }
-
- int readInto(List<int> buffer, [int offset = 0, int len]) {
- if (_closed) return null;
- if (len == null) len = buffer.length;
- if (offset < 0) throw new StreamException("Illegal offset $offset");
- if (len < 0) throw new StreamException("Illegal length $len");
- return _socket.readList(buffer, offset, len);
- }
-
- int available() => _socket.available();
-
- void pipe(OutputStream output, {bool close: true}) {
- _pipe(this, output, close: close);
- }
-
- void close() {
- if (!_closed) {
- _socket.close();
- }
- }
-
- bool get closed => _closed;
-
- void set onData(void callback()) {
- _socket._onData = callback;
- }
-
- void set onClosed(void callback()) {
- _clientCloseHandler = callback;
- _socket._onClosed = _onClosed;
- }
-
- void set onError(void callback(e)) {
- _onError = callback;
- }
-
- void _onClosed() {
- _closed = true;
- if (_clientCloseHandler != null) {
- _clientCloseHandler();
- }
- }
-
- bool _onSocketError(e) {
- close();
- if (_onError != null) {
- _onError(e);
- return true;
- } else {
- return false;
- }
- }
-
- Socket _socket;
- bool _closed = false;
- Function _clientCloseHandler;
- Function _onError;
-}
-
-
-class _SocketOutputStream
- extends _BaseOutputStream implements OutputStream {
- _SocketOutputStream(Socket socket)
- : _socket = socket, _pendingWrites = new _BufferList();
-
- bool write(List<int> buffer, [bool copyBuffer = true]) {
- return _write(buffer, 0, buffer.length, copyBuffer);
- }
-
- bool writeFrom(List<int> buffer, [int offset = 0, int len]) {
- return _write(
- buffer, offset, (len == null) ? buffer.length - offset : len, true);
- }
-
- void flush() {
- // Nothing to do on a socket output stream.
- }
-
- void close() {
- if (_closing) return;
- _closing = true;
- if (!_pendingWrites.isEmpty) {
- // Mark the socket for close when all data is written.
- _socket._onWrite = _onWrite;
- } else {
- // Close the socket for writing.
- _socket._closeWrite();
- _closed = true;
- // Invoke the callback asynchronously.
- Timer.run(() {
- if (_onClosed != null) _onClosed();
- });
- }
- }
-
- void destroy() {
- _socket._onWrite = null;
- _pendingWrites.clear();
- _socket.close();
- _closed = true;
- }
-
- bool get closed => _closed;
-
- void set onNoPendingWrites(void callback()) {
- _onNoPendingWrites = callback;
- if (_onNoPendingWrites != null) {
- _socket._onWrite = _onWrite;
- }
- }
-
- void set onClosed(void callback()) {
- _onClosed = callback;
- }
-
- bool _write(List<int> buffer, int offset, int len, bool copyBuffer) {
- if (_closing || _closed) {
- if (_error) return false;
- _error = true;
- var e = new StreamException.streamClosed();
- if (_onError != null) {
- _onError(e);
- return false;
- } else {
- throw e;
- }
- }
- int bytesWritten = 0;
- if (_pendingWrites.isEmpty) {
- // If nothing is buffered write as much as possible and buffer
- // the rest.
- try {
- bytesWritten = _socket.writeList(buffer, offset, len);
- if (bytesWritten == len) return true;
- } catch (e) {
- if (_error) return false;
- _error = true;
- if (_onError != null) {
- _onError(e);
- return false;
- } else {
- throw e;
- }
- }
- }
-
- // Place remaining data on the pending writes queue.
- int notWrittenOffset = offset + bytesWritten;
- if (copyBuffer) {
- List<int> newBuffer =
- buffer.getRange(notWrittenOffset, len - bytesWritten);
- _pendingWrites.add(newBuffer);
- } else {
- assert(offset + len == buffer.length);
- _pendingWrites.add(buffer, notWrittenOffset);
- }
- _socket._onWrite = _onWrite;
- return false;
- }
-
- void _onWrite() {
- // Write as much buffered data to the socket as possible.
- while (!_pendingWrites.isEmpty) {
- List<int> buffer = _pendingWrites.first;
- int offset = _pendingWrites.index;
- int bytesToWrite = buffer.length - offset;
- int bytesWritten;
- try {
- bytesWritten = _socket.writeList(buffer, offset, bytesToWrite);
- } catch (e) {
- _pendingWrites.clear();
- if (_onError != null) _onError(e);
- return;
- }
- _pendingWrites.removeBytes(bytesWritten);
- if (bytesWritten < bytesToWrite) {
- _socket._onWrite = _onWrite;
- return;
- }
- }
-
- // All buffered data was written.
- if (_closing) {
- _socket._closeWrite();
- _closed = true;
- if (_onClosed != null) {
- _onClosed();
- }
- } else {
- if (_onNoPendingWrites != null) _onNoPendingWrites();
- }
- if (_onNoPendingWrites == null) {
- _socket._onWrite = null;
- } else {
- _socket._onWrite = _onWrite;
- }
- }
-
- bool _onSocketError(e) {
- destroy();
- if (_error) return true;
- if (_onError != null) {
- _onError(e);
- return true;
- } else {
- throw e;
- }
- }
-
- Socket _socket;
- _BufferList _pendingWrites;
- Function _onNoPendingWrites;
- Function _onClosed;
- bool _closing = false;
- bool _closed = false;
- bool _error = false;
-}
diff --git a/sdk/lib/io/stdio.dart b/sdk/lib/io/stdio.dart
index ae66488..eb7119c 100644
--- a/sdk/lib/io/stdio.dart
+++ b/sdk/lib/io/stdio.dart
@@ -21,12 +21,12 @@
}
-InputStream _stdin;
-OutputStream _stdout;
-OutputStream _stderr;
+Stream<List<int>> _stdin;
+IOSink _stdout;
+IOSink _stderr;
-InputStream get stdin {
+Stream<List<int>> get stdin {
if (_stdin == null) {
_stdin = _StdIOUtils._getStdioInputStream();
}
@@ -34,7 +34,7 @@
}
-OutputStream get stdout {
+IOSink get stdout {
if (_stdout == null) {
_stdout = _StdIOUtils._getStdioOutputStream(1);
}
@@ -42,7 +42,7 @@
}
-OutputStream get stderr {
+IOSink get stderr {
if (_stderr == null) {
_stderr = _StdIOUtils._getStdioOutputStream(2);
}
@@ -51,23 +51,31 @@
StdioType stdioType(object) {
- if (object is _FileOutputStream || object is _FileInputStream) {
+ if (object is _FileStream) {
return StdioType.FILE;
}
- if (object is !_SocketOutputStream && object is !_SocketInputStream) {
- return StdioType.OTHER;
+ if (object is Socket) {
+ switch (_StdIOUtils._socketType(object._nativeSocket)) {
+ case _STDIO_HANDLE_TYPE_TERMINAL: return StdioType.TERMINAL;
+ case _STDIO_HANDLE_TYPE_PIPE: return StdioType.PIPE;
+ case _STDIO_HANDLE_TYPE_FILE: return StdioType.FILE;
+ }
}
- switch (_StdIOUtils._socketType(object._socket)) {
- case _STDIO_HANDLE_TYPE_TERMINAL: return StdioType.TERMINAL;
- case _STDIO_HANDLE_TYPE_PIPE: return StdioType.PIPE;
- case _STDIO_HANDLE_TYPE_FILE: return StdioType.FILE;
- default: return StdioType.OTHER;
+ if (object is IOSink) {
+ try {
+ if (object._sink.target is _FileStreamConsumer) {
+ return StdioType.FILE;
+ }
+ } catch (e) {
+ // Only the interface implemented, _sink not available.
+ }
}
+ return StdioType.OTHER;
}
class _StdIOUtils {
- external static OutputStream _getStdioOutputStream(int fd);
- external static InputStream _getStdioInputStream();
- external static int _socketType(Socket socket);
+ external static IOSink _getStdioOutputStream(int fd);
+ external static Stream<List<int>> _getStdioInputStream();
+ external static int _socketType(nativeSocket);
}
diff --git a/sdk/lib/io/stream_util.dart b/sdk/lib/io/stream_util.dart
deleted file mode 100644
index b68b8fd..0000000
--- a/sdk/lib/io/stream_util.dart
+++ /dev/null
@@ -1,211 +0,0 @@
-// 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.
-
-part of dart.io;
-
-abstract class _BaseDataInputStream {
- int available();
-
- List<int> read([int len]) {
- if (_closeCallbackCalled || _scheduledCloseCallback != null) return null;
- int bytesToRead = available();
- if (bytesToRead == 0) {
- _checkScheduleCallbacks();
- return null;
- }
- if (len != null) {
- if (len <= 0) {
- throw new StreamException("Illegal length $len");
- } else if (bytesToRead > len) {
- bytesToRead = len;
- }
- }
- return _read(bytesToRead);
- }
-
- int readInto(List<int> buffer, [int offset = 0, int len]) {
- if (_closeCallbackCalled || _scheduledCloseCallback != null) return 0;
- if (len == null) len = buffer.length;
- if (offset < 0) throw new StreamException("Illegal offset $offset");
- if (len < 0) throw new StreamException("Illegal length $len");
- int bytesToRead = min(len, available());
- return _readInto(buffer, offset, bytesToRead);
- }
-
- void pipe(OutputStream output, {bool close: true}) {
- _pipe(this, output, close: close);
- }
-
- void close() {
- _cancelScheduledDataCallback();
- _close();
- _checkScheduleCallbacks();
- }
-
- bool get closed => _closeCallbackCalled;
-
- void set onData(void callback()) {
- _clientDataHandler = callback;
- _checkScheduleCallbacks();
- }
-
- void set onClosed(void callback()) {
- _clientCloseHandler = callback;
- _checkScheduleCallbacks();
- }
-
- void set onError(void callback(e)) {
- _clientErrorHandler = callback;
- }
-
- void _reportError(e) {
- if (_clientErrorHandler != null) {
- _clientErrorHandler(e);
- } else {
- throw e;
- }
- }
-
- List<int> _read(int bytesToRead);
-
- void _dataReceived() {
- // More data has been received asynchronously. Perform the data
- // handler callback now.
- _cancelScheduledDataCallback();
- if (_clientDataHandler != null) {
- _clientDataHandler();
- }
- _checkScheduleCallbacks();
- }
-
- void _closeReceived() {
- // Close indication has been received asynchronously. Perform the
- // close callback now if all data has been delivered.
- _streamMarkedClosed = true;
- if (available() == 0) {
- _closeCallbackCalled = true;
- if (_clientCloseHandler != null) _clientCloseHandler();
- } else {
- _checkScheduleCallbacks();
- }
- }
-
- void _cancelScheduledDataCallback() {
- if (_scheduledDataCallback != null) {
- _scheduledDataCallback.cancel();
- _scheduledDataCallback = null;
- }
- }
-
- void _checkScheduleCallbacks() {
- void issueDataCallback() {
- _scheduledDataCallback = null;
- if (_clientDataHandler != null) {
- _clientDataHandler();
- _checkScheduleCallbacks();
- }
- }
-
- void issueCloseCallback() {
- _scheduledCloseCallback = null;
- _closeCallbackCalled = true;
- if (_clientCloseHandler != null) _clientCloseHandler();
- }
-
- // Schedule data callback if there is more data to read. Schedule
- // close callback once when all data has been read. Only schedule
- // a new callback if the previous one has actually been called.
- if (!_closeCallbackCalled) {
- if (available() > 0) {
- if (_scheduledDataCallback == null) {
- _scheduledDataCallback = Timer.run(issueDataCallback);
- }
- } else if (_streamMarkedClosed && _scheduledCloseCallback == null) {
- _cancelScheduledDataCallback();
- _close();
- _scheduledCloseCallback = Timer.run(issueCloseCallback);
- }
- }
- }
-
- // When this is set to true the stream is marked closed. When a
- // stream is marked closed no more data can arrive and the value
- // from available is now all remaining data. If this is true and the
- // value of available is zero the close handler is called.
- bool _streamMarkedClosed = false;
-
- // When this is set to true the close callback has been called and
- // the stream is fully closed.
- bool _closeCallbackCalled = false;
-
- Timer _scheduledDataCallback;
- Timer _scheduledCloseCallback;
- Function _clientDataHandler;
- Function _clientCloseHandler;
- Function _clientErrorHandler;
-}
-
-
-void _pipe(InputStream input, OutputStream output, {bool close}) {
- Function pipeDataHandler;
- Function pipeCloseHandler;
- Function pipeNoPendingWriteHandler;
-
- Function _inputCloseHandler;
-
- pipeDataHandler = () {
- List<int> data;
- while ((data = input.read()) != null) {
- if (!output.write(data)) {
- input.onData = null;
- output.onNoPendingWrites = pipeNoPendingWriteHandler;
- break;
- }
- }
- };
-
- pipeCloseHandler = () {
- if (close) output.close();
- if (_inputCloseHandler != null) {
- _inputCloseHandler();
- }
- };
-
- pipeNoPendingWriteHandler = () {
- input.onData = pipeDataHandler;
- output.onNoPendingWrites = null;
- };
-
- _inputCloseHandler = input._clientCloseHandler;
- input.onData = pipeDataHandler;
- input.onClosed = pipeCloseHandler;
- output.onNoPendingWrites = null;
-}
-
-
-class _BaseOutputStream {
- bool writeString(String string, [Encoding encoding = Encoding.UTF_8]) {
- if (string.length > 0) {
- // Encode and write data.
- _StringEncoder encoder = _StringEncoders.encoder(encoding);
- List<int> data = encoder.encodeString(string);
- return write(data, false);
- }
- return true;
- }
-
- void set onError(void callback(e)) {
- _onError = callback;
- }
-
- void _reportError(e) {
- if (_onError != null) {
- _onError(e);
- } else {
- throw e;
- }
- }
-
- Function _onError;
-}
diff --git a/sdk/lib/io/string_stream.dart b/sdk/lib/io/string_stream.dart
deleted file mode 100644
index cd38e2c..0000000
--- a/sdk/lib/io/string_stream.dart
+++ /dev/null
@@ -1,594 +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.
-
-part of dart.io;
-
-// Interface for decoders decoding binary data into string data. The
-// decoder keeps track of line breaks during decoding.
-abstract class _StringDecoder {
- // Add more binary data to be decoded. The ownership of the buffer
- // is transfered to the decoder and the caller most not modify it any more.
- int write(List<int> buffer);
-
- void done();
-
- // Returns whether any decoded data is available.
- bool get isEmpty;
-
- // Returns the number of available decoded characters.
- int available();
-
- // Get the number of line breaks present in the current decoded
- // data.
- int get lineBreaks;
-
- // Get up to [len] characters of string data decoded since the last
- // call to [decode] or [decodeLine]. Returns null if no decoded data
- // is available. If [len] is not specified all decoded characters
- // are returned.
- String decoded([int len]);
-
- // Get the string data decoded since the last call to [decode] or
- // [decodeLine] up to the next line break present. Returns null if
- // no line break is present. The line break character sequence is
- // discarded.
- String get decodedLine;
-
- // Set the handler that will be called if an error ocurs while decoding.
- void set onError(Function callback);
-}
-
-
-class _StringDecoders {
- static _StringDecoder decoder(Encoding encoding) {
- if (encoding == Encoding.UTF_8) {
- return new _UTF8Decoder();
- } else if (encoding == Encoding.ISO_8859_1) {
- return new _Latin1Decoder();
- } else if (encoding == Encoding.ASCII) {
- return new _AsciiDecoder();
- } else if (encoding == Encoding.SYSTEM) {
- if (Platform.operatingSystem == 'windows') {
- return new _WindowsCodePageDecoder();
- }
- return new _UTF8Decoder();
- } else {
- if (encoding is Encoding) {
- throw new StreamException("Unsupported encoding ${encoding.name}");
- } else {
- throw new StreamException("Unsupported encoding ${encoding}");
- }
- }
- }
-}
-
-
-class DecoderException implements Exception {
- const DecoderException([String this.message]);
- String toString() => "DecoderException: $message";
- final String message;
-}
-
-
-// Utility class for decoding UTF-8 from data delivered as a stream of
-// bytes.
-abstract class _StringDecoderBase implements _StringDecoder {
- _StringDecoderBase()
- : _bufferList = new _BufferList(),
- _result = new List<int>(),
- _lineBreakEnds = new Queue<int>();
-
- int write(List<int> buffer) {
- _bufferList.add(buffer);
- // Decode as many bytes into characters as possible.
- while (_bufferList.length > 0) {
- if (!_processNext()) {
- break;
- }
- }
- return buffer.length;
- }
-
- void done() { }
-
- bool get isEmpty => _result.isEmpty;
-
- int get lineBreaks => _lineBreaks;
-
- String decoded([int length]) {
- if (isEmpty) return null;
-
- List<int> result;
- if (length != null && length < available()) {
- result = _result.getRange(_resultOffset, length);
- } else if (_resultOffset == 0) {
- result = _result;
- } else {
- result = _result.getRange(_resultOffset, _result.length - _resultOffset);
- }
-
- _resultOffset += result.length;
- while (!_lineBreakEnds.isEmpty &&
- _lineBreakEnds.first < _charOffset + _resultOffset) {
- _lineBreakEnds.removeFirst();
- _lineBreaks--;
- }
- if (_result.length == _resultOffset) _resetResult();
- return new String.fromCharCodes(result);
- }
-
- String get decodedLine {
- if (_lineBreakEnds.isEmpty) return null;
- int lineEnd = _lineBreakEnds.removeFirst();
- int terminationSequenceLength = 1;
- if (_result[lineEnd - _charOffset] == LF &&
- lineEnd > _charOffset &&
- _resultOffset < lineEnd &&
- _result[lineEnd - _charOffset - 1] == CR) {
- terminationSequenceLength = 2;
- }
- var lineLength =
- lineEnd - _charOffset - _resultOffset - terminationSequenceLength + 1;
- String result =
- new String.fromCharCodes(_result.getRange(_resultOffset, lineLength));
- _lineBreaks--;
- _resultOffset += (lineLength + terminationSequenceLength);
- if (_result.length == _resultOffset) _resetResult();
- return result;
- }
-
- // Add another decoded character.
- void addChar(int charCode) {
- _result.add(charCode);
- _charCount++;
- // Check for line ends (\r, \n and \r\n).
- if (charCode == LF) {
- _recordLineBreakEnd(_charCount - 1);
- } else if (_lastCharCode == CR) {
- _recordLineBreakEnd(_charCount - 2);
- }
- _lastCharCode = charCode;
- }
-
- int available() => _result.length - _resultOffset;
-
- void _recordLineBreakEnd(int charPos) {
- _lineBreakEnds.add(charPos);
- _lineBreaks++;
- }
-
- void _resetResult() {
- _charOffset += _result.length;
- _result = new List<int>();
- _resultOffset = 0;
- }
-
- bool _processNext();
-
- _BufferList _bufferList;
- int _resultOffset = 0;
- List<int> _result;
- int _lineBreaks = 0; // Number of line breaks in the current list.
- // The positions of the line breaks are tracked in terms of absolute
- // character positions from the begining of the decoded data.
- Queue<int> _lineBreakEnds; // Character position of known line breaks.
- int _charOffset = 0; // Character number of the first character in the list.
- int _charCount = 0; // Total number of characters decoded.
- int _lastCharCode = -1;
- Function onError;
-
- final int LF = 10;
- final int CR = 13;
-}
-
-
-// Utility class for decoding UTF-8 from data delivered as a stream of
-// bytes.
-class _UTF8Decoder extends _StringDecoderBase {
- static const kMaxCodePoint = 0x10FFFF;
- static const kReplacementCodePoint = 0x3f;
-
- void done() {
- if (!_bufferList.isEmpty) {
- _reportError(new DecoderException("Illegal UTF-8"));
- }
- }
-
- bool _reportError(error) {
- if (onError != null) {
- onError(error);
- return false;
- } else {
- throw error;
- }
- }
-
- // Process the next UTF-8 encoded character.
- bool _processNext() {
- // Peek the next byte to calculate the number of bytes required for
- // the next character.
- int value = _bufferList.peek() & 0xFF;
- if ((value & 0x80) == 0x80) {
- int additionalBytes;
- if ((value & 0xe0) == 0xc0) { // 110xxxxx
- value = value & 0x1F;
- additionalBytes = 1;
- } else if ((value & 0xf0) == 0xe0) { // 1110xxxx
- value = value & 0x0F;
- additionalBytes = 2;
- } else if ((value & 0xf8) == 0xf0) { // 11110xxx
- value = value & 0x07;
- additionalBytes = 3;
- } else if ((value & 0xfc) == 0xf8) { // 111110xx
- value = value & 0x03;
- additionalBytes = 4;
- } else if ((value & 0xfe) == 0xfc) { // 1111110x
- value = value & 0x01;
- additionalBytes = 5;
- } else {
- return _reportError(new DecoderException("Illegal UTF-8"));
- }
- // Check if there are enough bytes to decode the character. Otherwise
- // return false.
- if (_bufferList.length < additionalBytes + 1) {
- return false;
- }
- // Remove the value peeked from the buffer list.
- _bufferList.next();
- for (int i = 0; i < additionalBytes; i++) {
- int byte = _bufferList.next();
- if ((byte & 0xc0) != 0x80) {
- return _reportError(new DecoderException("Illegal UTF-8"));
- }
- value = value << 6 | (byte & 0x3F);
- }
- } else {
- // Remove the value peeked from the buffer list.
- _bufferList.next();
- }
- if (value > kMaxCodePoint) {
- addChar(kReplacementCodePoint);
- } else {
- addChar(value);
- }
- return true;
- }
-}
-
-
-// Utility class for decoding ascii data delivered as a stream of
-// bytes.
-class _AsciiDecoder extends _StringDecoderBase {
- // Process the next ascii encoded character.
- bool _processNext() {
- while (_bufferList.length > 0) {
- int byte = _bufferList.next();
- if (byte > 127) {
- var error = new DecoderException("Illegal ASCII character $byte");
- if (onError != null) {
- onError(error);
- return false;
- } else {
- throw error;
- }
- }
- addChar(byte);
- }
- return true;
- }
-}
-
-
-// Utility class for decoding Latin-1 data delivered as a stream of
-// bytes.
-class _Latin1Decoder extends _StringDecoderBase {
- // Process the next Latin-1 encoded character.
- bool _processNext() {
- while (_bufferList.length > 0) {
- int byte = _bufferList.next();
- addChar(byte);
- }
- return true;
- }
-}
-
-
-// Utility class for decoding Windows current code page data delivered
-// as a stream of bytes.
-class _WindowsCodePageDecoder extends _StringDecoderBase {
- // Process the next chunk of data.
- bool _processNext() {
- List<int> bytes = _bufferList.readBytes(_bufferList.length);
- for (var charCode in _decodeBytes(bytes).charCodes) {
- addChar(charCode);
- }
- return true;
- }
-
- external static String _decodeBytes(List<int> bytes);
-}
-
-
-// Interface for encoders encoding string data into binary data.
-abstract class _StringEncoder {
- List<int> encodeString(String string);
-}
-
-
-// Utility class for encoding a string into UTF-8 byte stream.
-class _UTF8Encoder implements _StringEncoder {
- List<int> encodeString(String string) {
- int size = _encodingSize(string);
- List<int> result = new Uint8List(size);
- _encodeString(string, result);
- return result;
- }
-
- static int _encodingSize(String string) => _encodeString(string, null);
-
- static int _encodeString(String string, List<int> buffer) {
- List<int> utf8CodeUnits = encodeUtf8(string);
- if (buffer != null) {
- for (int i = 0; i < utf8CodeUnits.length; i++) {
- buffer[i] = utf8CodeUnits[i];
- }
- }
- return utf8CodeUnits.length;
- }
-}
-
-
-// Utility class for encoding a string into a Latin1 byte stream.
-class _Latin1Encoder implements _StringEncoder {
- List<int> encodeString(String string) {
- List<int> result = new Uint8List(string.length);
- for (int i = 0; i < string.length; i++) {
- int charCode = string.charCodeAt(i);
- if (charCode > 255) {
- throw new EncoderException(
- "No ISO_8859_1 encoding for code point $charCode");
- }
- result[i] = charCode;
- }
- return result;
- }
-}
-
-
-// Utility class for encoding a string into an ASCII byte stream.
-class _AsciiEncoder implements _StringEncoder {
- List<int> encodeString(String string) {
- List<int> result = new Uint8List(string.length);
- for (int i = 0; i < string.length; i++) {
- int charCode = string.charCodeAt(i);
- if (charCode > 127) {
- throw new EncoderException(
- "No ASCII encoding for code point $charCode");
- }
- result[i] = charCode;
- }
- return result;
- }
-}
-
-
-// Utility class for encoding a string into a current windows
-// code page byte list.
-class _WindowsCodePageEncoder implements _StringEncoder {
- List<int> encodeString(String string) {
- return _encodeString(string);
- }
-
- external static List<int> _encodeString(String string);
-}
-
-
-class _StringEncoders {
- static _StringEncoder encoder(Encoding encoding) {
- if (encoding == Encoding.UTF_8) {
- return new _UTF8Encoder();
- } else if (encoding == Encoding.ISO_8859_1) {
- return new _Latin1Encoder();
- } else if (encoding == Encoding.ASCII) {
- return new _AsciiEncoder();
- } else if (encoding == Encoding.SYSTEM) {
- if (Platform.operatingSystem == 'windows') {
- return new _WindowsCodePageEncoder();
- }
- return new _UTF8Encoder();
- } else {
- throw new StreamException("Unsupported encoding ${encoding.name}");
- }
- }
-}
-
-
-class EncoderException implements Exception {
- const EncoderException([String this.message]);
- String toString() => "EncoderException: $message";
- final String message;
-}
-
-
-class _StringInputStream implements StringInputStream {
- _StringInputStream(InputStream this._input, Encoding this._encoding) {
- _decoder = _StringDecoders.decoder(encoding);
- _input.onData = _onData;
- _input.onClosed = _onClosed;
- }
-
- String read([int len]) {
- String result = _decoder.decoded(len);
- _checkInstallDataHandler();
- return result;
- }
-
- String readLine() {
- String decodedLine = _decoder.decodedLine;
- if (decodedLine == null) {
- if (_inputClosed) {
- // Last line might not have a line separator.
- decodedLine = _decoder.decoded();
- if (decodedLine != null &&
- decodedLine[decodedLine.length - 1] == '\r') {
- decodedLine = decodedLine.substring(0, decodedLine.length - 1);
- }
- }
- }
- _checkInstallDataHandler();
- return decodedLine;
- }
-
- int available() => _decoder.available();
-
- Encoding get encoding => _encoding;
-
- bool get closed => _inputClosed && _decoder.isEmpty;
-
- void set onData(void callback()) {
- _clientDataHandler = callback;
- _clientLineHandler = null;
- _checkInstallDataHandler();
- _checkScheduleCallback();
- }
-
- void set onLine(void callback()) {
- _clientLineHandler = callback;
- _clientDataHandler = null;
- _checkInstallDataHandler();
- _checkScheduleCallback();
- }
-
- void set onClosed(void callback()) {
- _clientCloseHandler = callback;
- }
-
- void set onError(void callback(e)) {
- _input.onError = callback;
- _decoder.onError = (e) {
- _clientCloseHandler = null;
- _input.close();
- callback(e);
- };
- }
-
- void _onData() {
- _readData();
- if (!_decoder.isEmpty && _clientDataHandler != null) {
- _clientDataHandler();
- }
- if (_decoder.lineBreaks > 0 && _clientLineHandler != null) {
- _clientLineHandler();
- }
- _checkScheduleCallback();
- _checkInstallDataHandler();
- }
-
- void _onClosed() {
- _inputClosed = true;
- _decoder.done();
- if (_decoder.isEmpty && _clientCloseHandler != null) {
- _clientCloseHandler();
- } else {
- _checkScheduleCallback();
- }
- }
-
- void _readData() {
- List<int> data = _input.read();
- if (data != null) {
- _decoder.write(data);
- }
- }
-
- void _checkInstallDataHandler() {
- if (_inputClosed ||
- (_clientDataHandler == null && _clientLineHandler == null)) {
- _input.onData = null;
- } else if (_clientDataHandler != null) {
- if (_decoder.isEmpty) {
- _input.onData = _onData;
- } else {
- _input.onData = null;
- }
- } else {
- assert(_clientLineHandler != null);
- if (_decoder.lineBreaks == 0) {
- _input.onData = _onData;
- } else {
- _input.onData = null;
- }
- }
- }
-
- // TODO(sgjesse): Find a better way of scheduling callbacks from
- // the event loop.
- void _checkScheduleCallback() {
- void issueDataCallback() {
- _scheduledDataCallback = null;
- if (_clientDataHandler != null) {
- _clientDataHandler();
- _checkScheduleCallback();
- }
- }
-
- void issueLineCallback() {
- _scheduledLineCallback = null;
- if (_clientLineHandler != null) {
- _clientLineHandler();
- _checkScheduleCallback();
- }
- }
-
- void issueCloseCallback() {
- _scheduledCloseCallback = null;
- if (!_closed) {
- if (_clientCloseHandler != null) _clientCloseHandler();
- _closed = true;
- }
- }
-
- if (!_closed) {
- // Schedule data callback if string data available.
- if (_clientDataHandler != null &&
- !_decoder.isEmpty &&
- _scheduledDataCallback == null) {
- if (_scheduledLineCallback != null) {
- _scheduledLineCallback.cancel();
- }
- _scheduledDataCallback = Timer.run(issueDataCallback);
- }
-
- // Schedule line callback if a line is available.
- if (_clientLineHandler != null &&
- (_decoder.lineBreaks > 0 || (!_decoder.isEmpty && _inputClosed)) &&
- _scheduledLineCallback == null) {
- if (_scheduledDataCallback != null) {
- _scheduledDataCallback.cancel();
- }
- _scheduledLineCallback = Timer.run(issueLineCallback);
- }
-
- // Schedule close callback if no more data and input is closed.
- if (_decoder.isEmpty &&
- _inputClosed &&
- _scheduledCloseCallback == null) {
- _scheduledCloseCallback = Timer.run(issueCloseCallback);
- }
- }
- }
-
- InputStream _input;
- Encoding _encoding;
- _StringDecoder _decoder;
- bool _inputClosed = false; // Is the underlying input stream closed?
- bool _closed = false; // Is this stream closed.
- bool _eof = false; // Has all data been read from the decoder?
- Timer _scheduledDataCallback;
- Timer _scheduledLineCallback;
- Timer _scheduledCloseCallback;
- Function _clientDataHandler;
- Function _clientLineHandler;
- Function _clientCloseHandler;
-}
diff --git a/sdk/lib/io/string_transformer.dart b/sdk/lib/io/string_transformer.dart
new file mode 100644
index 0000000..857b8dc
--- /dev/null
+++ b/sdk/lib/io/string_transformer.dart
@@ -0,0 +1,414 @@
+// 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.
+
+part of dart.io;
+
+/**
+ * String encodings.
+ */
+class Encoding {
+ static const Encoding UTF_8 = const Encoding._internal("UTF-8");
+ static const Encoding ISO_8859_1 = const Encoding._internal("ISO-8859-1");
+ static const Encoding ASCII = const Encoding._internal("ASCII");
+ /**
+ * SYSTEM encoding is the current code page on Windows and UTF-8 on
+ * Linux and Mac.
+ */
+ static const Encoding SYSTEM = const Encoding._internal("SYSTEM");
+ const Encoding._internal(String this.name);
+ final String name;
+}
+
+
+/**
+ * Stream transformer that can decode a stream of bytes into a stream of
+ * strings using [encoding].
+ *
+ * Invalid or forbidden byte-sequences will not produce errors, but will instead
+ * insert [replacementChar] in the decoded strings.
+ */
+class StringDecoder implements StreamTransformer<List<int>, String> {
+ var _decoder;
+
+ /**
+ * Create a new [StringDecoder] with an optional [encoding] and
+ * [replacementChar].
+ */
+ StringDecoder([Encoding encoding = Encoding.UTF_8, int replacementChar]) {
+ switch (encoding) {
+ case Encoding.UTF_8:
+ if (replacementChar == null) {
+ replacementChar = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT;
+ }
+ _decoder = new Utf8DecoderTransformer(replacementChar);
+ break;
+ case Encoding.ASCII:
+ if (replacementChar == null) {
+ replacementChar = '?'.charCodeAt(0);
+ } else if (replacementChar > 127) {
+ throw new ArgumentError("Invalid replacement character for ASCII");
+ }
+ _decoder = new _AsciiDecoder(replacementChar);
+ break;
+ case Encoding.ISO_8859_1:
+ if (replacementChar == null) {
+ replacementChar = '?'.charCodeAt(0);
+ } else if (replacementChar > 255) {
+ throw new ArgumentError(
+ "Invalid replacement character for ISO_8859_1");
+ }
+ _decoder = new _Latin1Decoder(replacementChar);
+ break;
+ case Encoding.SYSTEM:
+ if (Platform.operatingSystem == "windows") {
+ _decoder = new _WindowsCodePageDecoder();
+ } else {
+ if (replacementChar != null) {
+ // TODO(ajohnsen): Handle replacement character.
+ throw new UnsupportedError(
+ "Replacement character is not supported for SYSTEM encoding");
+ }
+ _decoder = new Utf8DecoderTransformer();
+ }
+ break;
+ default:
+ throw new ArgumentError("Unsupported encoding '$encoding'");
+ }
+ }
+
+ Stream<String> bind(Stream<List<int>> stream) => _decoder.bind(stream);
+}
+
+
+/**
+ * Stream transformer that can encode a stream of strings info a stream of
+ * bytes using [encoding].
+ *
+ * Strings that cannot be represented in the given encoding will result in an
+ * error and a close event on the stream.
+ */
+class StringEncoder implements StreamTransformer<String, List<int>> {
+ var _encoder;
+
+ /**
+ * Create a new [StringDecoder] with an optional [encoding] and
+ * [replacementChar].
+ */
+ StringEncoder([Encoding encoding = Encoding.UTF_8]) {
+ switch (encoding) {
+ case Encoding.UTF_8:
+ _encoder = new Utf8EncoderTransformer();
+ break;
+ case Encoding.ASCII:
+ _encoder = new _AsciiEncoder();
+ break;
+ case Encoding.ISO_8859_1:
+ _encoder = new _Latin1Encoder();
+ break;
+ case Encoding.SYSTEM:
+ if (Platform.operatingSystem == "windows") {
+ _encoder = new _WindowsCodePageEncoder();
+ } else {
+ _encoder = new Utf8EncoderTransformer();
+ }
+ break;
+ default:
+ throw new ArgumentError("Unsupported encoding '$encoding'");
+ }
+ }
+
+ Stream<List<int>> bind(Stream<String> stream) => _encoder.bind(stream);
+}
+
+
+// Utility function to synchronously decode a list of bytes.
+String _decodeString(List<int> bytes, [Encoding encoding = Encoding.UTF_8]) {
+ if (bytes.length == 0) return "";
+ var string;
+ var controller = new StreamController();
+ controller.stream
+ .transform(new StringDecoder(encoding))
+ .listen((data) => string = data);
+ controller.add(bytes);
+ controller.close();
+ assert(string != null);
+ return string;
+}
+
+
+// Utility function to synchronously encode a String.
+// Will throw an exception if the encoding is invalid.
+List<int> _encodeString(String string, [Encoding encoding = Encoding.UTF_8]) {
+ if (string.length == 0) return [];
+ var bytes;
+ var controller = new StreamController();
+ controller.stream
+ .transform(new StringEncoder(encoding))
+ .listen((data) => bytes = data);
+ controller.add(string);
+ controller.close();
+ assert(bytes != null);
+ return bytes;
+}
+
+
+class LineTransformer implements StreamTransformer<String, String> {
+ const int _LF = 10;
+ const int _CR = 13;
+
+ final StringBuffer _buffer = new StringBuffer();
+
+ StreamSubscription<String> _subscription;
+ StreamController<String> _controller;
+ String _carry;
+
+ Stream<String> bind(Stream<String> stream) {
+ _controller = new StreamController<String>(
+ onPauseStateChange: _pauseChanged,
+ onSubscriptionStateChange: _subscriptionChanged);
+
+ void handle(String data, bool isClosing) {
+ if (_carry != null) {
+ data = _carry.concat(data);
+ _carry = null;
+ }
+ int startPos = 0;
+ int pos = 0;
+ while (pos < data.length) {
+ int skip = 0;
+ int char = data.charCodeAt(pos);
+ if (char == _LF) {
+ skip = 1;
+ } else if (char == _CR) {
+ skip = 1;
+ if (pos + 1 < data.length) {
+ if (data.charCodeAt(pos + 1) == _LF) {
+ skip = 2;
+ }
+ } else if (!isClosing) {
+ _carry = data.substring(startPos);
+ return;
+ }
+ }
+ if (skip > 0) {
+ _buffer.add(data.substring(startPos, pos));
+ _controller.add(_buffer.toString());
+ _buffer.clear();
+ startPos = pos = pos + skip;
+ } else {
+ pos++;
+ }
+ }
+ if (pos != startPos) {
+ // Add remaining
+ _buffer.add(data.substring(startPos, pos));
+ }
+ if (isClosing && !_buffer.isEmpty) {
+ _controller.add(_buffer.toString());
+ _buffer.clear();
+ }
+ }
+
+ _subscription = stream.listen(
+ (data) => handle(data, false),
+ onDone: () {
+ // Handle remaining data (mainly _carry).
+ handle("", true);
+ _controller.close();
+ },
+ onError: _controller.signalError);
+ return _controller.stream;
+ }
+
+ void _pauseChanged() {
+ if (_controller.isPaused) {
+ _subscription.pause();
+ } else {
+ _subscription.resume();
+ }
+ }
+
+ void _subscriptionChanged() {
+ if (!_controller.hasSubscribers) {
+ _subscription.cancel();
+ }
+ }
+}
+
+
+class _SingleByteDecoder implements StreamTransformer<List<int>, String> {
+ StreamSubscription<List<int>> _subscription;
+ StreamController<String> _controller;
+ final int _replacementChar;
+
+ _SingleByteDecoder(this._replacementChar);
+
+ Stream<String> bind(Stream<List<int>> stream) {
+ _controller = new StreamController<String>(
+ onPauseStateChange: _pauseChanged,
+ onSubscriptionStateChange: _subscriptionChanged);
+ _subscription = stream.listen(
+ (data) {
+ var buffer = new List<int>.fixedLength(data.length);
+ for (int i = 0; i < data.length; i++) {
+ int char = _decodeByte(data[i]);
+ if (char < 0) char = _replacementChar;
+ buffer[i] = char;
+ }
+ _controller.add(new String.fromCharCodes(buffer));
+ },
+ onDone: _controller.close,
+ onError: _controller.signalError);
+ return _controller.stream;
+ }
+
+ int _decodeByte(int byte);
+
+ void _pauseChanged() {
+ if (_controller.isPaused) {
+ _subscription.pause();
+ } else {
+ _subscription.resume();
+ }
+ }
+
+ void _subscriptionChanged() {
+ if (!_controller.hasSubscribers) {
+ _subscription.cancel();
+ }
+ }
+}
+
+
+// Utility class for decoding ascii data delivered as a stream of
+// bytes.
+class _AsciiDecoder extends _SingleByteDecoder {
+ _AsciiDecoder(int replacementChar) : super(replacementChar);
+
+ int _decodeByte(int byte) => ((byte & 0x7f) == byte) ? byte : -1;
+}
+
+
+// Utility class for decoding Latin-1 data delivered as a stream of
+// bytes.
+class _Latin1Decoder extends _SingleByteDecoder {
+ _Latin1Decoder(int replacementChar) : super(replacementChar);
+
+ int _decodeByte(int byte) => ((byte & 0xFF) == byte) ? byte : -1;
+}
+
+
+class _SingleByteEncoder implements StreamTransformer<String, List<int>> {
+ StreamSubscription<String> _subscription;
+ StreamController<List<int>> _controller;
+
+ Stream<List<int>> bind(Stream<String> stream) {
+ _controller = new StreamController<List<int>>(
+ onPauseStateChange: _pauseChanged,
+ onSubscriptionStateChange: _subscriptionChanged);
+ _subscription = stream.listen(
+ (string) {
+ var bytes = _encode(string);
+ if (bytes == null) {
+ _controller.signalError(new FormatException(
+ "Invalid character for encoding"));
+ _controller.close();
+ _subscription.cancel();
+ } else {
+ _controller.add(bytes);
+ }
+ },
+ onDone: _controller.close,
+ onError: _controller.signalError);
+ return _controller.stream;
+ }
+
+ List<int> _encode(String string);
+
+ void _pauseChanged() {
+ if (_controller.isPaused) {
+ _subscription.pause();
+ } else {
+ _subscription.resume();
+ }
+ }
+
+ void _subscriptionChanged() {
+ if (!_controller.hasSubscribers) {
+ _subscription.cancel();
+ }
+ }
+}
+
+
+// Utility class for encoding a string into an ASCII byte stream.
+class _AsciiEncoder extends _SingleByteEncoder {
+ List<int> _encode(String string) {
+ var bytes = string.charCodes;
+ for (var byte in bytes) {
+ if (byte > 127) return null;
+ }
+ return bytes;
+ }
+}
+
+
+// Utility class for encoding a string into a Latin1 byte stream.
+class _Latin1Encoder extends _SingleByteEncoder {
+ List<int> _encode(String string) {
+ var bytes = string.charCodes;
+ for (var byte in bytes) {
+ if (byte > 255) return null;
+ }
+ return bytes;
+ }
+}
+
+
+// Utility class for encoding a string into a current windows
+// code page byte list.
+// Implemented on top of a _SingleByteEncoder, even though it's not really a
+// single byte encoder, to avoid copying boilerplate.
+class _WindowsCodePageEncoder extends _SingleByteEncoder {
+ List<int> _encode(String string) => _encodeString(string);
+
+ external static List<int> _encodeString(String string);
+}
+
+
+// Utility class for decoding Windows current code page data delivered
+// as a stream of bytes.
+class _WindowsCodePageDecoder implements StreamTransformer<List<int>, String> {
+ StreamSubscription<List<int>> _subscription;
+ StreamController<String> _controller;
+
+ Stream<String> bind(Stream<List<int>> stream) {
+ _controller = new StreamController<String>(
+ onPauseStateChange: _pauseChanged,
+ onSubscriptionStateChange: _subscriptionChanged);
+ _subscription = stream.listen(
+ (data) {
+ _controller.add(_decodeBytes(data));
+ },
+ onDone: _controller.close,
+ onError: _controller.signalError);
+ return _controller.stream;
+ }
+
+ external static String _decodeBytes(List<int> bytes);
+
+ void _pauseChanged() {
+ if (_controller.isPaused) {
+ _subscription.pause();
+ } else {
+ _subscription.resume();
+ }
+ }
+
+ void _subscriptionChanged() {
+ if (!_controller.hasSubscribers) {
+ _subscription.cancel();
+ }
+ }
+}
diff --git a/sdk/lib/io/timer_impl.dart b/sdk/lib/io/timer_impl.dart
index 415c108..c08f3bb 100644
--- a/sdk/lib/io/timer_impl.dart
+++ b/sdk/lib/io/timer_impl.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
@@ -18,7 +18,7 @@
if (_timers == null) {
_timers = new DoubleLinkedQueue<_Timer>();
}
- Timer timer = new _Timer._internal();
+ _Timer timer = new _Timer._internal();
timer._callback = callback;
timer._milliSeconds = milliSeconds;
timer._wakeupTime = (new DateTime.now()).millisecondsSinceEpoch + milliSeconds;
diff --git a/sdk/lib/io/websocket.dart b/sdk/lib/io/websocket.dart
index 9ded504..48cf54a 100644
--- a/sdk/lib/io/websocket.dart
+++ b/sdk/lib/io/websocket.dart
@@ -24,145 +24,25 @@
}
/**
- * The web socket protocol is implemented by a HTTP or HTTPS server handler
- * which can be instantiated like this:
+ * The [WebSocketTransformer] is implemented as a stream transformer that
+ * transforms a stream of HttpRequest into a stream of WebSockets by upgrading
+ * each HttpRequest from the HTTP or HTTPS server, to the WebSocket protocol.
*
- * WebSocketHandler wsHandler = new WebSocketHandler();
+ * Example of usage:
*
- * and then its onRequest method can be assigned to the HTTP server, e.g.
- *
- * server.defaultHandler = wsHandler.onRequest;
+ * server.transform(new WebSocketTransformer()).listen((webSocket) => ...);
*
* or
*
- * server.addRequestHandler((req) => req.path == "/ws",
- * wsHandler.onRequest);
+ * server
+ * .where((request) => request.uri.scheme == "ws")
+ * .transform(new WebSocketTransformer()).listen((webSocket) => ...);
*
- * This handler strives to implement web sockets as specified by RFC6455.
+ * This transformer strives to implement web sockets as specified by RFC6455.
*/
-abstract class WebSocketHandler {
- factory WebSocketHandler() => new _WebSocketHandler();
-
- /**
- * Request handler to be registered with the HTTP server.
- */
- void onRequest(HttpRequest request, HttpResponse response);
-
- /**
- * Sets the callback to be called when a new web socket connection
- * has been established.
- */
- void set onOpen(callback(WebSocketConnection connection));
-}
-
-
-/**
- * Server web socket connection.
- */
-abstract class WebSocketConnection {
- /**
- * Sets the callback to be called when a message has been
- * received. The type on [message] is either [:String:] or
- * [:List<int>:] depending on whether it is a text or binary
- * message. If the message is empty [message] will be [:null:].
- * If [message] is a [:List<int>:] then it will contain byte values
- * from 0 to 255.
- */
- void set onMessage(void callback(message));
-
- /**
- * Sets the callback to be called when the web socket connection is
- * closed. [status] indicate the reason for closing. For network
- * errors the value of [status] will be
- * WebSocketStatus.ABNORMAL_CLOSURE]. In this callback it is
- * possible to call [close] if [close] has not already been called.
- * If [close] has still not been called after the close callback
- * returns the received close status will automatically be echoed
- * back to the other end to finish the close handshake.
- */
- void set onClosed(void callback(int status, String reason));
-
- /**
- * Sends a message. The [message] must be a [:String:], a
- * [:List<int>:] containing bytes, or [:null:].
- */
- send(Object message);
-
- /**
- * Close the web socket connection. The default value for [status]
- * and [reason] are [:null:].
- */
- close([int status, String reason]);
-}
-
-
-/**
- * Client web socket connection.
- */
-abstract class WebSocketClientConnection {
- /**
- * Creates a new web socket client connection based on a HTTP(S) client
- * connection. The HTTP or HTTPS client connection must be freshly opened.
- */
- factory WebSocketClientConnection(HttpClientConnection conn,
- [List<String> protocols]) {
- return new _WebSocketClientConnection(conn, protocols);
- }
-
- /**
- * Sets the callback to be called when the request object for the
- * opening handshake request is ready. This callback can be used if
- * one needs to add additional headers to the opening handshake
- * request.
- */
- void set onRequest(void callback(HttpClientRequest request));
-
- /**
- * Sets the callback to be called when a web socket connection has
- * been established.
- */
- void set onOpen(void callback());
-
- /**
- * Sets the callback to be called when a message has been
- * received. The type of [message] is either [:String:] or
- * [:List<int>:], depending on whether it is a text or binary
- * message. If the message is empty [message] will be [:null:].
- * If the message is a [:List<int>:] then it will contain byte values
- * from 0 to 255.
- */
- void set onMessage(void callback(message));
-
- /**
- * Sets the callback to be called when the web socket connection is
- * closed. [status] indicates the reason for closing. For network
- * errors the value of [status] will be
- * WebSocketStatus.ABNORMAL_CLOSURE].
- */
- void set onClosed(void callback(int status, String reason));
-
- /**
- * Sets the callback to be called when the response object for the
- * opening handshake did not cause a web socket connection
- * upgrade. This will be called in case the response status code is
- * not 101 (Switching Protocols). If this callback is not set and the
- * server does not upgrade the connection, the [:onError:] callback will
- * be called.
- */
- void set onNoUpgrade(void callback(HttpClientResponse response));
-
- /**
- * Sends a message. The [message] must be a [:String:] or a
- * [:List<int>:] containing bytes. To send an empty message send either
- * an empty [:String:] or an empty [:List<int>:]. [:null:] cannot be sent.
- */
- send(message);
-
- /**
- * Close the web socket connection. The default value for [status]
- * and [reason] are [:null:].
- */
- close([int status, String reason]);
+abstract class WebSocketTransformer
+ implements StreamTransformer<HttpRequest, WebSocket> {
+ factory WebSocketTransformer() => new _WebSocketTransformerImpl();
}
@@ -182,7 +62,6 @@
* message is empty [message] will be [:null:]
* If the message is a [:List<int>:] then it will contain byte values
* from 0 to 255.
-
*/
get data;
}
@@ -216,7 +95,7 @@
* with the W3C browser API for web sockets specified in
* http://dev.w3.org/html5/websockets/.
*/
-abstract class WebSocket {
+abstract class WebSocket implements Stream<Event> {
/**
* Possible states of the connection.
*/
@@ -227,11 +106,12 @@
/**
* Create a new web socket connection. The URL supplied in [url]
- * must use the scheme [:ws:]. The [protocols] argument is either a
- * [:String:] or [:List<String>:] specifying the subprotocols the
+ * must use the scheme [:ws:] or [:wss:]. The [protocols] argument is either
+ * a [:String:] or [:List<String>:] specifying the subprotocols the
* client is willing to speak.
*/
- factory WebSocket(String url, [protocols]) => new _WebSocket(url, protocols);
+ static Future<WebSocket> connect(String url, [protocols]) =>
+ _WebSocketImpl.connect(url, protocols);
/**
* Returns the current state of the connection.
@@ -244,24 +124,6 @@
int get bufferedAmount;
/**
- * Sets the callback to be called when a web socket connection has
- * been established.
- */
- void set onopen(void callback());
-
- /**
- * Sets the callback to be called when the web socket connection
- * encountered an error.
- */
- void set onerror(void callback(e));
-
- /**
- * Sets the callback to be called when the web socket connection is
- * closed.
- */
- void set onclose(void callback(CloseEvent event));
-
- /**
* The extensions property is initially the empty string. After the
* web socket connection is established this string reflects the
* extensions used by the server.
@@ -279,13 +141,7 @@
/**
* Closes the web socket connection.
*/
- void close(int code, String reason);
-
- /**
- * Sets the callback to be called when a message has been
- * received.
- */
- void set onmessage(void callback(MessageEvent event));
+ void close([int code, String reason]);
/**
* Sends data on the web socket connection. The data in [data] must
diff --git a/sdk/lib/io/websocket_impl.dart b/sdk/lib/io/websocket_impl.dart
index 673fe23..a8d01c2 100644
--- a/sdk/lib/io/websocket_impl.dart
+++ b/sdk/lib/io/websocket_impl.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// 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.
@@ -42,7 +42,6 @@
* [:onMessageData:]
* [:onMessageEnd:]
* [:onClosed:]
- * [:onError:]
*
*/
class _WebSocketProtocolProcessor {
@@ -62,9 +61,9 @@
/**
* Process data received from the underlying communication channel.
*/
- void update(List<int> buffer) {
- int index = 0;
- int lastIndex = buffer.length;
+ void update(List<int> buffer, int offset, int count) {
+ int index = offset;
+ int lastIndex = offset + count;
try {
if (_state == CLOSED) {
throw new WebSocketException("Data on closed connection");
@@ -296,10 +295,8 @@
throw new WebSocketException("Protocol error");
}
if (_controlPayload.length > 2) {
- var decoder = _StringDecoders.decoder(Encoding.UTF_8);
- decoder.write(
+ reason = _decodeString(
_controlPayload.getRange(2, _controlPayload.length - 2));
- reason = decoder.decoded();
}
}
if (onClosed != null) onClosed(status, reason);
@@ -360,253 +357,43 @@
}
-class _WebSocketConnectionBase {
- void _socketConnected(Socket socket) {
- _socket = socket;
- _socket.onError = (e) => _socket.close();
- }
+class _WebSocketTransformerImpl implements WebSocketTransformer {
+ final StreamController<WebSocket> _controller =
+ new StreamController<WebSocket>();
- void _startProcessing(List<int> unparsedData) {
- _WebSocketProtocolProcessor processor = new _WebSocketProtocolProcessor();
- processor.onMessageStart = _onWebSocketMessageStart;
- processor.onMessageData = _onWebSocketMessageData;
- processor.onMessageEnd = _onWebSocketMessageEnd;
- processor.onPing = _onWebSocketPing;
- processor.onPong = _onWebSocketPong;
- processor.onClosed = _onWebSocketClosed;
- if (unparsedData != null) {
- processor.update(unparsedData);
- }
- _socket.onData = () {
- processor.update(_socket.read());
- };
- _socket.onClosed = () {
- processor.closed();
- if (_closeSent) {
- // Got socket close in response to close frame. Don't treat
- // that as an error.
- if (_closeTimer != null) _closeTimer.cancel();
- } else {
- if (_onClosed != null) _onClosed(WebSocketStatus.ABNORMAL_CLOSURE,
- "Unexpected close");
+ Stream<WebSocket> bind(Stream<HttpRequest> stream) {
+ stream.listen((request) {
+ var response = request.response;
+ if (!_isWebSocketUpgrade(request)) {
+ _controller.signalError(
+ new AsyncError(
+ new WebSocketException("Invalid WebSocket upgrade request")));
+ request.listen((_) {}, onDone: () {
+ response.statusCode = HttpStatus.BAD_REQUEST;
+ response.contentLength = 0;
+ response.close();
+ });
+ return;
}
- _socket.close();
- };
- }
+ // Send the upgrade response.
+ response.statusCode = HttpStatus.SWITCHING_PROTOCOLS;
+ response.headers.add(HttpHeaders.CONNECTION, "Upgrade");
+ response.headers.add(HttpHeaders.UPGRADE, "websocket");
+ String key = request.headers.value("Sec-WebSocket-Key");
+ SHA1 sha1 = new SHA1();
+ sha1.add("$key$_webSocketGUID".charCodes);
+ String accept = _Base64._encode(sha1.close());
+ response.headers.add("Sec-WebSocket-Accept", accept);
+ response.headers.contentLength = 0;
+ response.detachSocket()
+ .then((socket) {
+ _controller.add(new _WebSocketImpl._fromSocket(socket));
+ }, onError: (error) {
+ _controller.signalError(error);
+ });
+ });
- void set onMessage(void callback(Object message)) {
- _onMessage = callback;
- }
-
- void set onClosed(void callback(int status, String reason)) {
- _onClosed = callback;
- }
-
- send(message) {
- if (_closeSent) {
- throw new WebSocketException("Connection closed");
- }
- List<int> data;
- int opcode;
- if (message != null) {
- if (message is String) {
- opcode = _WebSocketOpcode.TEXT;
- data = _StringEncoders.encoder(Encoding.UTF_8).encodeString(message);
- } else {
- if (message is !List<int>) {
- throw new ArgumentError(message);
- }
- opcode = _WebSocketOpcode.BINARY;
- data = message;
- }
- } else {
- opcode = _WebSocketOpcode.TEXT;
- }
- _sendFrame(opcode, data);
- }
-
- close([int status, String reason]) {
- if (status == WebSocketStatus.RESERVED_1004 ||
- status == WebSocketStatus.NO_STATUS_RECEIVED ||
- status == WebSocketStatus.RESERVED_1015) {
- throw new WebSocketException("Reserved status code $status");
- }
-
- if (_closeSent) return;
- List<int> data;
- if (status != null) {
- data = new List<int>();
- data.add((status >> 8) & 0xFF);
- data.add(status & 0xFF);
- if (reason != null) {
- data.addAll(
- _StringEncoders.encoder(Encoding.UTF_8).encodeString(reason));
- }
- }
- _sendFrame(_WebSocketOpcode.CLOSE, data);
-
- if (_closeReceived) {
- // Close the socket when the close frame has been sent - if it
- // does not take too long.
- _socket.outputStream.close();
- _socket.outputStream.onClosed = () {
- if (_closeTimer != null) _closeTimer.cancel();
- _socket.close();
- };
- _closeTimer = new Timer(const Duration(seconds: 5), _socket.close);
- } else {
- // Half close the socket and expect a close frame in response
- // before closing the socket. If a close frame does not arrive
- // within a reasonable amount of time just close the socket.
- _socket.outputStream.close();
- _closeTimer = new Timer(const Duration(seconds: 5), _socket.close);
- }
- _closeSent = true;
- }
-
- int get hashCode => _hash;
-
- _onWebSocketMessageStart(int type) {
- _currentMessageType = type;
- if (_currentMessageType == _WebSocketMessageType.TEXT) {
- _decoder = _StringDecoders.decoder(Encoding.UTF_8);
- } else {
- _outputStream = new ListOutputStream();
- }
- }
-
- _onWebSocketMessageData(List<int> buffer, int offset, int count) {
- if (_currentMessageType == _WebSocketMessageType.TEXT) {
- _decoder.write(buffer.getRange(offset, count));
- } else {
- _outputStream.write(buffer.getRange(offset, count));
- }
- }
-
- _onWebSocketMessageEnd() {
- if (_onMessage != null) {
- if (_currentMessageType == _WebSocketMessageType.TEXT) {
- _onMessage(_decoder.decoded());
- } else {
- _onMessage(_outputStream.read());
- }
- }
- _decoder = null;
- _outputStream = null;
- }
-
- _onWebSocketPing(List<int> payload) {
- _sendFrame(_WebSocketOpcode.PONG, payload);
- }
-
- _onWebSocketPong(List<int> payload) {
- // Currently pong messages are ignored.
- }
-
- _onWebSocketClosed(int status, String reason) {
- _closeReceived = true;
- if (_onClosed != null) _onClosed(status, reason);
- if (_closeSent) {
- // Got close frame in response to close frame. Now close the socket.
- if (_closeTimer != null) _closeTimer.cancel();
- _socket.close();
- } else {
- if (status != WebSocketStatus.NO_STATUS_RECEIVED) {
- close(status);
- } else {
- close();
- }
- }
- }
-
- _sendFrame(int opcode, [List<int> data]) {
- bool mask = false; // Masking not implemented for server.
- int dataLength = data == null ? 0 : data.length;
- // Determine the header size.
- int headerSize = (mask) ? 6 : 2;
- if (dataLength > 65535) {
- headerSize += 8;
- } else if (dataLength > 125) {
- headerSize += 2;
- }
- List<int> header = new List<int>.fixedLength(headerSize);
- int index = 0;
- // Set FIN and opcode.
- header[index++] = 0x80 | opcode;
- // Determine size and position of length field.
- int lengthBytes = 1;
- int firstLengthByte = 1;
- if (dataLength > 65535) {
- header[index++] = 127;
- lengthBytes = 8;
- } else if (dataLength > 125) {
- header[index++] = 126;
- lengthBytes = 2;
- }
- // Write the length in network byte order into the header.
- for (int i = 0; i < lengthBytes; i++) {
- header[index++] = dataLength >> (((lengthBytes - 1) - i) * 8) & 0xFF;
- }
- assert(index == headerSize);
- _socket.outputStream.write(header);
- if (data != null) {
- _socket.outputStream.write(data);
- }
- }
-
- Socket _socket;
- Timer _closeTimer;
- int _hash;
-
- Function _onMessage;
- Function _onClosed;
-
- int _currentMessageType = _WebSocketMessageType.NONE;
- _StringDecoder _decoder;
- ListOutputStream _outputStream;
- bool _closeReceived = false;
- bool _closeSent = false;
-}
-
-
-class _WebSocketConnection
- extends _WebSocketConnectionBase implements WebSocketConnection {
- _WebSocketConnection(DetachedSocket detached) {
- _hash = detached.socket.hashCode;
- _socketConnected(detached.socket);
- _startProcessing(detached.unparsedData);
- }
-}
-
-
-class _WebSocketHandler implements WebSocketHandler {
- void onRequest(HttpRequest request, HttpResponse response) {
- // Check that this is a web socket upgrade.
- if (!_isWebSocketUpgrade(request)) {
- response.statusCode = HttpStatus.BAD_REQUEST;
- response.outputStream.close();
- return;
- }
-
- // Send the upgrade response.
- response.statusCode = HttpStatus.SWITCHING_PROTOCOLS;
- response.headers.add(HttpHeaders.CONNECTION, "Upgrade");
- response.headers.add(HttpHeaders.UPGRADE, "websocket");
- String key = request.headers.value("Sec-WebSocket-Key");
- SHA1 sha1 = new SHA1();
- sha1.add("$key$_webSocketGUID".charCodes);
- String accept = _Base64._encode(sha1.close());
- response.headers.add("Sec-WebSocket-Accept", accept);
- response.contentLength = 0;
-
- // Upgrade the connection and get the underlying socket.
- WebSocketConnection conn =
- new _WebSocketConnection(response.detachSocket());
- if (_onOpen != null) _onOpen(conn);
- }
-
- void set onOpen(callback(WebSocketConnection connection)) {
- _onOpen = callback;
+ return _controller.stream;
}
bool _isWebSocketUpgrade(HttpRequest request) {
@@ -635,222 +422,248 @@
}
return true;
}
-
- Function _onOpen;
}
-class _WebSocketClientConnection
- extends _WebSocketConnectionBase implements WebSocketClientConnection {
- _WebSocketClientConnection(HttpClientConnection this._conn,
- [List<String> protocols]) {
- _conn.onRequest = _onHttpClientRequest;
- _conn.onResponse = _onHttpClientResponse;
- _conn.onError = (e) {
- if (_onClosed != null) {
- _onClosed(WebSocketStatus.ABNORMAL_CLOSURE, "$e");
- }
- };
+class _WebSocketImpl extends Stream<Event> implements WebSocket {
+ final StreamController<Event> _controller = new StreamController<Event>();
- // Generate the nonce now as it is also used to set the hash code.
- _generateNonceAndHash();
- }
+ final _WebSocketProtocolProcessor _processor =
+ new _WebSocketProtocolProcessor();
- void set onRequest(void callback(HttpClientRequest request)) {
- _onRequest = callback;
- }
+ final Socket _socket;
+ int _readyState = WebSocket.CONNECTING;
- void set onOpen(void callback()) {
- _onOpen = callback;
- }
+ static final HttpClient _httpClient = new HttpClient();
- void set onNoUpgrade(void callback(HttpClientResponse request)) {
- _onNoUpgrade = callback;
- }
-
- void _onHttpClientRequest(HttpClientRequest request) {
- if (_onRequest != null) {
- _onRequest(request);
- }
- // Setup the initial handshake.
- request.headers.add(HttpHeaders.CONNECTION, "upgrade");
- request.headers.set(HttpHeaders.UPGRADE, "websocket");
- request.headers.set("Sec-WebSocket-Key", _nonce);
- request.headers.set("Sec-WebSocket-Version", "13");
- request.contentLength = 0;
- request.outputStream.close();
- }
-
- void _onHttpClientResponse(HttpClientResponse response) {
- if (response.statusCode != HttpStatus.SWITCHING_PROTOCOLS) {
- if (_onNoUpgrade != null) {
- _onNoUpgrade(response);
- } else {
- _conn.detachSocket().socket.close();
- throw new WebSocketException("Protocol upgrade refused");
- }
- return;
- }
-
- if (!_isWebSocketUpgrade(response)) {
- _conn.detachSocket().socket.close();
- throw new WebSocketException("Protocol upgrade failed");
- }
-
- // Connection upgrade successful.
- DetachedSocket detached = _conn.detachSocket();
- _socketConnected(detached.socket);
- if (_onOpen != null) _onOpen();
- _startProcessing(detached.unparsedData);
- }
-
- void _generateNonceAndHash() {
- Random random = new Random();
- assert(_nonce == null);
- void intToBigEndianBytes(int value, List<int> bytes, int offset) {
- bytes[offset] = (value >> 24) & 0xFF;
- bytes[offset + 1] = (value >> 16) & 0xFF;
- bytes[offset + 2] = (value >> 8) & 0xFF;
- bytes[offset + 3] = value & 0xFF;
- }
-
- // Generate 16 random bytes. Use the last four bytes for the hash code.
- List<int> nonce = new List<int>.fixedLength(16);
- for (int i = 0; i < 4; i++) {
- int r = random.nextInt(0x100000000);
- intToBigEndianBytes(r, nonce, i * 4);
- }
- _nonce = _Base64._encode(nonce);
- _hash = random.nextInt(0x100000000);
- }
-
- bool _isWebSocketUpgrade(HttpClientResponse response) {
- if (response.headers[HttpHeaders.CONNECTION] == null) {
- return false;
- }
- bool isUpgrade = false;
- response.headers[HttpHeaders.CONNECTION].forEach((String value) {
- if (value.toLowerCase() == "upgrade") isUpgrade = true;
- });
- if (!isUpgrade) return false;
- String upgrade = response.headers.value(HttpHeaders.UPGRADE);
- if (upgrade == null || upgrade.toLowerCase() != "websocket") {
- return false;
- }
- String accept = response.headers.value("Sec-WebSocket-Accept");
- if (accept == null) {
- return false;
- }
- SHA1 sha1 = new SHA1();
- sha1.add("$_nonce$_webSocketGUID".charCodes);
- List<int> expectedAccept = sha1.close();
- List<int> receivedAccept = _Base64._decode(accept);
- if (expectedAccept.length != receivedAccept.length) return false;
- for (int i = 0; i < expectedAccept.length; i++) {
- if (expectedAccept[i] != receivedAccept[i]) return false;
- }
- return true;
- }
-
- Function _onRequest;
- Function _onOpen;
- Function _onNoUpgrade;
- HttpClientConnection _conn;
- String _nonce;
-}
-
-
-class _WebSocket implements WebSocket {
- _WebSocket(String url, [protocols]) {
+ static Future<WebSocket> connect(String url, [protocols]) {
Uri uri = Uri.parse(url);
if (uri.scheme != "ws" && uri.scheme != "wss") {
- throw new WebSocketException("Unsupported URL scheme ${uri.scheme}");
+ throw new WebSocketException("Unsupported URL scheme '${uri.scheme}'");
}
if (uri.userInfo != "") {
- throw new WebSocketException("Unsupported user info ${uri.userInfo}");
- }
- int port = uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : uri.port;
- String path = uri.path;
- if (path.length == 0) path = "/";
- if (uri.query != "") {
- if (uri.fragment != "") {
- path = "${path}?${uri.query}#${uri.fragment}";
- } else {
- path = "${path}?${uri.query}";
- }
+ throw new WebSocketException("Unsupported user info '${uri.userInfo}'");
}
- HttpClient client = new HttpClient();
- bool secure = (uri.scheme == 'wss');
- HttpClientConnection conn = client.openUrl("GET",
- new Uri.fromComponents(scheme: secure ? "https" : "http",
- domain: uri.domain,
- port: port,
- path: path));
- if (protocols is String) protocols = [protocols];
- _wsconn = new WebSocketClientConnection(conn, protocols);
- _wsconn.onOpen = () {
- // HTTP client not needed after socket have been detached.
- client.shutdown();
- client = null;
- _readyState = WebSocket.OPEN;
- if (_onopen != null) _onopen();
- };
- _wsconn.onMessage = (message) {
- if (_onmessage != null) {
- _onmessage(new _WebSocketMessageEvent(message));
+ Random random = new Random();
+ // Generate 16 random bytes.
+ List<int> nonceData = new List<int>.fixedLength(16);
+ for (int i = 0; i < 16; i++) {
+ nonceData[i] = random.nextInt(256);
+ }
+ String nonce = _Base64._encode(nonceData);
+
+ uri = new Uri.fromComponents(scheme: uri.scheme == "wss" ? "https" : "http",
+ userInfo: uri.userInfo,
+ domain: uri.domain,
+ port: uri.port,
+ path: uri.path,
+ query: uri.query,
+ fragment: uri.fragment);
+ return _httpClient.openUrl("GET", uri)
+ .then((request) {
+ // Setup the initial handshake.
+ request.headers.add(HttpHeaders.CONNECTION, "upgrade");
+ request.headers.set(HttpHeaders.UPGRADE, "websocket");
+ request.headers.set("Sec-WebSocket-Key", nonce);
+ request.headers.set("Sec-WebSocket-Version", "13");
+ return request.close();
+ })
+ .then((response) {
+ void error(String message) {
+ // Flush data.
+ response.detachSocket().then((socket) {
+ socket.destroy();
+ });
+ throw new WebSocketException(message);
+ }
+ if (response.statusCode != HttpStatus.SWITCHING_PROTOCOLS ||
+ response.headers[HttpHeaders.CONNECTION] == null ||
+ !response.headers[HttpHeaders.CONNECTION].any(
+ (value) => value.toLowerCase() == "upgrade") ||
+ response.headers.value(HttpHeaders.UPGRADE).toLowerCase() !=
+ "websocket") {
+ error("Connection to '$uri' was not upgraded to websocket");
+ }
+ String accept = response.headers.value("Sec-WebSocket-Accept");
+ if (accept == null) {
+ error("Response did not contain a 'Sec-WebSocket-Accept' header");
+ }
+ SHA1 sha1 = new SHA1();
+ sha1.add("$nonce$_webSocketGUID".charCodes);
+ List<int> expectedAccept = sha1.close();
+ List<int> receivedAccept = _Base64._decode(accept);
+ if (expectedAccept.length != receivedAccept.length) {
+ error("Reasponse header 'Sec-WebSocket-Accept' is the wrong length");
+ }
+ for (int i = 0; i < expectedAccept.length; i++) {
+ if (expectedAccept[i] != receivedAccept[i]) {
+ error("Bad response 'Sec-WebSocket-Accept' header");
+ }
+ }
+ return response.detachSocket()
+ .then((socket) => new _WebSocketImpl._fromSocket(socket));
+ });
+ }
+
+ _WebSocketImpl._fromSocket(Socket this._socket) {
+ _readyState = WebSocket.OPEN;
+
+ int type;
+ var data;
+ _processor.onMessageStart = (int t) {
+ type = t;
+ if (type == _WebSocketMessageType.TEXT) {
+ data = new StringBuffer();
+ } else {
+ data = [];
}
};
- _wsconn.onClosed = (status, reason) {
- _readyState = WebSocket.CLOSED;
- if (_onclose != null) {
- _onclose(new _WebSocketCloseEvent(true, status, reason));
+ _processor.onMessageData = (buffer, offset, count) {
+ if (type == _WebSocketMessageType.TEXT) {
+ data.add(_decodeString(buffer.getRange(offset, count)));
+ } else {
+ data.addAll(buffer.getRange(offset, count));
}
};
- _wsconn.onNoUpgrade = (response) {
- if (_onclose != null) {
- _onclose(
- new _WebSocketCloseEvent(true,
- WebSocketStatus.ABNORMAL_CLOSURE,
- "Connection not upgraded"));
+ _processor.onMessageEnd = () {
+ if (type == _WebSocketMessageType.TEXT) {
+ _controller.add(new _WebSocketMessageEvent(data.toString()));
+ } else {
+ _controller.add(new _WebSocketMessageEvent(data));
}
};
+ _processor.onClosed = (code, reason) {
+ bool clean = true;
+ if (_readyState == WebSocket.OPEN) {
+ _readyState = WebSocket.CLOSING;
+ if (code != WebSocketStatus.NO_STATUS_RECEIVED) {
+ _close(code);
+ } else {
+ _close();
+ clean = false;
+ }
+ _readyState = WebSocket.CLOSED;
+ }
+ _controller.add(new _WebSocketCloseEvent(clean, code, reason));
+ _controller.close();
+ };
+
+ _socket.listen(
+ (data) => _processor.update(data, 0, data.length),
+ onDone: () => _processor.closed(),
+ onError: (error) => _controller.signalError(error));
+ }
+
+ StreamSubscription<Event> listen(void onData(Event event),
+ {void onError(AsyncError error),
+ void onDone(),
+ bool unsubscribeOnError}) {
+ return _controller.stream.listen(onData,
+ onError: onError,
+ onDone: onDone,
+ unsubscribeOnError: unsubscribeOnError);
}
int get readyState => _readyState;
int get bufferedAmount => 0;
- void set onopen(Function callback) {
- _onopen = callback;
- }
-
- void set onerror(Function callback) {}
-
- void set onclose(Function callback) {
- _onclose = callback;
- }
-
String get extensions => null;
String get protocol => null;
- void close(int code, String reason) {
+ void close([int code, String reason]) {
if (_readyState < WebSocket.CLOSING) _readyState = WebSocket.CLOSING;
- _wsconn.close(code, reason);
+ if (code == WebSocketStatus.RESERVED_1004 ||
+ code == WebSocketStatus.NO_STATUS_RECEIVED ||
+ code == WebSocketStatus.RESERVED_1015) {
+ throw new WebSocketException("Reserved status code $code");
+ }
+ _close(code, reason);
}
- void set onmessage(Function callback) {
- _onmessage = callback;
+ void _close([int code, String reason]) {
+ List<int> data;
+ if (code != null) {
+ data = new List<int>();
+ data.add((code >> 8) & 0xFF);
+ data.add(code & 0xFF);
+ if (reason != null) {
+ data.addAll(_encodeString(reason));
+ }
+ }
+ _sendFrame(_WebSocketOpcode.CLOSE, data);
+
+ if (_readyState == WebSocket.CLOSED) {
+ // Close the socket when the close frame has been sent - if it
+ // does not take too long.
+ // TODO(ajohnsen): Honor comment.
+ _socket.destroy();
+ } else {
+ // Half close the socket and expect a close frame in response
+ // before closing the socket. If a close frame does not arrive
+ // within a reasonable amount of time just close the socket.
+ // TODO(ajohnsen): Honor comment.
+ _socket.close();
+ }
}
- void send(data) {
- _wsconn.send(data);
+ void send(message) {
+ if (readyState != WebSocket.OPEN) {
+ throw new StateError("Connection not open");
+ }
+ List<int> data;
+ int opcode;
+ if (message != null) {
+ if (message is String) {
+ opcode = _WebSocketOpcode.TEXT;
+ data = _encodeString(message);
+ } else {
+ if (message is !List<int>) {
+ throw new ArgumentError(message);
+ }
+ opcode = _WebSocketOpcode.BINARY;
+ data = message;
+ }
+ } else {
+ opcode = _WebSocketOpcode.TEXT;
+ }
+ _sendFrame(opcode, data);
}
- WebSocketClientConnection _wsconn;
- int _readyState = WebSocket.CONNECTING;
- Function _onopen;
- Function _onclose;
- Function _onmessage;
+ void _sendFrame(int opcode, [List<int> data]) {
+ bool mask = false; // Masking not implemented for server.
+ int dataLength = data == null ? 0 : data.length;
+ // Determine the header size.
+ int headerSize = (mask) ? 6 : 2;
+ if (dataLength > 65535) {
+ headerSize += 8;
+ } else if (dataLength > 125) {
+ headerSize += 2;
+ }
+ List<int> header = new List<int>.fixedLength(headerSize);
+ int index = 0;
+ // Set FIN and opcode.
+ header[index++] = 0x80 | opcode;
+ // Determine size and position of length field.
+ int lengthBytes = 1;
+ int firstLengthByte = 1;
+ if (dataLength > 65535) {
+ header[index++] = 127;
+ lengthBytes = 8;
+ } else if (dataLength > 125) {
+ header[index++] = 126;
+ lengthBytes = 2;
+ }
+ // Write the length in network byte order into the header.
+ for (int i = 0; i < lengthBytes; i++) {
+ header[index++] = dataLength >> (((lengthBytes - 1) - i) * 8) & 0xFF;
+ }
+ assert(index == headerSize);
+ _socket.add(header);
+ if (data != null) {
+ _socket.add(data);
+ }
+ }
}
diff --git a/sdk/lib/json/json.dart b/sdk/lib/json/json.dart
index 4348767..17147e1 100644
--- a/sdk/lib/json/json.dart
+++ b/sdk/lib/json/json.dart
@@ -4,801 +4,4 @@
library dart.json;
-// JSON parsing and serialization.
-
-/**
- * Error thrown by JSON serialization if an object cannot be serialized.
- *
- * The [unsupportedObject] field holds that object that failed to be serialized.
- *
- * If an object isn't directly serializable, the serializer calls the 'toJson'
- * method on the object. If that call fails, the error will be stored in the
- * [cause] field. If the call returns an object that isn't directly
- * serializable, the [cause] will be null.
- */
-class JsonUnsupportedObjectError implements Error {
- /** The object that could not be serialized. */
- final unsupportedObject;
- /** The exception thrown by object's [:toJson:] method, if any. */
- final cause;
- JsonUnsupportedObjectError(this.unsupportedObject) : cause = null;
- JsonUnsupportedObjectError.withCause(this.unsupportedObject, this.cause);
-
- String toString() {
- if (cause != null) {
- return "Calling toJson method on object failed.";
- } else {
- return "Object toJson method returns non-serializable value.";
- }
- }
-}
-
-
-/**
- * Parses [json] and build the corresponding parsed JSON value.
- *
- * Parsed JSON values are of the types [num], [String], [bool], [Null],
- * [List]s of parsed JSON values or [Map]s from [String] to parsed
- * JSON values.
- *
- * The optional [revivier] function, if provided, is called once for each
- * object or list property parsed. The arguments are the property name
- * ([String]) or list index ([int]), and the value is the parsed value.
- * The return value of the revivier will be used as the value of that property
- * instead the parsed value.
- *
- * Throws [FormatException] if the input is not valid JSON text.
- */
-parse(String json, [reviver(var key, var value)]) {
- BuildJsonListener listener;
- if (reviver == null) {
- listener = new BuildJsonListener();
- } else {
- listener = new ReviverJsonListener(reviver);
- }
- new JsonParser(json, listener).parse();
- return listener.result;
-}
-
-/**
- * Serializes [object] into a JSON string.
- *
- * Directly serializable types are [num], [String], [bool], [Null], [List]
- * and [Map].
- * For [List], the elements must all be serializable.
- * For [Map], the keys must be [String] and the values must be serializable.
- * If a value is any other type is attempted serialized, a "toJson()" method
- * is invoked on the object and the result, which must be a directly
- * serializable type, is serialized instead of the original value.
- * If the object does not support this method, throws, or returns a
- * value that is not directly serializable, a [JsonUnsupportedObjectError]
- * exception is thrown. If the call throws (including the case where there
- * is no nullary "toJson" method, the error is caught and stored in the
- * [JsonUnsupportedObjectError]'s [:cause:] field.
- *Json
- * Objects should not change during serialization.
- * If an object is serialized more than once, [stringify] is allowed to cache
- * the JSON text for it. I.e., if an object changes after it is first
- * serialized, the new values may or may not be reflected in the result.
- */
-String stringify(Object object) {
- return _JsonStringifier.stringify(object);
-}
-
-/**
- * Serializes [object] into [output] stream.
- *
- * Performs the same operations as [stringify] but outputs the resulting
- * string to an existing [StringBuffer] instead of creating a new [String].
- *
- * If serialization fails by throwing, some data might have been added to
- * [output], but it won't contain valid JSON text.
- */
-void printOn(Object object, StringBuffer output) {
- return _JsonStringifier.printOn(object, output);
-}
-
-//// Implementation ///////////////////////////////////////////////////////////
-
-// Simple API for JSON parsing.
-
-abstract class JsonListener {
- void handleString(String value) {}
- void handleNumber(num value) {}
- void handleBool(bool value) {}
- void handleNull() {}
- void beginObject() {}
- void propertyName() {}
- void propertyValue() {}
- void endObject() {}
- void beginArray() {}
- void arrayElement() {}
- void endArray() {}
- /** Called on failure to parse [source]. */
- void fail(String source, int position, String message) {}
-}
-
-/**
- * A [JsonListener] that builds data objects from the parser events.
- *
- * This is a simple stack-based object builder. It keeps the most recently
- * seen value in a variable, and uses it depending on the following event.
- */
-class BuildJsonListener extends JsonListener {
- /**
- * Stack used to handle nested containers.
- *
- * The current container is pushed on the stack when a new one is
- * started. If the container is a [Map], there is also a current [key]
- * which is also stored on the stack.
- */
- List stack = [];
- /** The current [Map] or [List] being built. */
- var currentContainer;
- /** The most recently read property key. */
- String key;
- /** The most recently read value. */
- var value;
-
- /** Pushes the currently active container (and key, if a [Map]). */
- void pushContainer() {
- if (currentContainer is Map) stack.add(key);
- stack.add(currentContainer);
- }
-
- /** Pops the top container from the [stack], including a key if applicable. */
- void popContainer() {
- value = currentContainer;
- currentContainer = stack.removeLast();
- if (currentContainer is Map) key = stack.removeLast();
- }
-
- void handleString(String value) { this.value = value; }
- void handleNumber(num value) { this.value = value; }
- void handleBool(bool value) { this.value = value; }
- void handleNull() { this.value = value; }
-
- void beginObject() {
- pushContainer();
- currentContainer = {};
- }
-
- void propertyName() {
- key = value;
- value = null;
- }
-
- void propertyValue() {
- Map map = currentContainer;
- map[key] = value;
- key = value = null;
- }
-
- void endObject() {
- popContainer();
- }
-
- void beginArray() {
- pushContainer();
- currentContainer = [];
- }
-
- void arrayElement() {
- List list = currentContainer;
- currentContainer.add(value);
- value = null;
- }
-
- void endArray() {
- popContainer();
- }
-
- /** Read out the final result of parsing a JSON string. */
- get result {
- assert(currentContainer == null);
- return value;
- }
-}
-
-typedef _Reviver(var key, var value);
-
-class ReviverJsonListener extends BuildJsonListener {
- final _Reviver reviver;
- ReviverJsonListener(reviver(key, value)) : this.reviver = reviver;
-
- void arrayElement() {
- List list = currentContainer;
- value = reviver(list.length, value);
- super.arrayElement();
- }
-
- void propertyValue() {
- value = reviver(key, value);
- super.propertyValue();
- }
-
- get result {
- return reviver("", value);
- }
-}
-
-class JsonParser {
- // A simple non-recursive state-based parser for JSON.
- //
- // Literal values accepted in states ARRAY_EMPTY, ARRAY_COMMA, OBJECT_COLON
- // and strings also in OBJECT_EMPTY, OBJECT_COMMA.
- // VALUE STRING : , } ] Transitions to
- // EMPTY X X -> END
- // ARRAY_EMPTY X X @ -> ARRAY_VALUE / pop
- // ARRAY_VALUE @ @ -> ARRAY_COMMA / pop
- // ARRAY_COMMA X X -> ARRAY_VALUE
- // OBJECT_EMPTY X @ -> OBJECT_KEY / pop
- // OBJECT_KEY @ -> OBJECT_COLON
- // OBJECT_COLON X X -> OBJECT_VALUE
- // OBJECT_VALUE @ @ -> OBJECT_COMMA / pop
- // OBJECT_COMMA X -> OBJECT_KEY
- // END
- // Starting a new array or object will push the current state. The "pop"
- // above means restoring this state and then marking it as an ended value.
- // X means generic handling, @ means special handling for just that
- // state - that is, values are handled generically, only punctuation
- // cares about the current state.
- // Values for states are chosen so bits 0 and 1 tell whether
- // a string/value is allowed, and setting bits 0 through 2 after a value
- // gets to the next state (not empty, doesn't allow a value).
-
- // State building-block constants.
- static const int INSIDE_ARRAY = 1;
- static const int INSIDE_OBJECT = 2;
- static const int AFTER_COLON = 3; // Always inside object.
-
- static const int ALLOW_STRING_MASK = 8; // Allowed if zero.
- static const int ALLOW_VALUE_MASK = 4; // Allowed if zero.
- static const int ALLOW_VALUE = 0;
- static const int STRING_ONLY = 4;
- static const int NO_VALUES = 12;
-
- // Objects and arrays are "empty" until their first property/element.
- static const int EMPTY = 0;
- static const int NON_EMPTY = 16;
- static const int EMPTY_MASK = 16; // Empty if zero.
-
-
- static const int VALUE_READ_BITS = NO_VALUES | NON_EMPTY;
-
- // Actual states.
- static const int STATE_INITIAL = EMPTY | ALLOW_VALUE;
- static const int STATE_END = NON_EMPTY | NO_VALUES;
-
- static const int STATE_ARRAY_EMPTY = INSIDE_ARRAY | EMPTY | ALLOW_VALUE;
- static const int STATE_ARRAY_VALUE = INSIDE_ARRAY | NON_EMPTY | NO_VALUES;
- static const int STATE_ARRAY_COMMA = INSIDE_ARRAY | NON_EMPTY | ALLOW_VALUE;
-
- static const int STATE_OBJECT_EMPTY = INSIDE_OBJECT | EMPTY | STRING_ONLY;
- static const int STATE_OBJECT_KEY = INSIDE_OBJECT | NON_EMPTY | NO_VALUES;
- static const int STATE_OBJECT_COLON = AFTER_COLON | NON_EMPTY | ALLOW_VALUE;
- static const int STATE_OBJECT_VALUE = AFTER_COLON | NON_EMPTY | NO_VALUES;
- static const int STATE_OBJECT_COMMA = INSIDE_OBJECT | NON_EMPTY | STRING_ONLY;
-
- // Character code constants.
- static const int BACKSPACE = 0x08;
- static const int TAB = 0x09;
- static const int NEWLINE = 0x0a;
- static const int CARRIAGE_RETURN = 0x0d;
- static const int FORM_FEED = 0x0c;
- static const int SPACE = 0x20;
- static const int QUOTE = 0x22;
- static const int PLUS = 0x2b;
- static const int COMMA = 0x2c;
- static const int MINUS = 0x2d;
- static const int DECIMALPOINT = 0x2e;
- static const int SLASH = 0x2f;
- static const int CHAR_0 = 0x30;
- static const int CHAR_9 = 0x39;
- static const int COLON = 0x3a;
- static const int CHAR_E = 0x45;
- static const int LBRACKET = 0x5b;
- static const int BACKSLASH = 0x5c;
- static const int RBRACKET = 0x5d;
- static const int CHAR_a = 0x61;
- static const int CHAR_b = 0x62;
- static const int CHAR_e = 0x65;
- static const int CHAR_f = 0x66;
- static const int CHAR_l = 0x6c;
- static const int CHAR_n = 0x6e;
- static const int CHAR_r = 0x72;
- static const int CHAR_s = 0x73;
- static const int CHAR_t = 0x74;
- static const int CHAR_u = 0x75;
- static const int LBRACE = 0x7b;
- static const int RBRACE = 0x7d;
-
- final String source;
- final JsonListener listener;
- JsonParser(this.source, this.listener);
-
- /** Parses [source], or throws if it fails. */
- void parse() {
- final List<int> states = <int>[];
- int state = STATE_INITIAL;
- int position = 0;
- int length = source.length;
- while (position < length) {
- int char = source.charCodeAt(position);
- switch (char) {
- case SPACE:
- case CARRIAGE_RETURN:
- case NEWLINE:
- case TAB:
- position++;
- break;
- case QUOTE:
- if ((state & ALLOW_STRING_MASK) != 0) fail(position);
- position = parseString(position + 1);
- state |= VALUE_READ_BITS;
- break;
- case LBRACKET:
- if ((state & ALLOW_VALUE_MASK) != 0) fail(position);
- listener.beginArray();
- states.add(state);
- state = STATE_ARRAY_EMPTY;
- position++;
- break;
- case LBRACE:
- if ((state & ALLOW_VALUE_MASK) != 0) fail(position);
- listener.beginObject();
- states.add(state);
- state = STATE_OBJECT_EMPTY;
- position++;
- break;
- case CHAR_n:
- if ((state & ALLOW_VALUE_MASK) != 0) fail(position);
- position = parseNull(position);
- state |= VALUE_READ_BITS;
- break;
- case CHAR_f:
- if ((state & ALLOW_VALUE_MASK) != 0) fail(position);
- position = parseFalse(position);
- state |= VALUE_READ_BITS;
- break;
- case CHAR_t:
- if ((state & ALLOW_VALUE_MASK) != 0) fail(position);
- position = parseTrue(position);
- state |= VALUE_READ_BITS;
- break;
- case COLON:
- if (state != STATE_OBJECT_KEY) fail(position);
- listener.propertyName();
- state = STATE_OBJECT_COLON;
- position++;
- break;
- case COMMA:
- if (state == STATE_OBJECT_VALUE) {
- listener.propertyValue();
- state = STATE_OBJECT_COMMA;
- position++;
- } else if (state == STATE_ARRAY_VALUE) {
- listener.arrayElement();
- state = STATE_ARRAY_COMMA;
- position++;
- } else {
- fail(position);
- }
- break;
- case RBRACKET:
- if (state == STATE_ARRAY_EMPTY) {
- listener.endArray();
- } else if (state == STATE_ARRAY_VALUE) {
- listener.arrayElement();
- listener.endArray();
- } else {
- fail(position);
- }
- state = states.removeLast() | VALUE_READ_BITS;
- position++;
- break;
- case RBRACE:
- if (state == STATE_OBJECT_EMPTY) {
- listener.endObject();
- } else if (state == STATE_OBJECT_VALUE) {
- listener.propertyValue();
- listener.endObject();
- } else {
- fail(position);
- }
- state = states.removeLast() | VALUE_READ_BITS;
- position++;
- break;
- default:
- if ((state & ALLOW_VALUE_MASK) != 0) fail(position);
- position = parseNumber(char, position);
- state |= VALUE_READ_BITS;
- break;
- }
- }
- if (state != STATE_END) fail(position);
- }
-
- /**
- * Parses a "true" literal starting at [position].
- *
- * [:source[position]:] must be "t".
- */
- int parseTrue(int position) {
- assert(source.charCodeAt(position) == CHAR_t);
- if (source.length < position + 4) fail(position, "Unexpected identifier");
- if (source.charCodeAt(position + 1) != CHAR_r ||
- source.charCodeAt(position + 2) != CHAR_u ||
- source.charCodeAt(position + 3) != CHAR_e) {
- fail(position);
- }
- listener.handleBool(true);
- return position + 4;
- }
-
- /**
- * Parses a "false" literal starting at [position].
- *
- * [:source[position]:] must be "f".
- */
- int parseFalse(int position) {
- assert(source.charCodeAt(position) == CHAR_f);
- if (source.length < position + 5) fail(position, "Unexpected identifier");
- if (source.charCodeAt(position + 1) != CHAR_a ||
- source.charCodeAt(position + 2) != CHAR_l ||
- source.charCodeAt(position + 3) != CHAR_s ||
- source.charCodeAt(position + 4) != CHAR_e) {
- fail(position);
- }
- listener.handleBool(false);
- return position + 5;
- }
-
- /** Parses a "null" literal starting at [position].
- *
- * [:source[position]:] must be "n".
- */
- int parseNull(int position) {
- assert(source.charCodeAt(position) == CHAR_n);
- if (source.length < position + 4) fail(position, "Unexpected identifier");
- if (source.charCodeAt(position + 1) != CHAR_u ||
- source.charCodeAt(position + 2) != CHAR_l ||
- source.charCodeAt(position + 3) != CHAR_l) {
- fail(position);
- }
- listener.handleNull();
- return position + 4;
- }
-
- int parseString(int position) {
- // Format: '"'([^\x00-\x1f\\\"]|'\\'[bfnrt/\\"])*'"'
- // Initial position is right after first '"'.
- int start = position;
- int char;
- do {
- if (position == source.length) {
- fail(start - 1, "Unterminated string");
- }
- char = source.charCodeAt(position);
- if (char == QUOTE) {
- listener.handleString(source.substring(start, position));
- return position + 1;
- }
- if (char < SPACE) {
- fail(position, "Control character in string");
- }
- position++;
- } while (char != BACKSLASH);
- // Backslash escape detected. Collect character codes for rest of string.
- int firstEscape = position - 1;
- List<int> chars = <int>[];
- while (true) {
- if (position == source.length) {
- fail(start - 1, "Unterminated string");
- }
- char = source.charCodeAt(position);
- switch (char) {
- case CHAR_b: char = BACKSPACE; break;
- case CHAR_f: char = FORM_FEED; break;
- case CHAR_n: char = NEWLINE; break;
- case CHAR_r: char = CARRIAGE_RETURN; break;
- case CHAR_t: char = TAB; break;
- case SLASH:
- case BACKSLASH:
- case QUOTE:
- break;
- case CHAR_u:
- int hexStart = position - 1;
- int value = 0;
- for (int i = 0; i < 4; i++) {
- position++;
- if (position == source.length) {
- fail(start - 1, "Unterminated string");
- }
- char = source.charCodeAt(position);
- char -= 0x30;
- if (char < 0) fail(hexStart, "Invalid unicode escape");
- if (char < 10) {
- value = value * 16 + char;
- } else {
- char = (char | 0x20) - 0x31;
- if (char < 0 || char > 5) {
- fail(hexStart, "Invalid unicode escape");
- }
- value = value * 16 + char + 10;
- }
- }
- char = value;
- break;
- default:
- if (char < SPACE) fail(position, "Control character in string");
- fail(position, "Unrecognized string escape");
- }
- do {
- chars.add(char);
- position++;
- if (position == source.length) fail(start - 1, "Unterminated string");
- char = source.charCodeAt(position);
- if (char == QUOTE) {
- String result = new String.fromCharCodes(chars);
- if (start < firstEscape) {
- result = "${source.substring(start, firstEscape)}$result";
- }
- listener.handleString(result);
- return position + 1;
- }
- if (char < SPACE) {
- fail(position, "Control character in string");
- }
- } while (char != BACKSLASH);
- position++;
- }
- }
-
- int parseNumber(int char, int position) {
- // Format:
- // '-'?('0'|[1-9][0-9]*)('.'[0-9]+)?([eE][+-]?[0-9]+)?
- int start = position;
- int length = source.length;
- bool isDouble = false;
- if (char == MINUS) {
- position++;
- if (position == length) fail(position, "Missing expected digit");
- char = source.charCodeAt(position);
- }
- if (char < CHAR_0 || char > CHAR_9) {
- fail(position, "Missing expected digit");
- }
- int handleLiteral(position) {
- String literal = source.substring(start, position);
- // This correctly creates -0 for doubles.
- num value = (isDouble ? double.parse(literal) : int.parse(literal));
- listener.handleNumber(value);
- return position;
- }
- if (char == CHAR_0) {
- position++;
- if (position == length) return handleLiteral(position);
- char = source.charCodeAt(position);
- if (CHAR_0 <= char && char <= CHAR_9) {
- fail(position);
- }
- } else {
- do {
- position++;
- if (position == length) return handleLiteral(position);
- char = source.charCodeAt(position);
- } while (CHAR_0 <= char && char <= CHAR_9);
- }
- if (char == DECIMALPOINT) {
- isDouble = true;
- position++;
- if (position == length) fail(position, "Missing expected digit");
- char = source.charCodeAt(position);
- if (char < CHAR_0 || char > CHAR_9) fail(position);
- do {
- position++;
- if (position == length) return handleLiteral(position);
- char = source.charCodeAt(position);
- } while (CHAR_0 <= char && char <= CHAR_9);
- }
- if (char == CHAR_e || char == CHAR_E) {
- isDouble = true;
- position++;
- if (position == length) fail(position, "Missing expected digit");
- char = source.charCodeAt(position);
- if (char == PLUS || char == MINUS) {
- position++;
- if (position == length) fail(position, "Missing expected digit");
- char = source.charCodeAt(position);
- }
- if (char < CHAR_0 || char > CHAR_9) {
- fail(position, "Missing expected digit");
- }
- do {
- position++;
- if (position == length) return handleLiteral(position);
- char = source.charCodeAt(position);
- } while (CHAR_0 <= char && char <= CHAR_9);
- }
- return handleLiteral(position);
- }
-
- void fail(int position, [String message]) {
- if (message == null) message = "Unexpected character";
- listener.fail(source, position, message);
- // If the listener didn't throw, do it here.
- String slice;
- int sliceEnd = position + 20;
- if (sliceEnd > source.length) {
- slice = "'${source.substring(position)}'";
- } else {
- slice = "'${source.substring(position, sliceEnd)}...'";
- }
- throw new FormatException("Unexpected character at $position: $slice");
- }
-}
-
-
-class _JsonStringifier {
- StringBuffer sb;
- List<Object> seen; // TODO: that should be identity set.
-
- _JsonStringifier(this.sb) : seen = [];
-
- static String stringify(final object) {
- StringBuffer output = new StringBuffer();
- _JsonStringifier stringifier = new _JsonStringifier(output);
- stringifier.stringifyValue(object);
- return output.toString();
- }
-
- static void printOn(final object, StringBuffer output) {
- _JsonStringifier stringifier = new _JsonStringifier(output);
- stringifier.stringifyValue(object);
- }
-
- static String numberToString(num x) {
- return x.toString();
- }
-
- // ('0' + x) or ('a' + x - 10)
- static int hexDigit(int x) => x < 10 ? 48 + x : 87 + x;
-
- static void escape(StringBuffer sb, String s) {
- final int length = s.length;
- bool needsEscape = false;
- final charCodes = new List<int>();
- for (int i = 0; i < length; i++) {
- int charCode = s.charCodeAt(i);
- if (charCode < 32) {
- needsEscape = true;
- charCodes.add(JsonParser.BACKSLASH);
- switch (charCode) {
- case JsonParser.BACKSPACE:
- charCodes.add(JsonParser.CHAR_b);
- break;
- case JsonParser.TAB:
- charCodes.add(JsonParser.CHAR_t);
- break;
- case JsonParser.NEWLINE:
- charCodes.add(JsonParser.CHAR_n);
- break;
- case JsonParser.FORM_FEED:
- charCodes.add(JsonParser.CHAR_f);
- break;
- case JsonParser.CARRIAGE_RETURN:
- charCodes.add(JsonParser.CHAR_r);
- break;
- default:
- charCodes.add(JsonParser.CHAR_u);
- charCodes.add(hexDigit((charCode >> 12) & 0xf));
- charCodes.add(hexDigit((charCode >> 8) & 0xf));
- charCodes.add(hexDigit((charCode >> 4) & 0xf));
- charCodes.add(hexDigit(charCode & 0xf));
- break;
- }
- } else if (charCode == JsonParser.QUOTE ||
- charCode == JsonParser.BACKSLASH) {
- needsEscape = true;
- charCodes.add(JsonParser.BACKSLASH);
- charCodes.add(charCode);
- } else {
- charCodes.add(charCode);
- }
- }
- sb.add(needsEscape ? new String.fromCharCodes(charCodes) : s);
- }
-
- void checkCycle(final object) {
- // TODO: use Iterables.
- for (int i = 0; i < seen.length; i++) {
- if (identical(seen[i], object)) {
- throw 'Cyclic structure';
- }
- }
- seen.add(object);
- }
-
- void stringifyValue(final object) {
- // Tries stringifying object directly. If it's not a simple value, List or
- // Map, call toJson() to get a custom representation and try serializing
- // that.
- if (!stringifyJsonValue(object)) {
- checkCycle(object);
- try {
- var customJson = object.toJson();
- if (!stringifyJsonValue(customJson)) {
- throw new JsonUnsupportedObjectError(object);
- }
- seen.removeLast();
- } catch (e) {
- throw new JsonUnsupportedObjectError.withCause(object, e);
- }
- }
- }
-
- /**
- * Serializes a [num], [String], [bool], [Null], [List] or [Map] value.
- *
- * Returns true if the value is one of these types, and false if not.
- * If a value is both a [List] and a [Map], it's serialized as a [List].
- */
- bool stringifyJsonValue(final object) {
- if (object is num) {
- // TODO: use writeOn.
- sb.add(numberToString(object));
- return true;
- } else if (identical(object, true)) {
- sb.add('true');
- return true;
- } else if (identical(object, false)) {
- sb.add('false');
- return true;
- } else if (object == null) {
- sb.add('null');
- return true;
- } else if (object is String) {
- sb.add('"');
- escape(sb, object);
- sb.add('"');
- return true;
- } else if (object is List) {
- checkCycle(object);
- List a = object;
- sb.add('[');
- if (a.length > 0) {
- stringifyValue(a[0]);
- // TODO: switch to Iterables.
- for (int i = 1; i < a.length; i++) {
- sb.add(',');
- stringifyValue(a[i]);
- }
- }
- sb.add(']');
- seen.removeLast();
- return true;
- } else if (object is Map) {
- checkCycle(object);
- Map<String, Object> m = object;
- sb.add('{');
- bool first = true;
- m.forEach((String key, Object value) {
- if (!first) {
- sb.add(',"');
- } else {
- sb.add('"');
- }
- escape(sb, key);
- sb.add('":');
- stringifyValue(value);
- first = false;
- });
- sb.add('}');
- seen.removeLast();
- return true;
- } else {
- return false;
- }
- }
-}
+part 'json_base.dart';
diff --git a/sdk/lib/json/json_base.dart b/sdk/lib/json/json_base.dart
new file mode 100644
index 0000000..9f74d65
--- /dev/null
+++ b/sdk/lib/json/json_base.dart
@@ -0,0 +1,802 @@
+// 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.
+
+// JSON parsing and serialization.
+
+/**
+ * Error thrown by JSON serialization if an object cannot be serialized.
+ *
+ * The [unsupportedObject] field holds that object that failed to be serialized.
+ *
+ * If an object isn't directly serializable, the serializer calls the 'toJson'
+ * method on the object. If that call fails, the error will be stored in the
+ * [cause] field. If the call returns an object that isn't directly
+ * serializable, the [cause] will be null.
+ */
+class JsonUnsupportedObjectError implements Error {
+ /** The object that could not be serialized. */
+ final unsupportedObject;
+ /** The exception thrown by object's [:toJson:] method, if any. */
+ final cause;
+ JsonUnsupportedObjectError(this.unsupportedObject) : cause = null;
+ JsonUnsupportedObjectError.withCause(this.unsupportedObject, this.cause);
+
+ String toString() {
+ if (cause != null) {
+ return "Calling toJson method on object failed.";
+ } else {
+ return "Object toJson method returns non-serializable value.";
+ }
+ }
+}
+
+
+/**
+ * Parses [json] and build the corresponding parsed JSON value.
+ *
+ * Parsed JSON values are of the types [num], [String], [bool], [Null],
+ * [List]s of parsed JSON values or [Map]s from [String] to parsed
+ * JSON values.
+ *
+ * The optional [revivier] function, if provided, is called once for each
+ * object or list property parsed. The arguments are the property name
+ * ([String]) or list index ([int]), and the value is the parsed value.
+ * The return value of the revivier will be used as the value of that property
+ * instead the parsed value.
+ *
+ * Throws [FormatException] if the input is not valid JSON text.
+ */
+parse(String json, [reviver(var key, var value)]) {
+ BuildJsonListener listener;
+ if (reviver == null) {
+ listener = new BuildJsonListener();
+ } else {
+ listener = new ReviverJsonListener(reviver);
+ }
+ new JsonParser(json, listener).parse();
+ return listener.result;
+}
+
+/**
+ * Serializes [object] into a JSON string.
+ *
+ * Directly serializable types are [num], [String], [bool], [Null], [List]
+ * and [Map].
+ * For [List], the elements must all be serializable.
+ * For [Map], the keys must be [String] and the values must be serializable.
+ * If a value is any other type is attempted serialized, a "toJson()" method
+ * is invoked on the object and the result, which must be a directly
+ * serializable type, is serialized instead of the original value.
+ * If the object does not support this method, throws, or returns a
+ * value that is not directly serializable, a [JsonUnsupportedObjectError]
+ * exception is thrown. If the call throws (including the case where there
+ * is no nullary "toJson" method, the error is caught and stored in the
+ * [JsonUnsupportedObjectError]'s [:cause:] field.
+ *Json
+ * Objects should not change during serialization.
+ * If an object is serialized more than once, [stringify] is allowed to cache
+ * the JSON text for it. I.e., if an object changes after it is first
+ * serialized, the new values may or may not be reflected in the result.
+ */
+String stringify(Object object) {
+ return _JsonStringifier.stringify(object);
+}
+
+/**
+ * Serializes [object] into [output] stream.
+ *
+ * Performs the same operations as [stringify] but outputs the resulting
+ * string to an existing [StringBuffer] instead of creating a new [String].
+ *
+ * If serialization fails by throwing, some data might have been added to
+ * [output], but it won't contain valid JSON text.
+ */
+void printOn(Object object, StringBuffer output) {
+ return _JsonStringifier.printOn(object, output);
+}
+
+//// Implementation ///////////////////////////////////////////////////////////
+
+// Simple API for JSON parsing.
+
+abstract class JsonListener {
+ void handleString(String value) {}
+ void handleNumber(num value) {}
+ void handleBool(bool value) {}
+ void handleNull() {}
+ void beginObject() {}
+ void propertyName() {}
+ void propertyValue() {}
+ void endObject() {}
+ void beginArray() {}
+ void arrayElement() {}
+ void endArray() {}
+ /** Called on failure to parse [source]. */
+ void fail(String source, int position, String message) {}
+}
+
+/**
+ * A [JsonListener] that builds data objects from the parser events.
+ *
+ * This is a simple stack-based object builder. It keeps the most recently
+ * seen value in a variable, and uses it depending on the following event.
+ */
+class BuildJsonListener extends JsonListener {
+ /**
+ * Stack used to handle nested containers.
+ *
+ * The current container is pushed on the stack when a new one is
+ * started. If the container is a [Map], there is also a current [key]
+ * which is also stored on the stack.
+ */
+ List stack = [];
+ /** The current [Map] or [List] being built. */
+ var currentContainer;
+ /** The most recently read property key. */
+ String key;
+ /** The most recently read value. */
+ var value;
+
+ /** Pushes the currently active container (and key, if a [Map]). */
+ void pushContainer() {
+ if (currentContainer is Map) stack.add(key);
+ stack.add(currentContainer);
+ }
+
+ /** Pops the top container from the [stack], including a key if applicable. */
+ void popContainer() {
+ value = currentContainer;
+ currentContainer = stack.removeLast();
+ if (currentContainer is Map) key = stack.removeLast();
+ }
+
+ void handleString(String value) { this.value = value; }
+ void handleNumber(num value) { this.value = value; }
+ void handleBool(bool value) { this.value = value; }
+ void handleNull() { this.value = value; }
+
+ void beginObject() {
+ pushContainer();
+ currentContainer = {};
+ }
+
+ void propertyName() {
+ key = value;
+ value = null;
+ }
+
+ void propertyValue() {
+ Map map = currentContainer;
+ map[key] = value;
+ key = value = null;
+ }
+
+ void endObject() {
+ popContainer();
+ }
+
+ void beginArray() {
+ pushContainer();
+ currentContainer = [];
+ }
+
+ void arrayElement() {
+ List list = currentContainer;
+ currentContainer.add(value);
+ value = null;
+ }
+
+ void endArray() {
+ popContainer();
+ }
+
+ /** Read out the final result of parsing a JSON string. */
+ get result {
+ assert(currentContainer == null);
+ return value;
+ }
+}
+
+typedef _Reviver(var key, var value);
+
+class ReviverJsonListener extends BuildJsonListener {
+ final _Reviver reviver;
+ ReviverJsonListener(reviver(key, value)) : this.reviver = reviver;
+
+ void arrayElement() {
+ List list = currentContainer;
+ value = reviver(list.length, value);
+ super.arrayElement();
+ }
+
+ void propertyValue() {
+ value = reviver(key, value);
+ super.propertyValue();
+ }
+
+ get result {
+ return reviver("", value);
+ }
+}
+
+class JsonParser {
+ // A simple non-recursive state-based parser for JSON.
+ //
+ // Literal values accepted in states ARRAY_EMPTY, ARRAY_COMMA, OBJECT_COLON
+ // and strings also in OBJECT_EMPTY, OBJECT_COMMA.
+ // VALUE STRING : , } ] Transitions to
+ // EMPTY X X -> END
+ // ARRAY_EMPTY X X @ -> ARRAY_VALUE / pop
+ // ARRAY_VALUE @ @ -> ARRAY_COMMA / pop
+ // ARRAY_COMMA X X -> ARRAY_VALUE
+ // OBJECT_EMPTY X @ -> OBJECT_KEY / pop
+ // OBJECT_KEY @ -> OBJECT_COLON
+ // OBJECT_COLON X X -> OBJECT_VALUE
+ // OBJECT_VALUE @ @ -> OBJECT_COMMA / pop
+ // OBJECT_COMMA X -> OBJECT_KEY
+ // END
+ // Starting a new array or object will push the current state. The "pop"
+ // above means restoring this state and then marking it as an ended value.
+ // X means generic handling, @ means special handling for just that
+ // state - that is, values are handled generically, only punctuation
+ // cares about the current state.
+ // Values for states are chosen so bits 0 and 1 tell whether
+ // a string/value is allowed, and setting bits 0 through 2 after a value
+ // gets to the next state (not empty, doesn't allow a value).
+
+ // State building-block constants.
+ static const int INSIDE_ARRAY = 1;
+ static const int INSIDE_OBJECT = 2;
+ static const int AFTER_COLON = 3; // Always inside object.
+
+ static const int ALLOW_STRING_MASK = 8; // Allowed if zero.
+ static const int ALLOW_VALUE_MASK = 4; // Allowed if zero.
+ static const int ALLOW_VALUE = 0;
+ static const int STRING_ONLY = 4;
+ static const int NO_VALUES = 12;
+
+ // Objects and arrays are "empty" until their first property/element.
+ static const int EMPTY = 0;
+ static const int NON_EMPTY = 16;
+ static const int EMPTY_MASK = 16; // Empty if zero.
+
+
+ static const int VALUE_READ_BITS = NO_VALUES | NON_EMPTY;
+
+ // Actual states.
+ static const int STATE_INITIAL = EMPTY | ALLOW_VALUE;
+ static const int STATE_END = NON_EMPTY | NO_VALUES;
+
+ static const int STATE_ARRAY_EMPTY = INSIDE_ARRAY | EMPTY | ALLOW_VALUE;
+ static const int STATE_ARRAY_VALUE = INSIDE_ARRAY | NON_EMPTY | NO_VALUES;
+ static const int STATE_ARRAY_COMMA = INSIDE_ARRAY | NON_EMPTY | ALLOW_VALUE;
+
+ static const int STATE_OBJECT_EMPTY = INSIDE_OBJECT | EMPTY | STRING_ONLY;
+ static const int STATE_OBJECT_KEY = INSIDE_OBJECT | NON_EMPTY | NO_VALUES;
+ static const int STATE_OBJECT_COLON = AFTER_COLON | NON_EMPTY | ALLOW_VALUE;
+ static const int STATE_OBJECT_VALUE = AFTER_COLON | NON_EMPTY | NO_VALUES;
+ static const int STATE_OBJECT_COMMA = INSIDE_OBJECT | NON_EMPTY | STRING_ONLY;
+
+ // Character code constants.
+ static const int BACKSPACE = 0x08;
+ static const int TAB = 0x09;
+ static const int NEWLINE = 0x0a;
+ static const int CARRIAGE_RETURN = 0x0d;
+ static const int FORM_FEED = 0x0c;
+ static const int SPACE = 0x20;
+ static const int QUOTE = 0x22;
+ static const int PLUS = 0x2b;
+ static const int COMMA = 0x2c;
+ static const int MINUS = 0x2d;
+ static const int DECIMALPOINT = 0x2e;
+ static const int SLASH = 0x2f;
+ static const int CHAR_0 = 0x30;
+ static const int CHAR_9 = 0x39;
+ static const int COLON = 0x3a;
+ static const int CHAR_E = 0x45;
+ static const int LBRACKET = 0x5b;
+ static const int BACKSLASH = 0x5c;
+ static const int RBRACKET = 0x5d;
+ static const int CHAR_a = 0x61;
+ static const int CHAR_b = 0x62;
+ static const int CHAR_e = 0x65;
+ static const int CHAR_f = 0x66;
+ static const int CHAR_l = 0x6c;
+ static const int CHAR_n = 0x6e;
+ static const int CHAR_r = 0x72;
+ static const int CHAR_s = 0x73;
+ static const int CHAR_t = 0x74;
+ static const int CHAR_u = 0x75;
+ static const int LBRACE = 0x7b;
+ static const int RBRACE = 0x7d;
+
+ final String source;
+ final JsonListener listener;
+ JsonParser(this.source, this.listener);
+
+ /** Parses [source], or throws if it fails. */
+ void parse() {
+ final List<int> states = <int>[];
+ int state = STATE_INITIAL;
+ int position = 0;
+ int length = source.length;
+ while (position < length) {
+ int char = source.charCodeAt(position);
+ switch (char) {
+ case SPACE:
+ case CARRIAGE_RETURN:
+ case NEWLINE:
+ case TAB:
+ position++;
+ break;
+ case QUOTE:
+ if ((state & ALLOW_STRING_MASK) != 0) fail(position);
+ position = parseString(position + 1);
+ state |= VALUE_READ_BITS;
+ break;
+ case LBRACKET:
+ if ((state & ALLOW_VALUE_MASK) != 0) fail(position);
+ listener.beginArray();
+ states.add(state);
+ state = STATE_ARRAY_EMPTY;
+ position++;
+ break;
+ case LBRACE:
+ if ((state & ALLOW_VALUE_MASK) != 0) fail(position);
+ listener.beginObject();
+ states.add(state);
+ state = STATE_OBJECT_EMPTY;
+ position++;
+ break;
+ case CHAR_n:
+ if ((state & ALLOW_VALUE_MASK) != 0) fail(position);
+ position = parseNull(position);
+ state |= VALUE_READ_BITS;
+ break;
+ case CHAR_f:
+ if ((state & ALLOW_VALUE_MASK) != 0) fail(position);
+ position = parseFalse(position);
+ state |= VALUE_READ_BITS;
+ break;
+ case CHAR_t:
+ if ((state & ALLOW_VALUE_MASK) != 0) fail(position);
+ position = parseTrue(position);
+ state |= VALUE_READ_BITS;
+ break;
+ case COLON:
+ if (state != STATE_OBJECT_KEY) fail(position);
+ listener.propertyName();
+ state = STATE_OBJECT_COLON;
+ position++;
+ break;
+ case COMMA:
+ if (state == STATE_OBJECT_VALUE) {
+ listener.propertyValue();
+ state = STATE_OBJECT_COMMA;
+ position++;
+ } else if (state == STATE_ARRAY_VALUE) {
+ listener.arrayElement();
+ state = STATE_ARRAY_COMMA;
+ position++;
+ } else {
+ fail(position);
+ }
+ break;
+ case RBRACKET:
+ if (state == STATE_ARRAY_EMPTY) {
+ listener.endArray();
+ } else if (state == STATE_ARRAY_VALUE) {
+ listener.arrayElement();
+ listener.endArray();
+ } else {
+ fail(position);
+ }
+ state = states.removeLast() | VALUE_READ_BITS;
+ position++;
+ break;
+ case RBRACE:
+ if (state == STATE_OBJECT_EMPTY) {
+ listener.endObject();
+ } else if (state == STATE_OBJECT_VALUE) {
+ listener.propertyValue();
+ listener.endObject();
+ } else {
+ fail(position);
+ }
+ state = states.removeLast() | VALUE_READ_BITS;
+ position++;
+ break;
+ default:
+ if ((state & ALLOW_VALUE_MASK) != 0) fail(position);
+ position = parseNumber(char, position);
+ state |= VALUE_READ_BITS;
+ break;
+ }
+ }
+ if (state != STATE_END) fail(position);
+ }
+
+ /**
+ * Parses a "true" literal starting at [position].
+ *
+ * [:source[position]:] must be "t".
+ */
+ int parseTrue(int position) {
+ assert(source.charCodeAt(position) == CHAR_t);
+ if (source.length < position + 4) fail(position, "Unexpected identifier");
+ if (source.charCodeAt(position + 1) != CHAR_r ||
+ source.charCodeAt(position + 2) != CHAR_u ||
+ source.charCodeAt(position + 3) != CHAR_e) {
+ fail(position);
+ }
+ listener.handleBool(true);
+ return position + 4;
+ }
+
+ /**
+ * Parses a "false" literal starting at [position].
+ *
+ * [:source[position]:] must be "f".
+ */
+ int parseFalse(int position) {
+ assert(source.charCodeAt(position) == CHAR_f);
+ if (source.length < position + 5) fail(position, "Unexpected identifier");
+ if (source.charCodeAt(position + 1) != CHAR_a ||
+ source.charCodeAt(position + 2) != CHAR_l ||
+ source.charCodeAt(position + 3) != CHAR_s ||
+ source.charCodeAt(position + 4) != CHAR_e) {
+ fail(position);
+ }
+ listener.handleBool(false);
+ return position + 5;
+ }
+
+ /** Parses a "null" literal starting at [position].
+ *
+ * [:source[position]:] must be "n".
+ */
+ int parseNull(int position) {
+ assert(source.charCodeAt(position) == CHAR_n);
+ if (source.length < position + 4) fail(position, "Unexpected identifier");
+ if (source.charCodeAt(position + 1) != CHAR_u ||
+ source.charCodeAt(position + 2) != CHAR_l ||
+ source.charCodeAt(position + 3) != CHAR_l) {
+ fail(position);
+ }
+ listener.handleNull();
+ return position + 4;
+ }
+
+ int parseString(int position) {
+ // Format: '"'([^\x00-\x1f\\\"]|'\\'[bfnrt/\\"])*'"'
+ // Initial position is right after first '"'.
+ int start = position;
+ int char;
+ do {
+ if (position == source.length) {
+ fail(start - 1, "Unterminated string");
+ }
+ char = source.charCodeAt(position);
+ if (char == QUOTE) {
+ listener.handleString(source.substring(start, position));
+ return position + 1;
+ }
+ if (char < SPACE) {
+ fail(position, "Control character in string");
+ }
+ position++;
+ } while (char != BACKSLASH);
+ // Backslash escape detected. Collect character codes for rest of string.
+ int firstEscape = position - 1;
+ List<int> chars = <int>[];
+ while (true) {
+ if (position == source.length) {
+ fail(start - 1, "Unterminated string");
+ }
+ char = source.charCodeAt(position);
+ switch (char) {
+ case CHAR_b: char = BACKSPACE; break;
+ case CHAR_f: char = FORM_FEED; break;
+ case CHAR_n: char = NEWLINE; break;
+ case CHAR_r: char = CARRIAGE_RETURN; break;
+ case CHAR_t: char = TAB; break;
+ case SLASH:
+ case BACKSLASH:
+ case QUOTE:
+ break;
+ case CHAR_u:
+ int hexStart = position - 1;
+ int value = 0;
+ for (int i = 0; i < 4; i++) {
+ position++;
+ if (position == source.length) {
+ fail(start - 1, "Unterminated string");
+ }
+ char = source.charCodeAt(position);
+ char -= 0x30;
+ if (char < 0) fail(hexStart, "Invalid unicode escape");
+ if (char < 10) {
+ value = value * 16 + char;
+ } else {
+ char = (char | 0x20) - 0x31;
+ if (char < 0 || char > 5) {
+ fail(hexStart, "Invalid unicode escape");
+ }
+ value = value * 16 + char + 10;
+ }
+ }
+ char = value;
+ break;
+ default:
+ if (char < SPACE) fail(position, "Control character in string");
+ fail(position, "Unrecognized string escape");
+ }
+ do {
+ chars.add(char);
+ position++;
+ if (position == source.length) fail(start - 1, "Unterminated string");
+ char = source.charCodeAt(position);
+ if (char == QUOTE) {
+ String result = new String.fromCharCodes(chars);
+ if (start < firstEscape) {
+ result = "${source.substring(start, firstEscape)}$result";
+ }
+ listener.handleString(result);
+ return position + 1;
+ }
+ if (char < SPACE) {
+ fail(position, "Control character in string");
+ }
+ } while (char != BACKSLASH);
+ position++;
+ }
+ }
+
+ int parseNumber(int char, int position) {
+ // Format:
+ // '-'?('0'|[1-9][0-9]*)('.'[0-9]+)?([eE][+-]?[0-9]+)?
+ int start = position;
+ int length = source.length;
+ bool isDouble = false;
+ if (char == MINUS) {
+ position++;
+ if (position == length) fail(position, "Missing expected digit");
+ char = source.charCodeAt(position);
+ }
+ if (char < CHAR_0 || char > CHAR_9) {
+ fail(position, "Missing expected digit");
+ }
+ int handleLiteral(position) {
+ String literal = source.substring(start, position);
+ // This correctly creates -0 for doubles.
+ num value = (isDouble ? double.parse(literal) : int.parse(literal));
+ listener.handleNumber(value);
+ return position;
+ }
+ if (char == CHAR_0) {
+ position++;
+ if (position == length) return handleLiteral(position);
+ char = source.charCodeAt(position);
+ if (CHAR_0 <= char && char <= CHAR_9) {
+ fail(position);
+ }
+ } else {
+ do {
+ position++;
+ if (position == length) return handleLiteral(position);
+ char = source.charCodeAt(position);
+ } while (CHAR_0 <= char && char <= CHAR_9);
+ }
+ if (char == DECIMALPOINT) {
+ isDouble = true;
+ position++;
+ if (position == length) fail(position, "Missing expected digit");
+ char = source.charCodeAt(position);
+ if (char < CHAR_0 || char > CHAR_9) fail(position);
+ do {
+ position++;
+ if (position == length) return handleLiteral(position);
+ char = source.charCodeAt(position);
+ } while (CHAR_0 <= char && char <= CHAR_9);
+ }
+ if (char == CHAR_e || char == CHAR_E) {
+ isDouble = true;
+ position++;
+ if (position == length) fail(position, "Missing expected digit");
+ char = source.charCodeAt(position);
+ if (char == PLUS || char == MINUS) {
+ position++;
+ if (position == length) fail(position, "Missing expected digit");
+ char = source.charCodeAt(position);
+ }
+ if (char < CHAR_0 || char > CHAR_9) {
+ fail(position, "Missing expected digit");
+ }
+ do {
+ position++;
+ if (position == length) return handleLiteral(position);
+ char = source.charCodeAt(position);
+ } while (CHAR_0 <= char && char <= CHAR_9);
+ }
+ return handleLiteral(position);
+ }
+
+ void fail(int position, [String message]) {
+ if (message == null) message = "Unexpected character";
+ listener.fail(source, position, message);
+ // If the listener didn't throw, do it here.
+ String slice;
+ int sliceEnd = position + 20;
+ if (sliceEnd > source.length) {
+ slice = "'${source.substring(position)}'";
+ } else {
+ slice = "'${source.substring(position, sliceEnd)}...'";
+ }
+ throw new FormatException("Unexpected character at $position: $slice");
+ }
+}
+
+
+class _JsonStringifier {
+ StringBuffer sb;
+ List<Object> seen; // TODO: that should be identity set.
+
+ _JsonStringifier(this.sb) : seen = [];
+
+ static String stringify(final object) {
+ StringBuffer output = new StringBuffer();
+ _JsonStringifier stringifier = new _JsonStringifier(output);
+ stringifier.stringifyValue(object);
+ return output.toString();
+ }
+
+ static void printOn(final object, StringBuffer output) {
+ _JsonStringifier stringifier = new _JsonStringifier(output);
+ stringifier.stringifyValue(object);
+ }
+
+ static String numberToString(num x) {
+ return x.toString();
+ }
+
+ // ('0' + x) or ('a' + x - 10)
+ static int hexDigit(int x) => x < 10 ? 48 + x : 87 + x;
+
+ static void escape(StringBuffer sb, String s) {
+ final int length = s.length;
+ bool needsEscape = false;
+ final charCodes = new List<int>();
+ for (int i = 0; i < length; i++) {
+ int charCode = s.charCodeAt(i);
+ if (charCode < 32) {
+ needsEscape = true;
+ charCodes.add(JsonParser.BACKSLASH);
+ switch (charCode) {
+ case JsonParser.BACKSPACE:
+ charCodes.add(JsonParser.CHAR_b);
+ break;
+ case JsonParser.TAB:
+ charCodes.add(JsonParser.CHAR_t);
+ break;
+ case JsonParser.NEWLINE:
+ charCodes.add(JsonParser.CHAR_n);
+ break;
+ case JsonParser.FORM_FEED:
+ charCodes.add(JsonParser.CHAR_f);
+ break;
+ case JsonParser.CARRIAGE_RETURN:
+ charCodes.add(JsonParser.CHAR_r);
+ break;
+ default:
+ charCodes.add(JsonParser.CHAR_u);
+ charCodes.add(hexDigit((charCode >> 12) & 0xf));
+ charCodes.add(hexDigit((charCode >> 8) & 0xf));
+ charCodes.add(hexDigit((charCode >> 4) & 0xf));
+ charCodes.add(hexDigit(charCode & 0xf));
+ break;
+ }
+ } else if (charCode == JsonParser.QUOTE ||
+ charCode == JsonParser.BACKSLASH) {
+ needsEscape = true;
+ charCodes.add(JsonParser.BACKSLASH);
+ charCodes.add(charCode);
+ } else {
+ charCodes.add(charCode);
+ }
+ }
+ sb.add(needsEscape ? new String.fromCharCodes(charCodes) : s);
+ }
+
+ void checkCycle(final object) {
+ // TODO: use Iterables.
+ for (int i = 0; i < seen.length; i++) {
+ if (identical(seen[i], object)) {
+ throw 'Cyclic structure';
+ }
+ }
+ seen.add(object);
+ }
+
+ void stringifyValue(final object) {
+ // Tries stringifying object directly. If it's not a simple value, List or
+ // Map, call toJson() to get a custom representation and try serializing
+ // that.
+ if (!stringifyJsonValue(object)) {
+ checkCycle(object);
+ try {
+ var customJson = object.toJson();
+ if (!stringifyJsonValue(customJson)) {
+ throw new JsonUnsupportedObjectError(object);
+ }
+ seen.removeLast();
+ } catch (e) {
+ throw new JsonUnsupportedObjectError.withCause(object, e);
+ }
+ }
+ }
+
+ /**
+ * Serializes a [num], [String], [bool], [Null], [List] or [Map] value.
+ *
+ * Returns true if the value is one of these types, and false if not.
+ * If a value is both a [List] and a [Map], it's serialized as a [List].
+ */
+ bool stringifyJsonValue(final object) {
+ if (object is num) {
+ // TODO: use writeOn.
+ sb.add(numberToString(object));
+ return true;
+ } else if (identical(object, true)) {
+ sb.add('true');
+ return true;
+ } else if (identical(object, false)) {
+ sb.add('false');
+ return true;
+ } else if (object == null) {
+ sb.add('null');
+ return true;
+ } else if (object is String) {
+ sb.add('"');
+ escape(sb, object);
+ sb.add('"');
+ return true;
+ } else if (object is List) {
+ checkCycle(object);
+ List a = object;
+ sb.add('[');
+ if (a.length > 0) {
+ stringifyValue(a[0]);
+ // TODO: switch to Iterables.
+ for (int i = 1; i < a.length; i++) {
+ sb.add(',');
+ stringifyValue(a[i]);
+ }
+ }
+ sb.add(']');
+ seen.removeLast();
+ return true;
+ } else if (object is Map) {
+ checkCycle(object);
+ Map<String, Object> m = object;
+ sb.add('{');
+ bool first = true;
+ m.forEach((String key, Object value) {
+ if (!first) {
+ sb.add(',"');
+ } else {
+ sb.add('"');
+ }
+ escape(sb, key);
+ sb.add('":');
+ stringifyValue(value);
+ first = false;
+ });
+ sb.add('}');
+ seen.removeLast();
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/runtime/bin/json_sources.gypi b/sdk/lib/json/json_sources.gypi
similarity index 71%
rename from runtime/bin/json_sources.gypi
rename to sdk/lib/json/json_sources.gypi
index 6c9ca3d..f9a7160 100644
--- a/runtime/bin/json_sources.gypi
+++ b/sdk/lib/json/json_sources.gypi
@@ -2,9 +2,9 @@
# 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 file contains all sources for the dart:io library.
+# This file contains all sources for the dart:json library.
{
'sources': [
- '../../sdk/lib/json/json.dart',
+ 'json_base.dart',
],
}
diff --git a/sdk/lib/svg/dart2js/svg_dart2js.dart b/sdk/lib/svg/dart2js/svg_dart2js.dart
index 4b26804..c34dc27 100644
--- a/sdk/lib/svg/dart2js/svg_dart2js.dart
+++ b/sdk/lib/svg/dart2js/svg_dart2js.dart
@@ -6,7 +6,8 @@
import 'dart:html_common';
import 'dart:_js_helper' show Creates, Returns, JavaScriptIndexingBehavior, JSName;
import 'dart:_foreign_helper' show JS;
-// DO NOT EDIT
+// DO NOT EDIT - unless you are editing documentation as per:
+// https://code.google.com/p/dart/wiki/ContributingHTMLDocumentation
// Auto-generated dart:svg library.
@@ -5757,7 +5758,7 @@
Set<String> readClasses() {
var classname = _element.attributes['class'];
- Set<String> s = new Set<String>();
+ Set<String> s = new LinkedHashSet<String>();
if (classname == null) {
return s;
}
diff --git a/sdk/lib/svg/dartium/svg_dartium.dart b/sdk/lib/svg/dartium/svg_dartium.dart
index 1c03b3a..d6b19dd 100644
--- a/sdk/lib/svg/dartium/svg_dartium.dart
+++ b/sdk/lib/svg/dartium/svg_dartium.dart
@@ -6506,7 +6506,7 @@
Set<String> readClasses() {
var classname = _element.attributes['class'];
- Set<String> s = new Set<String>();
+ Set<String> s = new LinkedHashSet<String>();
if (classname == null) {
return s;
}
diff --git a/sdk/lib/uri/uri.dart b/sdk/lib/uri/uri.dart
index abd1223..f3876f6 100644
--- a/sdk/lib/uri/uri.dart
+++ b/sdk/lib/uri/uri.dart
@@ -7,259 +7,6 @@
import 'dart:math';
import 'dart:utf';
+part 'uri_base.dart';
part 'encode_decode.dart';
part 'helpers.dart';
-
-/**
- * A parsed URI, inspired by Closure's [URI][] class. Implements [RFC-3986][].
- * [uri]: http://closure-library.googlecode.com/svn/docs/class_goog_Uri.html
- * [RFC-3986]: http://tools.ietf.org/html/rfc3986#section-4.3)
- */
-class Uri {
- final String scheme;
- final String userInfo;
- final String domain;
- final int port;
- final String path;
- final String query;
- final String fragment;
-
- /**
- * Deprecated. Please use [parse] instead.
- */
- Uri.fromString(String uri) : this._fromMatch(_splitRe.firstMatch(uri));
-
- static Uri parse(String uri) => new Uri._fromMatch(_splitRe.firstMatch(uri));
-
- Uri._fromMatch(Match m) :
- this.fromComponents(scheme: _emptyIfNull(m[_COMPONENT_SCHEME]),
- userInfo: _emptyIfNull(m[_COMPONENT_USER_INFO]),
- domain: _emptyIfNull(m[_COMPONENT_DOMAIN]),
- port: _parseIntOrZero(m[_COMPONENT_PORT]),
- path: _emptyIfNull(m[_COMPONENT_PATH]),
- query: _emptyIfNull(m[_COMPONENT_QUERY_DATA]),
- fragment: _emptyIfNull(m[_COMPONENT_FRAGMENT]));
-
- const Uri.fromComponents({this.scheme: "",
- this.userInfo: "",
- this.domain: "",
- this.port: 0,
- this.path: "",
- this.query: "",
- this.fragment: ""});
-
- Uri(String uri) : this.fromString(uri);
-
- static String _emptyIfNull(String val) => val != null ? val : '';
-
- static int _parseIntOrZero(String val) {
- if (val != null && val != '') {
- return int.parse(val);
- } else {
- return 0;
- }
- }
-
- // NOTE: This code was ported from: closure-library/closure/goog/uri/utils.js
- static final RegExp _splitRe = new RegExp(
- '^'
- '(?:'
- '([^:/?#.]+)' // scheme - ignore special characters
- // used by other URL parts such as :,
- // ?, /, #, and .
- ':)?'
- '(?://'
- '(?:([^/?#]*)@)?' // userInfo
- '([\\w\\d\\-\\u0100-\\uffff.%]*)'
- // domain - restrict to letters,
- // digits, dashes, dots, percent
- // escapes, and unicode characters.
- '(?::([0-9]+))?' // port
- ')?'
- '([^?#]+)?' // path
- '(?:\\?([^#]*))?' // query
- '(?:#(.*))?' // fragment
- '\$');
-
- static const _COMPONENT_SCHEME = 1;
- static const _COMPONENT_USER_INFO = 2;
- static const _COMPONENT_DOMAIN = 3;
- static const _COMPONENT_PORT = 4;
- static const _COMPONENT_PATH = 5;
- static const _COMPONENT_QUERY_DATA = 6;
- static const _COMPONENT_FRAGMENT = 7;
-
- /**
- * Returns `true` if the URI is absolute.
- */
- bool get isAbsolute {
- if ("" == scheme) return false;
- if ("" != fragment) return false;
- return true;
-
- /* absolute-URI = scheme ":" hier-part [ "?" query ]
- * hier-part = "//" authority path-abempty
- * / path-absolute
- * / path-rootless
- * / path-empty
- *
- * path = path-abempty ; begins with "/" or is empty
- * / path-absolute ; begins with "/" but not "//"
- * / path-noscheme ; begins with a non-colon segment
- * / path-rootless ; begins with a segment
- * / path-empty ; zero characters
- *
- * path-abempty = *( "/" segment )
- * path-absolute = "/" [ segment-nz *( "/" segment ) ]
- * path-noscheme = segment-nz-nc *( "/" segment )
- * path-rootless = segment-nz *( "/" segment )
- * path-empty = 0<pchar>
- * segment = *pchar
- * segment-nz = 1*pchar
- * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
- * ; non-zero-length segment without any colon ":"
- *
- * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
- */
- }
-
- Uri resolve(String uri) {
- return resolveUri(Uri.parse(uri));
- }
-
- Uri resolveUri(Uri reference) {
- // From RFC 3986.
- String targetScheme;
- String targetUserInfo;
- String targetDomain;
- int targetPort;
- String targetPath;
- String targetQuery;
- if (reference.scheme != "") {
- targetScheme = reference.scheme;
- targetUserInfo = reference.userInfo;
- targetDomain = reference.domain;
- targetPort = reference.port;
- targetPath = removeDotSegments(reference.path);
- targetQuery = reference.query;
- } else {
- if (reference.hasAuthority) {
- targetUserInfo = reference.userInfo;
- targetDomain = reference.domain;
- targetPort = reference.port;
- targetPath = removeDotSegments(reference.path);
- targetQuery = reference.query;
- } else {
- if (reference.path == "") {
- targetPath = this.path;
- if (reference.query != "") {
- targetQuery = reference.query;
- } else {
- targetQuery = this.query;
- }
- } else {
- if (reference.path.startsWith("/")) {
- targetPath = removeDotSegments(reference.path);
- } else {
- targetPath = removeDotSegments(merge(this.path, reference.path));
- }
- targetQuery = reference.query;
- }
- targetUserInfo = this.userInfo;
- targetDomain = this.domain;
- targetPort = this.port;
- }
- targetScheme = this.scheme;
- }
- return new Uri.fromComponents(scheme: targetScheme,
- userInfo: targetUserInfo,
- domain: targetDomain,
- port: targetPort,
- path: targetPath,
- query: targetQuery,
- fragment: reference.fragment);
- }
-
- bool get hasAuthority {
- return (userInfo != "") || (domain != "") || (port != 0);
- }
-
- /**
- * For http/https schemes returns URI's [origin][] - scheme://domain:port.
- * For all other schemes throws ArgumentError.
- * [origin]: http://www.w3.org/TR/2011/WD-html5-20110405/origin-0.html#origin
- */
- String get origin {
- if (scheme == "") {
- // TODO(aprelev@gmail.com): Use StateException instead
- throw new ArgumentError("Cannot use origin without a scheme");
- }
- if (scheme != "http" && scheme != "https") {
- // TODO(aprelev@gmail.com): Use StateException instead
- throw new ArgumentError(
- "origin is applicable to http/https schemes only. Not \'$scheme\'");
- }
- StringBuffer sb = new StringBuffer();
- sb.add(scheme);
- sb.add(":");
- if (domain == null || domain == "") {
- // TODO(aprelev@gmail.com): Use StateException instead
- throw new ArgumentError("Cannot use origin without a domain");
- }
-
- sb.add("//");
- sb.add(domain);
- if (port != 0) {
- sb.add(":");
- sb.add(port);
- }
- return sb.toString();
- }
-
- String toString() {
- StringBuffer sb = new StringBuffer();
- _addIfNonEmpty(sb, scheme, scheme, ':');
- if (hasAuthority || (scheme == "file")) {
- sb.add("//");
- _addIfNonEmpty(sb, userInfo, userInfo, "@");
- sb.add(domain == null ? "null" : domain);
- if (port != 0) {
- sb.add(":");
- sb.add(port.toString());
- }
- }
- sb.add(path == null ? "null" : path);
- _addIfNonEmpty(sb, query, "?", query);
- _addIfNonEmpty(sb, fragment, "#", fragment);
- return sb.toString();
- }
-
- bool operator==(other) {
- if (other is! Uri) return false;
- Uri uri = other;
- return scheme == uri.scheme &&
- userInfo == uri.userInfo &&
- domain == uri.domain &&
- port == uri.port &&
- path == uri.path &&
- query == uri.query &&
- fragment == uri.fragment;
- }
-
- int get hashCode {
- int combine(part, current) {
- // The sum is truncated to 30 bits to make sure it fits into a Smi.
- return (current * 31 + part.hashCode) & 0x3FFFFFFF;
- }
- return combine(scheme, combine(userInfo, combine(domain, combine(port,
- combine(path, combine(query, combine(fragment, 1)))))));
- }
-
- static void _addIfNonEmpty(StringBuffer sb, String test,
- String first, String second) {
- if ("" != test) {
- sb.add(first == null ? "null" : first);
- sb.add(second == null ? "null" : second);
- }
- }
-}
diff --git a/sdk/lib/uri/uri_base.dart b/sdk/lib/uri/uri_base.dart
new file mode 100644
index 0000000..c775080
--- /dev/null
+++ b/sdk/lib/uri/uri_base.dart
@@ -0,0 +1,259 @@
+// 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.
+
+part of dart.uri;
+
+/**
+ * A parsed URI, inspired by Closure's [URI][] class. Implements [RFC-3986][].
+ * [uri]: http://closure-library.googlecode.com/svn/docs/class_goog_Uri.html
+ * [RFC-3986]: http://tools.ietf.org/html/rfc3986#section-4.3)
+ */
+class Uri {
+ final String scheme;
+ final String userInfo;
+ final String domain;
+ final int port;
+ final String path;
+ final String query;
+ final String fragment;
+
+ /**
+ * Deprecated. Please use [parse] instead.
+ */
+ Uri.fromString(String uri) : this._fromMatch(_splitRe.firstMatch(uri));
+
+ static Uri parse(String uri) => new Uri._fromMatch(_splitRe.firstMatch(uri));
+
+ Uri._fromMatch(Match m) :
+ this.fromComponents(scheme: _emptyIfNull(m[_COMPONENT_SCHEME]),
+ userInfo: _emptyIfNull(m[_COMPONENT_USER_INFO]),
+ domain: _emptyIfNull(m[_COMPONENT_DOMAIN]),
+ port: _parseIntOrZero(m[_COMPONENT_PORT]),
+ path: _emptyIfNull(m[_COMPONENT_PATH]),
+ query: _emptyIfNull(m[_COMPONENT_QUERY_DATA]),
+ fragment: _emptyIfNull(m[_COMPONENT_FRAGMENT]));
+
+ const Uri.fromComponents({this.scheme: "",
+ this.userInfo: "",
+ this.domain: "",
+ this.port: 0,
+ this.path: "",
+ this.query: "",
+ this.fragment: ""});
+
+ Uri(String uri) : this.fromString(uri);
+
+ static String _emptyIfNull(String val) => val != null ? val : '';
+
+ static int _parseIntOrZero(String val) {
+ if (val != null && val != '') {
+ return int.parse(val);
+ } else {
+ return 0;
+ }
+ }
+
+ // NOTE: This code was ported from: closure-library/closure/goog/uri/utils.js
+ static final RegExp _splitRe = new RegExp(
+ '^'
+ '(?:'
+ '([^:/?#.]+)' // scheme - ignore special characters
+ // used by other URL parts such as :,
+ // ?, /, #, and .
+ ':)?'
+ '(?://'
+ '(?:([^/?#]*)@)?' // userInfo
+ '([\\w\\d\\-\\u0100-\\uffff.%]*)'
+ // domain - restrict to letters,
+ // digits, dashes, dots, percent
+ // escapes, and unicode characters.
+ '(?::([0-9]+))?' // port
+ ')?'
+ '([^?#]+)?' // path
+ '(?:\\?([^#]*))?' // query
+ '(?:#(.*))?' // fragment
+ '\$');
+
+ static const _COMPONENT_SCHEME = 1;
+ static const _COMPONENT_USER_INFO = 2;
+ static const _COMPONENT_DOMAIN = 3;
+ static const _COMPONENT_PORT = 4;
+ static const _COMPONENT_PATH = 5;
+ static const _COMPONENT_QUERY_DATA = 6;
+ static const _COMPONENT_FRAGMENT = 7;
+
+ /**
+ * Returns `true` if the URI is absolute.
+ */
+ bool get isAbsolute {
+ if ("" == scheme) return false;
+ if ("" != fragment) return false;
+ return true;
+
+ /* absolute-URI = scheme ":" hier-part [ "?" query ]
+ * hier-part = "//" authority path-abempty
+ * / path-absolute
+ * / path-rootless
+ * / path-empty
+ *
+ * path = path-abempty ; begins with "/" or is empty
+ * / path-absolute ; begins with "/" but not "//"
+ * / path-noscheme ; begins with a non-colon segment
+ * / path-rootless ; begins with a segment
+ * / path-empty ; zero characters
+ *
+ * path-abempty = *( "/" segment )
+ * path-absolute = "/" [ segment-nz *( "/" segment ) ]
+ * path-noscheme = segment-nz-nc *( "/" segment )
+ * path-rootless = segment-nz *( "/" segment )
+ * path-empty = 0<pchar>
+ * segment = *pchar
+ * segment-nz = 1*pchar
+ * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
+ * ; non-zero-length segment without any colon ":"
+ *
+ * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
+ */
+ }
+
+ Uri resolve(String uri) {
+ return resolveUri(Uri.parse(uri));
+ }
+
+ Uri resolveUri(Uri reference) {
+ // From RFC 3986.
+ String targetScheme;
+ String targetUserInfo;
+ String targetDomain;
+ int targetPort;
+ String targetPath;
+ String targetQuery;
+ if (reference.scheme != "") {
+ targetScheme = reference.scheme;
+ targetUserInfo = reference.userInfo;
+ targetDomain = reference.domain;
+ targetPort = reference.port;
+ targetPath = removeDotSegments(reference.path);
+ targetQuery = reference.query;
+ } else {
+ if (reference.hasAuthority) {
+ targetUserInfo = reference.userInfo;
+ targetDomain = reference.domain;
+ targetPort = reference.port;
+ targetPath = removeDotSegments(reference.path);
+ targetQuery = reference.query;
+ } else {
+ if (reference.path == "") {
+ targetPath = this.path;
+ if (reference.query != "") {
+ targetQuery = reference.query;
+ } else {
+ targetQuery = this.query;
+ }
+ } else {
+ if (reference.path.startsWith("/")) {
+ targetPath = removeDotSegments(reference.path);
+ } else {
+ targetPath = removeDotSegments(merge(this.path, reference.path));
+ }
+ targetQuery = reference.query;
+ }
+ targetUserInfo = this.userInfo;
+ targetDomain = this.domain;
+ targetPort = this.port;
+ }
+ targetScheme = this.scheme;
+ }
+ return new Uri.fromComponents(scheme: targetScheme,
+ userInfo: targetUserInfo,
+ domain: targetDomain,
+ port: targetPort,
+ path: targetPath,
+ query: targetQuery,
+ fragment: reference.fragment);
+ }
+
+ bool get hasAuthority {
+ return (userInfo != "") || (domain != "") || (port != 0);
+ }
+
+ /**
+ * For http/https schemes returns URI's [origin][] - scheme://domain:port.
+ * For all other schemes throws ArgumentError.
+ * [origin]: http://www.w3.org/TR/2011/WD-html5-20110405/origin-0.html#origin
+ */
+ String get origin {
+ if (scheme == "") {
+ // TODO(aprelev@gmail.com): Use StateException instead
+ throw new ArgumentError("Cannot use origin without a scheme");
+ }
+ if (scheme != "http" && scheme != "https") {
+ // TODO(aprelev@gmail.com): Use StateException instead
+ throw new ArgumentError(
+ "origin is applicable to http/https schemes only. Not \'$scheme\'");
+ }
+ StringBuffer sb = new StringBuffer();
+ sb.add(scheme);
+ sb.add(":");
+ if (domain == null || domain == "") {
+ // TODO(aprelev@gmail.com): Use StateException instead
+ throw new ArgumentError("Cannot use origin without a domain");
+ }
+
+ sb.add("//");
+ sb.add(domain);
+ if (port != 0) {
+ sb.add(":");
+ sb.add(port);
+ }
+ return sb.toString();
+ }
+
+ String toString() {
+ StringBuffer sb = new StringBuffer();
+ _addIfNonEmpty(sb, scheme, scheme, ':');
+ if (hasAuthority || (scheme == "file")) {
+ sb.add("//");
+ _addIfNonEmpty(sb, userInfo, userInfo, "@");
+ sb.add(domain == null ? "null" : domain);
+ if (port != 0) {
+ sb.add(":");
+ sb.add(port.toString());
+ }
+ }
+ sb.add(path == null ? "null" : path);
+ _addIfNonEmpty(sb, query, "?", query);
+ _addIfNonEmpty(sb, fragment, "#", fragment);
+ return sb.toString();
+ }
+
+ bool operator==(other) {
+ if (other is! Uri) return false;
+ Uri uri = other;
+ return scheme == uri.scheme &&
+ userInfo == uri.userInfo &&
+ domain == uri.domain &&
+ port == uri.port &&
+ path == uri.path &&
+ query == uri.query &&
+ fragment == uri.fragment;
+ }
+
+ int get hashCode {
+ int combine(part, current) {
+ // The sum is truncated to 30 bits to make sure it fits into a Smi.
+ return (current * 31 + part.hashCode) & 0x3FFFFFFF;
+ }
+ return combine(scheme, combine(userInfo, combine(domain, combine(port,
+ combine(path, combine(query, combine(fragment, 1)))))));
+ }
+
+ static void _addIfNonEmpty(StringBuffer sb, String test,
+ String first, String second) {
+ if ("" != test) {
+ sb.add(first == null ? "null" : first);
+ sb.add(second == null ? "null" : second);
+ }
+ }
+}
diff --git a/runtime/bin/uri_sources.gypi b/sdk/lib/uri/uri_sources.gypi
similarity index 71%
rename from runtime/bin/uri_sources.gypi
rename to sdk/lib/uri/uri_sources.gypi
index 36a9e3c..87e234a 100644
--- a/runtime/bin/uri_sources.gypi
+++ b/sdk/lib/uri/uri_sources.gypi
@@ -5,8 +5,8 @@
# This file contains all sources for the dart:uri library.
{
'sources': [
- '../../sdk/lib/uri/uri.dart',
- '../../sdk/lib/uri/helpers.dart',
- '../../sdk/lib/uri/encode_decode.dart',
+ 'uri_base.dart',
+ 'helpers.dart',
+ 'encode_decode.dart',
],
}
diff --git a/sdk/lib/utf/utf.dart b/sdk/lib/utf/utf.dart
index cc3c573..abc3f84 100644
--- a/sdk/lib/utf/utf.dart
+++ b/sdk/lib/utf/utf.dart
@@ -3,7 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
library dart.utf;
+import "dart:async";
part "utf_core.dart";
+part "utf_stream.dart";
part "utf8.dart";
part "utf16.dart";
part "utf32.dart";
diff --git a/sdk/lib/utf/utf_sources.gypi b/sdk/lib/utf/utf_sources.gypi
new file mode 100644
index 0000000..9d86ff1
--- /dev/null
+++ b/sdk/lib/utf/utf_sources.gypi
@@ -0,0 +1,17 @@
+# 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.
+
+# This file contains all sources for the dart:utf library.
+#
+# TODO(ager): When the VM can use the #source directive for libraries.
+# At that point utf.dart should be the only utf library file.
+{
+ 'sources': [
+ 'utf_core.dart',
+ 'utf_stream.dart',
+ 'utf8.dart',
+ 'utf16.dart',
+ 'utf32.dart',
+ ],
+}
diff --git a/sdk/lib/utf/utf_stream.dart b/sdk/lib/utf/utf_stream.dart
new file mode 100644
index 0000000..ad69c62
--- /dev/null
+++ b/sdk/lib/utf/utf_stream.dart
@@ -0,0 +1,224 @@
+// 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.
+
+part of dart.utf;
+
+class _HelperStreamController<T> extends StreamController<T> {
+ final Function onPauseChanged;
+
+ _HelperStreamController(this.onPauseChanged);
+
+ void onPauseStateChange() {
+ onPauseChanged();
+ }
+}
+
+abstract class _StringDecoder implements StreamTransformer<List<int>, String> {
+ _HelperStreamController<String> _controller;
+ StreamSubscription<List<int>> _subscription;
+ List<int> _carry;
+ List<int> _buffer;
+ int _replacementChar;
+ bool _paused = false;
+
+ _StringDecoder(int this._replacementChar) {
+ _controller = new _HelperStreamController<String>(_onPauseChanged);
+ }
+
+ void _onPauseChanged() {
+ _paused = _controller.isPaused;
+ if (_subscription == null) return;
+ if (_paused) {
+ _subscription.pause();
+ } else {
+ _subscription.resume();
+ }
+ }
+
+ Stream<String> bind(Stream<List<int>> stream) {
+ _subscription = stream.listen(
+ _onData,
+ onError: _controller.signalError,
+ onDone: () {
+ if (_carry != null) {
+ _controller.add(new String.fromCharCodes(
+ new List.fixedLength(_carry.length, fill: _replacementChar)));
+ }
+ _controller.close();
+ },
+ unsubscribeOnError: false);
+ if (_paused) _subscription.pause();
+ return _controller.stream;
+ }
+
+ void _onData(List<int> bytes) {
+ _buffer = <int>[];
+ List<int> carry = _carry;
+ _carry = null;
+ int pos = 0;
+ int available = bytes.length;
+ // If we have carry-over data, start from negative index, indicating carry
+ // index.
+ int goodChars = 0;
+ if (carry != null) pos = -carry.length;
+ while (pos < available) {
+ int currentPos = pos;
+ int getNext() {
+ if (pos < 0) {
+ return carry[pos++ + carry.length];
+ } else if (pos < available) {
+ return bytes[pos++];
+ }
+ return null;
+ }
+ int consumed = _processBytes(getNext);
+ if (consumed > 0) {
+ goodChars = _buffer.length;
+ } else if (consumed == 0) {
+ _buffer.length = goodChars;
+ if (currentPos < 0) {
+ _carry = [];
+ _carry.addAll(carry);
+ _carry.addAll(bytes);
+ } else {
+ _carry = bytes.getRange(currentPos, bytes.length - currentPos);
+ }
+ break;
+ } else {
+ // Invalid byte at position pos - 1
+ _buffer.length = goodChars;
+ _addChar(-1);
+ goodChars = _buffer.length;
+ }
+ }
+ if (_buffer.length > 0) {
+ // Limit to 'goodChars', if lower than actual charCodes in the buffer.
+ _controller.add(new String.fromCharCodes(_buffer));
+ }
+ _buffer = null;
+ }
+
+ int _processBytes(int getNext());
+
+ void _addChar(int char) {
+ if (char > 0x10FFFF || char < 0) char = _replacementChar;
+ _buffer.add(char);
+ }
+}
+
+/**
+ * StringTransformer that decodes a stream of UTF-8 encoded bytes.
+ */
+class Utf8DecoderTransformer extends _StringDecoder {
+ Utf8DecoderTransformer(
+ [int replacementChar = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT])
+ : super(replacementChar);
+
+ int _processBytes(int getNext()) {
+ int value = getNext();
+ if ((value & 0xFF) != value) return -1; // Not a byte.
+ if ((value & 0x80) == 0x80) {
+ int additionalBytes;
+ int min;
+ if ((value & 0xe0) == 0xc0) { // 110xxxxx
+ value = value & 0x1F;
+ additionalBytes = 1;
+ min = 0x80;
+ } else if ((value & 0xf0) == 0xe0) { // 1110xxxx
+ value = value & 0x0F;
+ additionalBytes = 2;
+ min = 0x800;
+ } else if ((value & 0xf8) == 0xf0) { // 11110xxx
+ value = value & 0x07;
+ additionalBytes = 3;
+ min = 0x10000;
+ } else if ((value & 0xfc) == 0xf8) { // 111110xx
+ value = value & 0x03;
+ additionalBytes = 4;
+ min = 0x200000;
+ } else if ((value & 0xfe) == 0xfc) { // 1111110x
+ value = value & 0x01;
+ additionalBytes = 5;
+ min = 0x4000000;
+ } else {
+ return -1;
+ }
+ for (int i = 0; i < additionalBytes; i++) {
+ int next = getNext();
+ if (next == null) return 0; // Not enough chars, reset.
+ if ((next & 0xc0) != 0x80 || (next & 0xff) != next) return -1;
+ value = value << 6 | (next & 0x3f);
+ }
+ // Invalid charCode if less then minimum expected.
+ if (value < min) value = -1;
+ _addChar(value);
+ return 1 + additionalBytes;
+ }
+ _addChar(value);
+ return 1;
+ }
+}
+
+
+abstract class _StringEncoder implements StreamTransformer<String, List<int>> {
+ _HelperStreamController<List<int>> _controller;
+ StreamSubscription<String> _subscription;
+
+ void _onPauseChanged() {
+ if (_controller.isPaused) {
+ _subscription.pause();
+ } else {
+ _subscription.resume();
+ }
+ }
+ Stream<List<int>> bind(Stream<String> stream) {
+ _controller = new _HelperStreamController(_onPauseChanged);
+ _subscription = stream.listen(
+ (string) => _controller.add(_processString(string)),
+ onError: _controller.signalError,
+ onDone: _controller.close,
+ unsubscribeOnError: false);
+ return _controller.stream;
+ }
+
+ List<int> _processString(String string);
+}
+
+/**
+ * StringTransformer that UTF-8 encodes a stream of strings.
+ */
+class Utf8EncoderTransformer extends _StringEncoder {
+ List<int> _processString(String string) {
+ var bytes = [];
+ int pos = 0;
+ List<int> codepoints = _utf16CodeUnitsToCodepoints(string.charCodes);
+ int length = codepoints.length;
+ for (int i = 0; i < length; i++) {
+ int additionalBytes;
+ int charCode = codepoints[i];
+ if (charCode <= 0x007F) {
+ additionalBytes = 0;
+ bytes.add(charCode);
+ } else if (charCode <= 0x07FF) {
+ // 110xxxxx (xxxxx is top 5 bits).
+ bytes.add(((charCode >> 6) & 0x1F) | 0xC0);
+ additionalBytes = 1;
+ } else if (charCode <= 0xFFFF) {
+ // 1110xxxx (xxxx is top 4 bits)
+ bytes.add(((charCode >> 12) & 0x0F)| 0xE0);
+ additionalBytes = 2;
+ } else {
+ // 11110xxx (xxx is top 3 bits)
+ bytes.add(((charCode >> 18) & 0x07) | 0xF0);
+ additionalBytes = 3;
+ }
+ for (int i = additionalBytes; i > 0; i--) {
+ // 10xxxxxx (xxxxxx is next 6 bits from the top).
+ bytes.add(((charCode >> (6 * (i - 1))) & 0x3F) | 0x80);
+ }
+ pos += additionalBytes + 1;
+ }
+ return bytes;
+ }
+}
diff --git a/sdk/lib/web_audio/dart2js/web_audio_dart2js.dart b/sdk/lib/web_audio/dart2js/web_audio_dart2js.dart
index 6680a44..7bc07f1 100644
--- a/sdk/lib/web_audio/dart2js/web_audio_dart2js.dart
+++ b/sdk/lib/web_audio/dart2js/web_audio_dart2js.dart
@@ -5,7 +5,8 @@
import 'dart:html';
import 'dart:html_common';
import 'dart:_foreign_helper' show JS;
-// DO NOT EDIT
+// DO NOT EDIT - unless you are editing documentation as per:
+// https://code.google.com/p/dart/wiki/ContributingHTMLDocumentation
// Auto-generated dart:audio library.
diff --git a/sdk/lib/web_sql/dart2js/web_sql_dart2js.dart b/sdk/lib/web_sql/dart2js/web_sql_dart2js.dart
new file mode 100644
index 0000000..8559913
--- /dev/null
+++ b/sdk/lib/web_sql/dart2js/web_sql_dart2js.dart
@@ -0,0 +1,450 @@
+library dart.dom.web_sql;
+
+import 'dart:async';
+import 'dart:collection';
+import 'dart:html';
+import 'dart:html_common';
+import 'dart:_js_helper' show convertDartClosureToJS, Creates, JavaScriptIndexingBehavior, JSName;
+import 'dart:_foreign_helper' show JS;
+// DO NOT EDIT - unless you are editing documentation as per:
+// https://code.google.com/p/dart/wiki/ContributingHTMLDocumentation
+// Auto-generated dart:audio library.
+
+
+
+
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+typedef void DatabaseCallback(database);
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+typedef void SqlStatementCallback(SqlTransaction transaction, SqlResultSet resultSet);
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+typedef void SqlStatementErrorCallback(SqlTransaction transaction, SqlError error);
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+typedef void SqlTransactionCallback(SqlTransaction transaction);
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+typedef void SqlTransactionErrorCallback(SqlError error);
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+typedef void SqlTransactionSyncCallback(SqlTransactionSync transaction);
+// 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.
+
+
+@DocsEditable
+@DomName('Database')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
+class SqlDatabase native "*Database" {
+
+ /// Checks if this type is supported on the current platform.
+ static bool get supported => JS('bool', '!!(window.openDatabase)');
+
+ @DomName('Database.version')
+ @DocsEditable
+ final String version;
+
+ @DomName('Database.changeVersion')
+ @DocsEditable
+ void changeVersion(String oldVersion, String newVersion, [SqlTransactionCallback callback, SqlTransactionErrorCallback errorCallback, VoidCallback successCallback]) native;
+
+ @DomName('Database.readTransaction')
+ @DocsEditable
+ void readTransaction(SqlTransactionCallback callback, [SqlTransactionErrorCallback errorCallback, VoidCallback successCallback]) native;
+
+ @DomName('Database.transaction')
+ @DocsEditable
+ void transaction(SqlTransactionCallback callback, [SqlTransactionErrorCallback errorCallback, VoidCallback successCallback]) native;
+}
+// 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.
+
+
+@DocsEditable
+@DomName('DatabaseSync')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
+class SqlDatabaseSync native "*DatabaseSync" {
+
+ @DomName('DatabaseSync.lastErrorMessage')
+ @DocsEditable
+ final String lastErrorMessage;
+
+ @DomName('DatabaseSync.version')
+ @DocsEditable
+ final String version;
+
+ @DomName('DatabaseSync.changeVersion')
+ @DocsEditable
+ void changeVersion(String oldVersion, String newVersion, [SqlTransactionSyncCallback callback]) native;
+
+ @DomName('DatabaseSync.readTransaction')
+ @DocsEditable
+ void readTransaction(SqlTransactionSyncCallback callback) native;
+
+ @DomName('DatabaseSync.transaction')
+ @DocsEditable
+ void transaction(SqlTransactionSyncCallback callback) native;
+}
+// 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.
+
+
+@DocsEditable
+@DomName('SQLError')
+class SqlError native "*SQLError" {
+
+ static const int CONSTRAINT_ERR = 6;
+
+ static const int DATABASE_ERR = 1;
+
+ static const int QUOTA_ERR = 4;
+
+ static const int SYNTAX_ERR = 5;
+
+ static const int TIMEOUT_ERR = 7;
+
+ static const int TOO_LARGE_ERR = 3;
+
+ static const int UNKNOWN_ERR = 0;
+
+ static const int VERSION_ERR = 2;
+
+ @DomName('SQLError.code')
+ @DocsEditable
+ final int code;
+
+ @DomName('SQLError.message')
+ @DocsEditable
+ final String message;
+}
+// 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.
+
+
+@DocsEditable
+@DomName('SQLException')
+class SqlException native "*SQLException" {
+
+ static const int CONSTRAINT_ERR = 6;
+
+ static const int DATABASE_ERR = 1;
+
+ static const int QUOTA_ERR = 4;
+
+ static const int SYNTAX_ERR = 5;
+
+ static const int TIMEOUT_ERR = 7;
+
+ static const int TOO_LARGE_ERR = 3;
+
+ static const int UNKNOWN_ERR = 0;
+
+ static const int VERSION_ERR = 2;
+
+ @DomName('SQLException.code')
+ @DocsEditable
+ final int code;
+
+ @DomName('SQLException.message')
+ @DocsEditable
+ final String message;
+}
+// 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.
+
+
+@DocsEditable
+@DomName('SQLResultSet')
+class SqlResultSet native "*SQLResultSet" {
+
+ @DomName('SQLResultSet.insertId')
+ @DocsEditable
+ final int insertId;
+
+ @DomName('SQLResultSet.rows')
+ @DocsEditable
+ final SqlResultSetRowList rows;
+
+ @DomName('SQLResultSet.rowsAffected')
+ @DocsEditable
+ final int rowsAffected;
+}
+// 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.
+
+
+@DocsEditable
+@DomName('SQLResultSetRowList')
+class SqlResultSetRowList implements JavaScriptIndexingBehavior, List<Map> native "*SQLResultSetRowList" {
+
+ @DomName('SQLResultSetRowList.length')
+ @DocsEditable
+ int get length => JS("int", "#.length", this);
+
+ Map operator[](int index) => JS("Map", "#[#]", this, index);
+
+ void operator[]=(int index, Map value) {
+ throw new UnsupportedError("Cannot assign element of immutable List.");
+ }
+ // -- start List<Map> mixins.
+ // Map is the element type.
+
+ // From Iterable<Map>:
+
+ Iterator<Map> get iterator {
+ // Note: NodeLists are not fixed size. And most probably length shouldn't
+ // be cached in both iterator _and_ forEach method. For now caching it
+ // for consistency.
+ return new FixedSizeListIterator<Map>(this);
+ }
+
+ dynamic reduce(dynamic initialValue, dynamic combine(dynamic, Map)) {
+ return IterableMixinWorkaround.reduce(this, initialValue, combine);
+ }
+
+ bool contains(Map element) => IterableMixinWorkaround.contains(this, element);
+
+ void forEach(void f(Map element)) => IterableMixinWorkaround.forEach(this, f);
+
+ String join([String separator]) =>
+ IterableMixinWorkaround.joinList(this, separator);
+
+ Iterable map(f(Map element)) =>
+ IterableMixinWorkaround.mapList(this, f);
+
+ Iterable<Map> where(bool f(Map element)) =>
+ IterableMixinWorkaround.where(this, f);
+
+ Iterable expand(Iterable f(Map element)) =>
+ IterableMixinWorkaround.expand(this, f);
+
+ bool every(bool f(Map element)) => IterableMixinWorkaround.every(this, f);
+
+ bool any(bool f(Map element)) => IterableMixinWorkaround.any(this, f);
+
+ List<Map> toList() => new List<Map>.from(this);
+ Set<Map> toSet() => new Set<Map>.from(this);
+
+ bool get isEmpty => this.length == 0;
+
+ Iterable<Map> take(int n) => IterableMixinWorkaround.takeList(this, n);
+
+ Iterable<Map> takeWhile(bool test(Map value)) {
+ return IterableMixinWorkaround.takeWhile(this, test);
+ }
+
+ Iterable<Map> skip(int n) => IterableMixinWorkaround.skipList(this, n);
+
+ Iterable<Map> skipWhile(bool test(Map value)) {
+ return IterableMixinWorkaround.skipWhile(this, test);
+ }
+
+ Map firstMatching(bool test(Map value), { Map orElse() }) {
+ return IterableMixinWorkaround.firstMatching(this, test, orElse);
+ }
+
+ Map lastMatching(bool test(Map value), {Map orElse()}) {
+ return IterableMixinWorkaround.lastMatchingInList(this, test, orElse);
+ }
+
+ Map singleMatching(bool test(Map value)) {
+ return IterableMixinWorkaround.singleMatching(this, test);
+ }
+
+ Map elementAt(int index) {
+ return this[index];
+ }
+
+ // From Collection<Map>:
+
+ void add(Map value) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ void addLast(Map value) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ void addAll(Iterable<Map> iterable) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ // From List<Map>:
+ void set length(int value) {
+ throw new UnsupportedError("Cannot resize immutable List.");
+ }
+
+ void clear() {
+ throw new UnsupportedError("Cannot clear immutable List.");
+ }
+
+ Iterable<Map> get reversed {
+ return IterableMixinWorkaround.reversedList(this);
+ }
+
+ void sort([int compare(Map a, Map b)]) {
+ throw new UnsupportedError("Cannot sort immutable List.");
+ }
+
+ int indexOf(Map element, [int start = 0]) =>
+ Lists.indexOf(this, element, start, this.length);
+
+ int lastIndexOf(Map element, [int start]) {
+ if (start == null) start = length - 1;
+ return Lists.lastIndexOf(this, element, start);
+ }
+
+ Map get first {
+ if (this.length > 0) return this[0];
+ throw new StateError("No elements");
+ }
+
+ Map get last {
+ if (this.length > 0) return this[this.length - 1];
+ throw new StateError("No elements");
+ }
+
+ Map get single {
+ if (length == 1) return this[0];
+ if (length == 0) throw new StateError("No elements");
+ throw new StateError("More than one element");
+ }
+
+ Map min([int compare(Map a, Map b)]) =>
+ IterableMixinWorkaround.min(this, compare);
+
+ Map max([int compare(Map a, Map b)]) =>
+ IterableMixinWorkaround.max(this, compare);
+
+ Map removeAt(int pos) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ Map removeLast() {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void remove(Object object) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void removeAll(Iterable elements) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void retainAll(Iterable elements) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void removeMatching(bool test(Map element)) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void retainMatching(bool test(Map element)) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void setRange(int start, int rangeLength, List<Map> from, [int startFrom]) {
+ throw new UnsupportedError("Cannot setRange on immutable List.");
+ }
+
+ void removeRange(int start, int rangeLength) {
+ throw new UnsupportedError("Cannot removeRange on immutable List.");
+ }
+
+ void insertRange(int start, int rangeLength, [Map initialValue]) {
+ throw new UnsupportedError("Cannot insertRange on immutable List.");
+ }
+
+ List<Map> getRange(int start, int rangeLength) =>
+ Lists.getRange(this, start, rangeLength, <Map>[]);
+
+ // -- end List<Map> mixins.
+
+ @DomName('SQLResultSetRowList.item')
+ @DocsEditable
+ @Creates('=Object')
+ Map item(int index) {
+ return convertNativeToDart_Dictionary(_item_1(index));
+ }
+ @JSName('item')
+ @DomName('SQLResultSetRowList.item')
+ @DocsEditable
+ @Creates('=Object')
+ _item_1(index) native;
+}
+// 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.
+
+
+@DocsEditable
+@DomName('SQLTransaction')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
+class SqlTransaction native "*SQLTransaction" {
+
+ @DomName('SQLTransaction.executeSql')
+ @DocsEditable
+ void executeSql(String sqlStatement, List arguments, [SqlStatementCallback callback, SqlStatementErrorCallback errorCallback]) native;
+}
+// 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.
+
+
+@DocsEditable
+@DomName('SQLTransactionSync')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
+class SqlTransactionSync native "*SQLTransactionSync" {
+
+ @DomName('SQLTransactionSync.executeSql')
+ @DocsEditable
+ SqlResultSet executeSql(String sqlStatement, List arguments) native;
+}
diff --git a/sdk/lib/web_sql/dartium/web_sql_dartium.dart b/sdk/lib/web_sql/dartium/web_sql_dartium.dart
new file mode 100644
index 0000000..c25f0ae
--- /dev/null
+++ b/sdk/lib/web_sql/dartium/web_sql_dartium.dart
@@ -0,0 +1,473 @@
+library dart.dom.web_sql;
+
+import 'dart:async';
+import 'dart:collection';
+import 'dart:html';
+import 'dart:html_common';
+import 'dart:nativewrappers';
+// DO NOT EDIT - unless you are editing documentation as per:
+// https://code.google.com/p/dart/wiki/ContributingHTMLDocumentation
+// Auto-generated dart:audio library.
+
+
+
+
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+typedef void DatabaseCallback(database);
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+typedef void SqlStatementCallback(SqlTransaction transaction, SqlResultSet resultSet);
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+typedef void SqlStatementErrorCallback(SqlTransaction transaction, SqlError error);
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+typedef void SqlTransactionCallback(SqlTransaction transaction);
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+typedef void SqlTransactionErrorCallback(SqlError error);
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+typedef void SqlTransactionSyncCallback(SqlTransactionSync transaction);
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+@DocsEditable
+@DomName('Database')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
+class SqlDatabase extends NativeFieldWrapperClass1 {
+ SqlDatabase.internal();
+
+ /// Checks if this type is supported on the current platform.
+ static bool get supported => true;
+
+ @DomName('Database.version')
+ @DocsEditable
+ String get version native "Database_version_Getter";
+
+ @DomName('Database.changeVersion')
+ @DocsEditable
+ void changeVersion(String oldVersion, String newVersion, [SqlTransactionCallback callback, SqlTransactionErrorCallback errorCallback, VoidCallback successCallback]) native "Database_changeVersion_Callback";
+
+ @DomName('Database.readTransaction')
+ @DocsEditable
+ void readTransaction(SqlTransactionCallback callback, [SqlTransactionErrorCallback errorCallback, VoidCallback successCallback]) native "Database_readTransaction_Callback";
+
+ @DomName('Database.transaction')
+ @DocsEditable
+ void transaction(SqlTransactionCallback callback, [SqlTransactionErrorCallback errorCallback, VoidCallback successCallback]) native "Database_transaction_Callback";
+
+}
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+@DocsEditable
+@DomName('DatabaseSync')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
+class SqlDatabaseSync extends NativeFieldWrapperClass1 {
+ SqlDatabaseSync.internal();
+
+ @DomName('DatabaseSync.lastErrorMessage')
+ @DocsEditable
+ String get lastErrorMessage native "DatabaseSync_lastErrorMessage_Getter";
+
+ @DomName('DatabaseSync.version')
+ @DocsEditable
+ String get version native "DatabaseSync_version_Getter";
+
+ @DomName('DatabaseSync.changeVersion')
+ @DocsEditable
+ void changeVersion(String oldVersion, String newVersion, [SqlTransactionSyncCallback callback]) native "DatabaseSync_changeVersion_Callback";
+
+ @DomName('DatabaseSync.readTransaction')
+ @DocsEditable
+ void readTransaction(SqlTransactionSyncCallback callback) native "DatabaseSync_readTransaction_Callback";
+
+ @DomName('DatabaseSync.transaction')
+ @DocsEditable
+ void transaction(SqlTransactionSyncCallback callback) native "DatabaseSync_transaction_Callback";
+
+}
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+@DocsEditable
+@DomName('SQLError')
+class SqlError extends NativeFieldWrapperClass1 {
+ SqlError.internal();
+
+ static const int CONSTRAINT_ERR = 6;
+
+ static const int DATABASE_ERR = 1;
+
+ static const int QUOTA_ERR = 4;
+
+ static const int SYNTAX_ERR = 5;
+
+ static const int TIMEOUT_ERR = 7;
+
+ static const int TOO_LARGE_ERR = 3;
+
+ static const int UNKNOWN_ERR = 0;
+
+ static const int VERSION_ERR = 2;
+
+ @DomName('SQLError.code')
+ @DocsEditable
+ int get code native "SQLError_code_Getter";
+
+ @DomName('SQLError.message')
+ @DocsEditable
+ String get message native "SQLError_message_Getter";
+
+}
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+@DocsEditable
+@DomName('SQLException')
+class SqlException extends NativeFieldWrapperClass1 {
+ SqlException.internal();
+
+ static const int CONSTRAINT_ERR = 6;
+
+ static const int DATABASE_ERR = 1;
+
+ static const int QUOTA_ERR = 4;
+
+ static const int SYNTAX_ERR = 5;
+
+ static const int TIMEOUT_ERR = 7;
+
+ static const int TOO_LARGE_ERR = 3;
+
+ static const int UNKNOWN_ERR = 0;
+
+ static const int VERSION_ERR = 2;
+
+ @DomName('SQLException.code')
+ @DocsEditable
+ int get code native "SQLException_code_Getter";
+
+ @DomName('SQLException.message')
+ @DocsEditable
+ String get message native "SQLException_message_Getter";
+
+}
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+@DocsEditable
+@DomName('SQLResultSet')
+class SqlResultSet extends NativeFieldWrapperClass1 {
+ SqlResultSet.internal();
+
+ @DomName('SQLResultSet.insertId')
+ @DocsEditable
+ int get insertId native "SQLResultSet_insertId_Getter";
+
+ @DomName('SQLResultSet.rows')
+ @DocsEditable
+ SqlResultSetRowList get rows native "SQLResultSet_rows_Getter";
+
+ @DomName('SQLResultSet.rowsAffected')
+ @DocsEditable
+ int get rowsAffected native "SQLResultSet_rowsAffected_Getter";
+
+}
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+@DocsEditable
+@DomName('SQLResultSetRowList')
+class SqlResultSetRowList extends NativeFieldWrapperClass1 implements List<Map> {
+ SqlResultSetRowList.internal();
+
+ @DomName('SQLResultSetRowList.length')
+ @DocsEditable
+ int get length native "SQLResultSetRowList_length_Getter";
+
+ Map operator[](int index) native "SQLResultSetRowList_item_Callback";
+
+ void operator[]=(int index, Map value) {
+ throw new UnsupportedError("Cannot assign element of immutable List.");
+ }
+ // -- start List<Map> mixins.
+ // Map is the element type.
+
+ // From Iterable<Map>:
+
+ Iterator<Map> get iterator {
+ // Note: NodeLists are not fixed size. And most probably length shouldn't
+ // be cached in both iterator _and_ forEach method. For now caching it
+ // for consistency.
+ return new FixedSizeListIterator<Map>(this);
+ }
+
+ dynamic reduce(dynamic initialValue, dynamic combine(dynamic, Map)) {
+ return IterableMixinWorkaround.reduce(this, initialValue, combine);
+ }
+
+ bool contains(Map element) => IterableMixinWorkaround.contains(this, element);
+
+ void forEach(void f(Map element)) => IterableMixinWorkaround.forEach(this, f);
+
+ String join([String separator]) =>
+ IterableMixinWorkaround.joinList(this, separator);
+
+ Iterable map(f(Map element)) =>
+ IterableMixinWorkaround.mapList(this, f);
+
+ Iterable<Map> where(bool f(Map element)) =>
+ IterableMixinWorkaround.where(this, f);
+
+ Iterable expand(Iterable f(Map element)) =>
+ IterableMixinWorkaround.expand(this, f);
+
+ bool every(bool f(Map element)) => IterableMixinWorkaround.every(this, f);
+
+ bool any(bool f(Map element)) => IterableMixinWorkaround.any(this, f);
+
+ List<Map> toList() => new List<Map>.from(this);
+ Set<Map> toSet() => new Set<Map>.from(this);
+
+ bool get isEmpty => this.length == 0;
+
+ Iterable<Map> take(int n) => IterableMixinWorkaround.takeList(this, n);
+
+ Iterable<Map> takeWhile(bool test(Map value)) {
+ return IterableMixinWorkaround.takeWhile(this, test);
+ }
+
+ Iterable<Map> skip(int n) => IterableMixinWorkaround.skipList(this, n);
+
+ Iterable<Map> skipWhile(bool test(Map value)) {
+ return IterableMixinWorkaround.skipWhile(this, test);
+ }
+
+ Map firstMatching(bool test(Map value), { Map orElse() }) {
+ return IterableMixinWorkaround.firstMatching(this, test, orElse);
+ }
+
+ Map lastMatching(bool test(Map value), {Map orElse()}) {
+ return IterableMixinWorkaround.lastMatchingInList(this, test, orElse);
+ }
+
+ Map singleMatching(bool test(Map value)) {
+ return IterableMixinWorkaround.singleMatching(this, test);
+ }
+
+ Map elementAt(int index) {
+ return this[index];
+ }
+
+ // From Collection<Map>:
+
+ void add(Map value) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ void addLast(Map value) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ void addAll(Iterable<Map> iterable) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ // From List<Map>:
+ void set length(int value) {
+ throw new UnsupportedError("Cannot resize immutable List.");
+ }
+
+ void clear() {
+ throw new UnsupportedError("Cannot clear immutable List.");
+ }
+
+ Iterable<Map> get reversed {
+ return IterableMixinWorkaround.reversedList(this);
+ }
+
+ void sort([int compare(Map a, Map b)]) {
+ throw new UnsupportedError("Cannot sort immutable List.");
+ }
+
+ int indexOf(Map element, [int start = 0]) =>
+ Lists.indexOf(this, element, start, this.length);
+
+ int lastIndexOf(Map element, [int start]) {
+ if (start == null) start = length - 1;
+ return Lists.lastIndexOf(this, element, start);
+ }
+
+ Map get first {
+ if (this.length > 0) return this[0];
+ throw new StateError("No elements");
+ }
+
+ Map get last {
+ if (this.length > 0) return this[this.length - 1];
+ throw new StateError("No elements");
+ }
+
+ Map get single {
+ if (length == 1) return this[0];
+ if (length == 0) throw new StateError("No elements");
+ throw new StateError("More than one element");
+ }
+
+ Map min([int compare(Map a, Map b)]) =>
+ IterableMixinWorkaround.min(this, compare);
+
+ Map max([int compare(Map a, Map b)]) =>
+ IterableMixinWorkaround.max(this, compare);
+
+ Map removeAt(int pos) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ Map removeLast() {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void remove(Object object) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void removeAll(Iterable elements) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void retainAll(Iterable elements) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void removeMatching(bool test(Map element)) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void retainMatching(bool test(Map element)) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void setRange(int start, int rangeLength, List<Map> from, [int startFrom]) {
+ throw new UnsupportedError("Cannot setRange on immutable List.");
+ }
+
+ void removeRange(int start, int rangeLength) {
+ throw new UnsupportedError("Cannot removeRange on immutable List.");
+ }
+
+ void insertRange(int start, int rangeLength, [Map initialValue]) {
+ throw new UnsupportedError("Cannot insertRange on immutable List.");
+ }
+
+ List<Map> getRange(int start, int rangeLength) =>
+ Lists.getRange(this, start, rangeLength, <Map>[]);
+
+ // -- end List<Map> mixins.
+
+ @DomName('SQLResultSetRowList.item')
+ @DocsEditable
+ Map item(int index) native "SQLResultSetRowList_item_Callback";
+
+}
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+@DocsEditable
+@DomName('SQLTransaction')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
+class SqlTransaction extends NativeFieldWrapperClass1 {
+ SqlTransaction.internal();
+
+ @DomName('SQLTransaction.executeSql')
+ @DocsEditable
+ void executeSql(String sqlStatement, List arguments, [SqlStatementCallback callback, SqlStatementErrorCallback errorCallback]) native "SQLTransaction_executeSql_Callback";
+
+}
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+@DocsEditable
+@DomName('SQLTransactionSync')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
+class SqlTransactionSync extends NativeFieldWrapperClass1 {
+ SqlTransactionSync.internal();
+
+ @DomName('SQLTransactionSync.executeSql')
+ @DocsEditable
+ SqlResultSet executeSql(String sqlStatement, List arguments) native "SQLTransactionSync_executeSql_Callback";
+
+}
diff --git a/tests/co19/co19-compiler.status b/tests/co19/co19-compiler.status
index 423a814..60c8924 100644
--- a/tests/co19/co19-compiler.status
+++ b/tests/co19/co19-compiler.status
@@ -490,6 +490,7 @@
LibTest/core/Queue/removeLast_A02_t01: Fail # Moved collection classes from core to collection. co19 issue 371.
LibTest/core/Set/Set.from_A01_t01: Fail # Moved collection classes from core to collection. co19 issue 371.
+LibTest/core/Date/compareTo_A03_t01: Fail # Comparable<T>.compareTo now statically accepts type T arguments.
LibTest/core/RegExp/Pattern_semantics/firstMatch_CharacterClassEscape_A03_t01: Fail # Strings class has been removed. co19 issue 380
LibTest/core/RegExp/Pattern_semantics/firstMatch_CharacterClassEscape_A04_t01: Fail # Strings class has been removed. co19 issue 380
diff --git a/tests/co19/co19-dart2dart.status b/tests/co19/co19-dart2dart.status
index 8d256ea..659bf48 100644
--- a/tests/co19/co19-dart2dart.status
+++ b/tests/co19/co19-dart2dart.status
@@ -1,4 +1,4 @@
-# Copyright (c) 2012, 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.
@@ -58,8 +58,6 @@
Language/05_Variables/05_Variables_A01_t14: Fail # http://dartbug.com/5519
Language/05_Variables/05_Variables_A01_t15: Fail # http://dartbug.com/5519
Language/05_Variables/05_Variables_A04_t01: Fail # http://dartbug.com/5519
-Language/05_Variables/05_Variables_A05_t01: Fail # http://dartbug.com/5519
-Language/05_Variables/05_Variables_A05_t02: Fail # http://dartbug.com/5519
Language/05_Variables/05_Variables_A05_t04: Fail # Inherited from dart2js
Language/05_Variables/05_Variables_A05_t05: Fail # Inherited from dart2js
Language/05_Variables/05_Variables_A05_t06: Fail # Inherited from dart2js
@@ -67,7 +65,6 @@
Language/05_Variables/05_Variables_A05_t08: Fail # Inherited from dart2js
Language/05_Variables/05_Variables_A05_t09: Fail # Inherited from dart2js
Language/05_Variables/05_Variables_A05_t10: Fail # Inherited from dart2js
-Language/05_Variables/05_Variables_A05_t11: Fail # Inherited from VM (assignment to final variable does not throw NSME).
Language/05_Variables/05_Variables_A05_t13: Fail # Inherited from VM (assignment to final variable does not throw NSME).
Language/05_Variables/05_Variables_A05_t14: Fail # Inherited from VM (assignment to final variable does not throw NSME).
Language/05_Variables/05_Variables_A05_t15: Fail # Inherited from VM (assignment to final variable does not throw NSME).
@@ -144,7 +141,6 @@
Language/07_Classes/6_Constructors/1_Generative_Constructors_A09_t01: Fail # http://dartbug.com/5519
Language/07_Classes/6_Constructors/1_Generative_Constructors_A15_t07: Fail # inherited from VM
Language/07_Classes/6_Constructors/1_Generative_Constructors_A16_t07: Fail # http://dartbug.com/6242
-Language/07_Classes/6_Constructors/1_Generative_Constructors_A21_t01: Fail # http://dartbug.com/5519
Language/07_Classes/6_Constructors/2_Factories_A01_t05: Fail # Inherited from dartjs
Language/07_Classes/6_Constructors/2_Factories_A07_t01: Fail # inherited from VM
Language/07_Classes/6_Constructors/3_Constant_Constructors_A03_t01: Fail # http://dartbug.com/5519
diff --git a/tests/co19/co19-dart2js.status b/tests/co19/co19-dart2js.status
index 41e3a33..e311447 100644
--- a/tests/co19/co19-dart2js.status
+++ b/tests/co19/co19-dart2js.status
@@ -7,7 +7,6 @@
[ $compiler == dart2js ]
Language/03_Overview/1_Scoping_A02_t05: Fail # TODO(ahe): Please triage this failure.
Language/03_Overview/1_Scoping_A02_t06: Fail # TODO(ahe): Please triage this failure.
-Language/05_Variables/05_Variables_A05_t02: Fail # TODO(ahe): Please triage this failure.
Language/05_Variables/05_Variables_A05_t04: Fail # TODO(ahe): Please triage this failure.
Language/05_Variables/05_Variables_A05_t05: Fail # TODO(ahe): Please triage this failure.
Language/05_Variables/05_Variables_A05_t06: Fail # TODO(ahe): Please triage this failure.
@@ -15,10 +14,6 @@
Language/05_Variables/05_Variables_A05_t08: Fail # TODO(ahe): Please triage this failure.
Language/05_Variables/05_Variables_A05_t09: Fail # TODO(ahe): Please triage this failure.
Language/05_Variables/05_Variables_A05_t10: Fail # TODO(ahe): Please triage this failure.
-Language/05_Variables/05_Variables_A05_t11: Fail # TODO(ahe): Please triage this failure.
-Language/05_Variables/05_Variables_A05_t13: Fail # TODO(ahe): Please triage this failure.
-Language/05_Variables/05_Variables_A05_t14: Fail # TODO(ahe): Please triage this failure.
-Language/05_Variables/05_Variables_A05_t15: Fail # TODO(ahe): Please triage this failure.
Language/05_Variables/05_Variables_A07_t01: Fail # TODO(ahe): Please triage this failure.
Language/05_Variables/05_Variables_A07_t02: Fail # TODO(ahe): Please triage this failure.
Language/05_Variables/05_Variables_A07_t03: Fail # TODO(ahe): Please triage this failure.
@@ -115,9 +110,6 @@
Language/12_Statements/09_Switch_A03_t02: Fail # TODO(ahe): Please triage this failure.
Language/12_Statements/09_Switch_A04_t01: Fail # TODO(ahe): Please triage this failure.
Language/12_Statements/09_Switch_A06_t02: Fail # TODO(ahe): Please triage this failure.
-Language/12_Statements/10_Try_A03_t01: Fail # TODO(ahe): Please triage this failure.
-Language/12_Statements/10_Try_A03_t02: Fail # TODO(ahe): Please triage this failure.
-Language/12_Statements/10_Try_A03_t03: Fail # TODO(ahe): Please triage this failure.
Language/12_Statements/10_Try_A06_t01: Fail # TODO(ahe): Please triage this failure.
Language/12_Statements/10_Try_A07_t03: Fail # TODO(ahe): Please triage this failure.
Language/12_Statements/11_Return_A05_t01: Fail # TODO(ahe): Please triage this failure.
@@ -180,10 +172,6 @@
[ $compiler == dart2js && $runtime == jsshell ]
LibTest/core/Map/Map_class_A01_t04: Pass, Slow # Issue 8096
-Language/05_Variables/05_Variables_A13_t02: Fail # TODO(ngeoaffray): Please triage these failure.
-Language/06_Functions/3_Type_of_a_Function_A01_t01: Fail # TODO(ngeoaffray): Please triage these failure.
-Language/11_Expressions/05_Strings/1_String_Interpolation_A03_t02: Fail # TODO(ngeoaffray): Please triage these failure.
-Language/11_Expressions/05_Strings/1_String_Interpolation_A04_t02: Fail # TODO(ngeoaffray): Please triage these failure.
Language/11_Expressions/14_Function_Invocation/4_Function_Expression_Invocation_A03_t01: Fail # TODO(ngeoaffray): Please triage these failure.
Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A02_t04: Fail # TODO(ngeoaffray): Please triage these failure.
LibTest/core/double/round_A01_t01: Fail # TODO(ngeoaffray): Please triage these failure.
@@ -575,6 +563,10 @@
LibTest/core/Date/operator_equality_A01_t01: Fail # DateTime.== now looks at timezone, co19 issue 379.
+Language/12_Statements/10_Try_A03_t01: Fail # co19 issue 381
+Language/12_Statements/10_Try_A03_t02: Fail # co19 issue 381
+Language/12_Statements/10_Try_A03_t03: Fail # co19 issue 381
+
LibTest/core/Strings/join_A01_t01: Fail # Strings class has been removed. co19 issue 380
LibTest/core/Strings/join_A04_t01: Fail # Strings class has been removed. co19 issue 380
LibTest/core/Strings/concatAll_A01_t01: Fail # Strings class has been removed. co19 issue 380
@@ -627,6 +619,11 @@
LibTest/math/atan_A01_t01: Fail, OK # co19 issue 44
+[ $compiler == dart2js && $host_checked ]
+Language/14_Types/4_Interface_Types_A11_t01: Crash # Issue 8557
+Language/14_Types/4_Interface_Types_A11_t02: Crash # Issue 8557
+
+
#
# The following tests are failing. Please add the error message
# (either a compiler error or exception message). The error messages
@@ -677,7 +674,6 @@
Language/05_Variables/05_Variables_A01_t14: Fail # Checks that variable declaration cannot contain 'const', 'final' and 'var' simultaneously.
Language/05_Variables/05_Variables_A01_t15: Fail # Checks that a variable declaration cannot contain the 'abstract' keyword.
Language/05_Variables/05_Variables_A04_t01: Fail # Checks that a compile-time error occurs if a local constant variable is redefined.
-Language/05_Variables/05_Variables_A05_t01: Fail # Checks that a compile-time error occurs if a constant variable is not initialized.
Language/06_Functions/06_Functions_A01_t31: Fail # Checks that functions can't be declared as static inside of a function body.
Language/06_Functions/1_Function_Declaration_A01_t01: Fail # Checks that it is a compile-time error to preface library function with 'static'.
Language/06_Functions/2_Formal_Parameters/1_Required_Formals_A02_t03: Fail # Checks that a required parameter can be constant. Reassigning it should produce a compile-time error.
@@ -713,7 +709,6 @@
Language/07_Classes/4_Abstract_Instance_Members_A04_t06: Fail # Checks that a compile-time error is produced when the overriding non-abstract instance method has almost the same set of named parameters as the abstract method being overriden, except for one that has a different name.
Language/07_Classes/6_Constructors/1_Generative_Constructors_A03_t05: Fail # Checks that redirecting constructor can not have initializing formals.
Language/07_Classes/6_Constructors/1_Generative_Constructors_A09_t01: Fail # Checks that error is produced if a final variable is not initialized in one of the specified ways.
-Language/07_Classes/6_Constructors/1_Generative_Constructors_A21_t01: Fail # Checks that a compile-time error occur if k’s initializer list contains an initializer for a final variable whose declaration includes an initialization expression.
Language/07_Classes/6_Constructors/3_Constant_Constructors_A03_t01: Fail # Checks that a compile-time error is produced when a class with constant constructor also declares a non-final instance variable.
Language/07_Classes/6_Constructors/3_Constant_Constructors_A03_t02: Fail # Checks that compile-time error is produced when a class with constant constructor inherits a non-final instance variable.
Language/07_Classes/6_Constructors_A01_t01: Fail # Checks that a compile-error is produced when a constructor's id coincides with the name of a field in the same class.
@@ -777,16 +772,6 @@
Language/14_Types/3_Type_Declarations/1_Typedef_A07_t03: Fail # dartbug.com/7202
Language/14_Types/3_Type_Declarations/1_Typedef_A07_t04: Fail # dartbug.com/7202
-Language/14_Types/5_Function_Types_A01_t08: Fail # dartbug.com/7342
-Language/14_Types/5_Function_Types_A01_t09: Fail # dartbug.com/7342
-Language/14_Types/5_Function_Types_A01_t11: Fail # dartbug.com/7342
-Language/14_Types/5_Function_Types_A02_t05: Fail # dartbug.com/7342
-Language/14_Types/5_Function_Types_A02_t09: Fail # dartbug.com/7342
-Language/14_Types/5_Function_Types_A02_t10: Fail # dartbug.com/7342
-Language/14_Types/5_Function_Types_A03_t06: Fail # dartbug.com/7342
-Language/14_Types/5_Function_Types_A03_t10: Fail # dartbug.com/7342
-Language/14_Types/5_Function_Types_A03_t11: Fail # dartbug.com/7342
-
# Doesn't expect null to be allowed in Set or Map keys (issue 377).
LibTest/core/Map/containsKey_A01_t02: Fail
LibTest/core/Map/operator_subscript_A01_t02: Fail
diff --git a/tests/co19/co19-runtime.status b/tests/co19/co19-runtime.status
index 0c4e1d2..fb79c4e 100644
--- a/tests/co19/co19-runtime.status
+++ b/tests/co19/co19-runtime.status
@@ -11,9 +11,9 @@
Language/13_Libraries_and_Scripts/1_Imports_A02_t22: Crash # Dart issue 6060
Language/06_Functions/2_Formal_Parameters/2_Optional_Formals_A01_t10: Fail # co19 issue 348
-Language/07_Classes/3_Setters_A04_t01: Fail # Dart issue 5840
-Language/07_Classes/3_Setters_A04_t04: Fail # Dart issue 5840
-Language/07_Classes/3_Setters_A04_t05: Fail # Dart issue 5840
+Language/07_Classes/3_Setters_A04_t01: Fail # co19 issue 383
+Language/07_Classes/3_Setters_A04_t04: Fail # co19 issue 383
+Language/07_Classes/3_Setters_A04_t05: Fail # co19 issue 383
Language/07_Classes/4_Abstract_Instance_Members_A03_t02: Fail # Dart issue 978
Language/07_Classes/4_Abstract_Instance_Members_A03_t03: Fail # Dart issue 978
Language/07_Classes/4_Abstract_Instance_Members_A03_t04: Fail # Dart issue 978
diff --git a/tests/compiler/dart2js/analyze_all_test.dart b/tests/compiler/dart2js/analyze_all_test.dart
index f172ada..c972f82 100644
--- a/tests/compiler/dart2js/analyze_all_test.dart
+++ b/tests/compiler/dart2js/analyze_all_test.dart
@@ -2,13 +2,8 @@
// 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:uri';
-
import 'compiler_helper.dart';
-import '../../../sdk/lib/_internal/compiler/implementation/dart2jslib.dart'
- show MessageKind;
-
const String SOURCE = """
class Foo {
// Deliberately not const to ensure compile error.
diff --git a/tests/compiler/dart2js/analyze_api_test.dart b/tests/compiler/dart2js/analyze_api_test.dart
index 9e1d365..1432d40 100644
--- a/tests/compiler/dart2js/analyze_api_test.dart
+++ b/tests/compiler/dart2js/analyze_api_test.dart
@@ -14,18 +14,88 @@
import '../../../sdk/lib/_internal/compiler/implementation/source_file_provider.dart';
import '../../../sdk/lib/_internal/libraries.dart';
+/**
+ * Map of white-listed warnings and errors.
+ *
+ * Only add a white-listing together with a bug report to dartbug.com and add
+ * the bug issue number as a comment on the white-listing.
+ *
+ * Use an identifiable suffix of the file uri as key. Use a fixed substring of
+ * the error/warning message in the list of white-listings for each file.
+ */
+// TODO(johnniwinther): Support canonical URIs as keys and message kinds as
+// values.
+const Map<String,List<String>> WHITE_LIST = const { };
+
class CollectingDiagnosticHandler extends FormattingDiagnosticHandler {
bool hasWarnings = false;
bool hasErrors = false;
- CollectingDiagnosticHandler(SourceFileProvider provider) : super(provider);
+ Map<String,Map<String,int>> whiteListMap = new Map<String,Map<String,int>>();
+
+ CollectingDiagnosticHandler(SourceFileProvider provider) : super(provider) {
+ WHITE_LIST.forEach((String file, List<String> messageParts) {
+ var useMap = new Map<String,int>();
+ for (String messagePart in messageParts) {
+ useMap[messagePart] = 0;
+ }
+ whiteListMap[file] = useMap;
+ });
+ }
+
+ bool checkWhiteListUse() {
+ bool allUsed = true;
+ for (String file in whiteListMap.keys) {
+ for (String messagePart in whiteListMap[file].keys) {
+ if (whiteListMap[file][messagePart] == 0) {
+ print("White-listing '$messagePart' is unused in '$file'. "
+ "Remove the white-listing from the WHITE_LIST map.");
+ allUsed = false;
+ }
+ }
+ }
+ return allUsed;
+ }
+
+ void reportWhiteListUse() {
+ for (String file in whiteListMap.keys) {
+ for (String messagePart in whiteListMap[file].keys) {
+ int useCount = whiteListMap[file][messagePart];
+ print("White-listed message '$messagePart' suppressed $useCount "
+ "time(s) in '$file'.");
+ }
+ }
+ }
+
+ bool checkWhiteList(Uri uri, String message) {
+ String path = uri.path;
+ for (String file in whiteListMap.keys) {
+ if (path.endsWith(file)) {
+ for (String messagePart in whiteListMap[file].keys) {
+ if (message.contains(messagePart)) {
+ whiteListMap[file][messagePart]++;
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
void diagnosticHandler(Uri uri, int begin, int end, String message,
api.Diagnostic kind) {
if (kind == api.Diagnostic.WARNING) {
+ if (checkWhiteList(uri, message)) {
+ // Suppress white listed warnings.
+ return;
+ }
hasWarnings = true;
}
if (kind == api.Diagnostic.ERROR) {
+ if (checkWhiteList(uri, message)) {
+ // Suppress white listed warnings.
+ return;
+ }
hasErrors = true;
}
super.diagnosticHandler(uri, begin, end, message, kind);
@@ -54,4 +124,6 @@
compiler.run(null);
Expect.isFalse(handler.hasWarnings);
Expect.isFalse(handler.hasErrors);
+ Expect.isTrue(handler.checkWhiteListUse());
+ handler.reportWhiteListUse();
}
diff --git a/tests/compiler/dart2js/backend_htype_list_test.dart b/tests/compiler/dart2js/backend_htype_list_test.dart
index 7cd5d3b..93d37a4 100644
--- a/tests/compiler/dart2js/backend_htype_list_test.dart
+++ b/tests/compiler/dart2js/backend_htype_list_test.dart
@@ -2,13 +2,8 @@
// 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:uri';
-
-import '../../../sdk/lib/_internal/compiler/implementation/elements/elements.dart';
import '../../../sdk/lib/_internal/compiler/implementation/js_backend/js_backend.dart';
import '../../../sdk/lib/_internal/compiler/implementation/ssa/ssa.dart';
-import '../../../sdk/lib/_internal/compiler/implementation/dart2jslib.dart'
- show Selector;
import 'compiler_helper.dart';
import 'parser_helper.dart';
diff --git a/tests/compiler/dart2js/call_site_type_inferer_static_test.dart b/tests/compiler/dart2js/call_site_type_inferer_static_test.dart
index 8b73b45..bc4e2c5 100644
--- a/tests/compiler/dart2js/call_site_type_inferer_static_test.dart
+++ b/tests/compiler/dart2js/call_site_type_inferer_static_test.dart
@@ -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.
-import "dart:uri";
-
import "../../../sdk/lib/_internal/compiler/implementation/js_backend/js_backend.dart";
import "../../../sdk/lib/_internal/compiler/implementation/ssa/ssa.dart";
diff --git a/tests/compiler/dart2js/call_site_type_inferer_test.dart b/tests/compiler/dart2js/call_site_type_inferer_test.dart
index c868f4b..f3c9e76 100644
--- a/tests/compiler/dart2js/call_site_type_inferer_test.dart
+++ b/tests/compiler/dart2js/call_site_type_inferer_test.dart
@@ -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.
-import 'dart:uri';
-
import '../../../sdk/lib/_internal/compiler/implementation/js_backend/js_backend.dart';
import '../../../sdk/lib/_internal/compiler/implementation/ssa/ssa.dart';
diff --git a/tests/compiler/dart2js/compiler_helper.dart b/tests/compiler/dart2js/compiler_helper.dart
index 57ef07d..405fb63 100644
--- a/tests/compiler/dart2js/compiler_helper.dart
+++ b/tests/compiler/dart2js/compiler_helper.dart
@@ -5,22 +5,41 @@
library compiler_helper;
-import "dart:uri";
+import 'dart:uri';
+export 'dart:uri' show Uri;
-import "../../../sdk/lib/_internal/compiler/implementation/elements/elements.dart"
+import '../../../sdk/lib/_internal/compiler/implementation/elements/elements.dart'
as lego;
-import "../../../sdk/lib/_internal/compiler/implementation/js_backend/js_backend.dart"
+export '../../../sdk/lib/_internal/compiler/implementation/elements/elements.dart';
+
+import '../../../sdk/lib/_internal/compiler/implementation/js_backend/js_backend.dart'
as js;
-import "../../../sdk/lib/_internal/compiler/implementation/dart2jslib.dart"
+
+import '../../../sdk/lib/_internal/compiler/implementation/dart2jslib.dart'
as leg;
-import "../../../sdk/lib/_internal/compiler/implementation/ssa/ssa.dart" as ssa;
-import "../../../sdk/lib/_internal/compiler/implementation/util/util.dart";
+export '../../../sdk/lib/_internal/compiler/implementation/dart2jslib.dart'
+ show Constant,
+ Message,
+ MessageKind,
+ Selector,
+ SourceSpan;
+
+import '../../../sdk/lib/_internal/compiler/implementation/ssa/ssa.dart' as ssa;
+
+import '../../../sdk/lib/_internal/compiler/implementation/util/util.dart';
+export '../../../sdk/lib/_internal/compiler/implementation/util/util.dart';
+
import '../../../sdk/lib/_internal/compiler/implementation/source_file.dart';
+
import '../../../sdk/lib/_internal/compiler/implementation/dart2jslib.dart'
show Compiler;
-import "mock_compiler.dart";
-import "parser_helper.dart";
+export '../../../sdk/lib/_internal/compiler/implementation/tree/tree.dart';
+
+import 'mock_compiler.dart';
+export 'mock_compiler.dart';
+
+import 'parser_helper.dart';
String compile(String code, {String entry: 'main',
String coreSource: DEFAULT_CORELIB,
@@ -39,6 +58,7 @@
compiler.phase = Compiler.PHASE_RESOLVING;
compiler.backend.enqueueHelpers(compiler.enqueuer.resolution);
compiler.processQueue(compiler.enqueuer.resolution, element);
+ compiler.world.populate();
var context = new js.JavaScriptItemCompilationContext();
leg.ResolutionWorkItem resolutionWork =
new leg.ResolutionWorkItem(element, context);
@@ -79,6 +99,23 @@
return check(compiler, element);
}
+compileSources(Map<String, String> sources,
+ check(MockCompiler compiler)) {
+ Uri base = new Uri.fromComponents(scheme: 'source');
+ Uri mainUri = base.resolve('main.dart');
+ String mainCode = sources['main.dart'];
+ Expect.isNotNull(mainCode, 'No source code found for "main.dart"');
+ MockCompiler compiler = compilerFor(mainCode, mainUri);
+
+ sources.forEach((String path, String code) {
+ if (path == 'main.dart') return;
+ compiler.registerSource(base.resolve(path), code);
+ });
+
+ compiler.runCompiler(mainUri);
+ return check(compiler);
+}
+
lego.Element findElement(compiler, String name) {
var element = compiler.mainApp.find(buildSourceString(name));
Expect.isNotNull(element, 'Could not locate $name.');
diff --git a/tests/compiler/dart2js/concrete_type_inference_test.dart b/tests/compiler/dart2js/concrete_type_inference_test.dart
index 90c479e..324b79d 100644
--- a/tests/compiler/dart2js/concrete_type_inference_test.dart
+++ b/tests/compiler/dart2js/concrete_type_inference_test.dart
@@ -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.
-import 'dart:uri';
-
import 'compiler_helper.dart';
import 'parser_helper.dart';
diff --git a/tests/compiler/dart2js/cpa_inference_test.dart b/tests/compiler/dart2js/cpa_inference_test.dart
index 8aa2bae..a204b4d 100644
--- a/tests/compiler/dart2js/cpa_inference_test.dart
+++ b/tests/compiler/dart2js/cpa_inference_test.dart
@@ -2,17 +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 "dart:uri";
-import "../../../sdk/lib/_internal/compiler/implementation/elements/elements.dart";
import '../../../sdk/lib/_internal/compiler/implementation/scanner/scannerlib.dart';
import '../../../sdk/lib/_internal/compiler/implementation/source_file.dart';
import '../../../sdk/lib/_internal/compiler/implementation/types/types.dart';
-import '../../../sdk/lib/_internal/compiler/implementation/tree/tree.dart';
-import "../../../sdk/lib/_internal/compiler/implementation/dart2jslib.dart" as leg;
import "parser_helper.dart";
import "compiler_helper.dart";
-import "mock_compiler.dart";
/**
* Finds the node corresponding to the last occurence of the substring
@@ -168,7 +163,9 @@
class Null {}
class Type {}
class Dynamic_ {}
- bool identical(Object a, Object b) {}''';
+ bool identical(Object a, Object b) {}
+ dynamic JS(String typeDescription, String codeTemplate,
+ [var arg0, var arg1, var arg2]) {}''';
AnalysisResult analyze(String code, {int maxConcreteTypeSize: 1000}) {
Uri uri = new Uri.fromComponents(scheme: 'source');
@@ -1023,6 +1020,17 @@
result.checkNodeHasUnknownType('x');
}
+testJsCall() {
+ final String source = r"""
+ main () {
+ var x = JS('', '', null);
+ x;
+ }
+ """;
+ AnalysisResult result = analyze(source);
+ result.checkNodeHasUnknownType('x');
+}
+
testIsCheck() {
final String source = r"""
main () {
@@ -1034,6 +1042,33 @@
result.checkNodeHasType('x', [result.bool]);
}
+testSeenClasses() {
+ final String source = r"""
+ class A {
+ witness() => 42;
+ }
+ class B {
+ witness() => "abc";
+ }
+ class AFactory {
+ onlyCalledInAFactory() => new A();
+ }
+ class BFactory {
+ onlyCalledInAFactory() => new B();
+ }
+
+ main() {
+ new AFactory().onlyCalledInAFactory();
+ new BFactory();
+ // should be of type {int} and not {int, String} since B is unreachable
+ var foo = "__dynamic_for_test".witness();
+ foo;
+ }
+ """;
+ AnalysisResult result = analyze(source);
+ result.checkNodeHasType('foo', [result.int]);
+}
+
void main() {
testDynamicBackDoor();
testLiterals();
@@ -1074,5 +1109,7 @@
testLists();
testListWithCapacity();
testEmptyList();
+ testJsCall();
testIsCheck();
+ testSeenClasses();
}
diff --git a/tests/compiler/dart2js/dart_backend_test.dart b/tests/compiler/dart2js/dart_backend_test.dart
index 6bf38fb..23f830d 100644
--- a/tests/compiler/dart2js/dart_backend_test.dart
+++ b/tests/compiler/dart2js/dart_backend_test.dart
@@ -21,8 +21,8 @@
interface double extends num {}
abstract class String {}
interface Function {}
-interface List {}
-interface Map {}
+interface List<T> {}
+interface Map<K,V> {}
interface Closure {}
interface Dynamic_ {}
interface Null {}
diff --git a/tests/compiler/dart2js/field_type_inferer_test.dart b/tests/compiler/dart2js/field_type_inferer_test.dart
index a3253b4..04773aa 100644
--- a/tests/compiler/dart2js/field_type_inferer_test.dart
+++ b/tests/compiler/dart2js/field_type_inferer_test.dart
@@ -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.
-import 'dart:uri';
-
import '../../../sdk/lib/_internal/compiler/implementation/js_backend/js_backend.dart';
import '../../../sdk/lib/_internal/compiler/implementation/ssa/ssa.dart';
import '../../../sdk/lib/_internal/compiler/implementation/scanner/scannerlib.dart';
diff --git a/tests/compiler/dart2js/gvn_dynamic_field_get_test.dart b/tests/compiler/dart2js/gvn_dynamic_field_get_test.dart
index ae87042..9d42462 100644
--- a/tests/compiler/dart2js/gvn_dynamic_field_get_test.dart
+++ b/tests/compiler/dart2js/gvn_dynamic_field_get_test.dart
@@ -6,7 +6,6 @@
import 'compiler_helper.dart';
import 'parser_helper.dart';
-import 'dart:uri';
const String TEST = r"""
class A {
@@ -31,7 +30,9 @@
checkNumberOfMatches(matches, 1);
var cls = findElement(compiler, 'A');
Expect.isNotNull(cls);
- var element = cls.lookupLocalMember(buildSourceString('foo'));
+ SourceString name = buildSourceString('foo');
+ var element = cls.lookupLocalMember(name);
Expect.isNotNull(element);
- Expect.isFalse(compiler.world.userDefinedGetters.contains(element));
+ Selector selector = new Selector.getter(name, null);
+ Expect.isFalse(compiler.world.hasAnyUserDefinedGetter(selector));
}
diff --git a/tests/compiler/dart2js/interceptor_test.dart b/tests/compiler/dart2js/interceptor_test.dart
index 3a346e1..ff0076a 100644
--- a/tests/compiler/dart2js/interceptor_test.dart
+++ b/tests/compiler/dart2js/interceptor_test.dart
@@ -41,14 +41,14 @@
// Check that one-shot interceptors preserve variable names, see
// https://code.google.com/p/dart/issues/detail?id=8106.
generated = compile(TEST_TWO, entry: 'foo');
- Expect.isTrue(generated.contains('\$.\$\$add(a, 42)'));
+ Expect.isTrue(generated.contains(r'$.$$add$n(a, 42)'));
Expect.isTrue(generated.contains('myVariableName'));
// Check that an intercepted getter that does not need to be
// intercepted, is turned into a regular getter call or field
// access.
generated = compile(TEST_THREE, entry: 'foo');
- Expect.isFalse(generated.contains(r'get$length'));
+ Expect.isFalse(generated.contains(r'a.get$length()'));
Expect.isTrue(generated.contains(r'$.A$().length'));
- Expect.isTrue(generated.contains(r'length(a)'));
+ Expect.isTrue(generated.contains(r'$.get$length$a(a)'));
}
diff --git a/tests/compiler/dart2js/js_parser_test.dart b/tests/compiler/dart2js/js_parser_test.dart
new file mode 100644
index 0000000..3bd6559
--- /dev/null
+++ b/tests/compiler/dart2js/js_parser_test.dart
@@ -0,0 +1,156 @@
+// 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.
+
+import 'mock_compiler.dart';
+import '../../../sdk/lib/_internal/compiler/implementation/js/js.dart' as jsAst;
+import '../../../sdk/lib/_internal/compiler/implementation/js/js.dart' show js;
+
+void testExpression(String expression, [String expect = ""]) {
+ jsAst.Node node = js[expression];
+ MockCompiler compiler = new MockCompiler();
+ String jsText =
+ jsAst.prettyPrint(node,
+ compiler,
+ allowVariableMinification: false).getText();
+ if (expect == "") {
+ Expect.stringEquals(expression, jsText);
+ } else {
+ Expect.stringEquals(expect, jsText);
+ }
+}
+
+void testError(String expression, [String expect = ""]) {
+ bool doCheck(exception) {
+ Expect.isTrue(exception.toString().contains(expect));
+ return true;
+ }
+ Expect.throws(() => js[expression], doCheck);
+}
+
+
+
+void main() {
+ // Asterisk indicates deviations from real JS.
+ // Simple var test.
+ testExpression('var a = ""');
+ // Parse and print will normalize whitespace.
+ testExpression(' var a = "" ', 'var a = ""');
+ // *We don't do operator prescedence.
+ testError('x = a + b * c', 'Mixed + and *');
+ // But we can chain binary operators (with left associativity).
+ testExpression('x = a + b + c');
+ // We can cope with relational operators and non-relational.
+ testExpression('a + b == c + d');
+ // The prettyprinter will insert braces where needed.
+ testExpression('a + (b == c) + d');
+ // We can handle () for calls.
+ testExpression('foo(bar)');
+ testExpression('foo(bar, baz)');
+ // *But we can't handle chained calls without parentheses.
+ testError('foo(bar)(baz)');
+ // The prettyprinter understands chained calls without extra parentheses.
+ testExpression('(foo(bar))(baz)', 'foo(bar)(baz)');
+ // Chains of dotting and calls.
+ testExpression('foo.bar(baz)');
+ // String literal.
+ testExpression('var x = "fisk"');
+ // String literal with \n.
+ testExpression(r'var x = "\n"');
+ // String literal with escaped quote.
+ testExpression(r'var x = "\""');
+ // *No clever escapes.
+ testError(r'var x = "\x42"', 'escapes allowed in string literals');
+ // Operator new.
+ testExpression('new Foo()');
+ // New with dotted access.
+ testExpression('new Frobinator.frobinate()');
+ // The prettyprinter is smarter than we are.
+ testExpression('(new Frobinator()).frobinate()',
+ 'new Frobinator().frobinate()');
+ // *We want a bracket on 'new'.
+ testError('new Foo');
+ testError('(new Foo)');
+ // Bogus operators.
+ testError('a +++ b', 'Unknown operator');
+ // This isn't perl. There are rules.
+ testError('a <=> b', 'Unknown operator');
+ // Typeof.
+ testExpression('typeof foo == "number"');
+ // *Strange relation.
+ testError('a < b < c', 'RELATION');
+ // Chained var.
+ testExpression('var x = 0, y = 1.2, z = 42');
+ // Empty object literal.
+ testExpression('foo({}, {})');
+ // *Can't handle non-empty object literals
+ testError('foo({meaning: 42})', 'Expected RBRACE');
+ // Literals.
+ testExpression('x(false, true, null)');
+ // *We should really throw here.
+ testExpression('var false = 42');
+ testExpression('var new = 42');
+ testExpression('var typeof = 42');
+ // Malformed decimal
+ testError('var x = 42.', "Unparseable number");
+ testError('var x = 1.1.1', "Unparseable number");
+ // More unary.
+ testExpression('x = !x');
+ testExpression('!x == false');
+ testExpression('var foo = void 0');
+ testExpression('delete foo.bar');
+ testExpression('delete foo');
+ testError('x typeof y', 'Unparsed junk');
+ testExpression('x &= ~mask');
+ // Adjacent tokens.
+ testExpression('foo[x[bar]]');
+ // Prefix ++ etc.
+ testExpression("++x");
+ testExpression("++foo.bar");
+ testExpression("+x");
+ testExpression("+foo.bar");
+ testExpression("-x");
+ testExpression("-foo.bar");
+ testExpression("--x");
+ testExpression("--foo.bar");
+ // Postfix ++ etc.
+ testExpression("x++");
+ testExpression("foo.bar++");
+ testExpression("x--");
+ testExpression("foo.bar--");
+ // Both!
+ testExpression("++x++");
+ testExpression("++foo.bar++");
+ testExpression("--x--");
+ testExpression("--foo.bar--");
+ // *We can't handle stacked unary operators.
+ testError("x++ ++");
+ testError("++ typeof x");
+ // ++ used as a binary operator.
+ testError("x++ ++ 42");
+ // Shift operators.
+ testExpression("x << 5");
+ testExpression("x << y + 1");
+ testExpression("x <<= y + 1");
+ // Array initializers.
+ testExpression("x = ['foo', 'bar', x[4]]");
+ testExpression("[]");
+ testError("[42 42]");
+ testExpression('beebop([1, 2, 3])');
+ // *We can't parse array literals with holes in them.
+ testError("[1,, 2]");
+ testError("[1,]");
+ testError("[,]");
+ testError("[, 42]");
+ // Ternary operator.
+ testExpression("x = a ? b : c");
+ testExpression("y = a == null ? b : a");
+ testExpression("y = a == null ? b + c : a + c");
+ testExpression("foo = a ? b : c ? d : e");
+ testExpression("foo = a ? b ? c : d : e");
+ testExpression("foo = (a = v) ? b = w : c = x ? d = y : e = z");
+ testExpression("foo = (a = v) ? b = w ? c = x : d = y : e = z");
+ // Stacked assignment.
+ testExpression("a = b = c");
+ testExpression("var a = b = c");
+}
diff --git a/tests/compiler/dart2js/metadata_test.dart b/tests/compiler/dart2js/metadata_test.dart
index 1591ebc..b90ca18 100644
--- a/tests/compiler/dart2js/metadata_test.dart
+++ b/tests/compiler/dart2js/metadata_test.dart
@@ -2,18 +2,9 @@
// 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:uri";
-
import 'compiler_helper.dart';
import 'parser_helper.dart';
-import '../../../sdk/lib/_internal/compiler/implementation/elements/elements.dart';
-import '../../../sdk/lib/_internal/compiler/implementation/dart2jslib.dart';
-import '../../../sdk/lib/_internal/compiler/implementation/util/util.dart'
- show Spannable, Link;
-import '../../../sdk/lib/_internal/compiler/implementation/tree/tree.dart'
- show Node, LibraryTag;
-
void checkPosition(Spannable spannable, Node node, String source, compiler) {
SourceSpan span = compiler.spanFromSpannable(spannable);
Expect.isTrue(span.begin < span.end,
diff --git a/tests/compiler/dart2js/mock_compiler.dart b/tests/compiler/dart2js/mock_compiler.dart
index 39ab912..686c8ff 100644
--- a/tests/compiler/dart2js/mock_compiler.dart
+++ b/tests/compiler/dart2js/mock_compiler.dart
@@ -25,6 +25,10 @@
import '../../../sdk/lib/_internal/compiler/implementation/dart_types.dart';
+import '../../../sdk/lib/_internal/compiler/implementation/deferred_load.dart'
+ show DeferredLoadTask;
+
+
class WarningMessage {
Node node;
Message message;
@@ -53,6 +57,7 @@
class LinkedHashMap {}
S() {}
assertHelper(a){}
+ createRuntimeType(a) {}
throwNoSuchMethod(obj, name, arguments, expectedArgumentNames) {}
throwAbstractClassInstantiationError(className) {}''';
@@ -89,9 +94,9 @@
operator <=(other) => true;
operator ==(other) => true;
}
- class JSInt {
+ class JSInt extends JSNumber {
}
- class JSDouble {
+ class JSDouble extends JSNumber {
}
class JSNull {
}
@@ -107,7 +112,10 @@
print(var obj) {}
abstract class num {}
abstract class int extends num { }
- abstract class double extends num { }
+ abstract class double extends num {
+ static var NAN = 0;
+ static parse(s) {}
+ }
class bool {}
class String {}
class Object {
@@ -118,6 +126,10 @@
class Function {}
class List<E> {}
abstract class Map<K,V> {}
+ class DateTime {
+ DateTime(year);
+ DateTime.utc(year);
+ }
bool identical(Object a, Object b) {}''';
const String DEFAULT_ISOLATE_HELPERLIB = r'''
@@ -167,6 +179,8 @@
// Our unit tests check code generation output that is affected by
// inlining support.
disableInlining = true;
+
+ deferredLoadTask = new MockDeferredLoadTask(this);
}
/**
@@ -182,11 +196,12 @@
* is fixed to export its top-level declarations.
*/
LibraryElement createLibrary(String name, String source) {
- Uri uri = new Uri.fromComponents(scheme: "source", path: name);
+ Uri uri = new Uri.fromComponents(scheme: "dart", path: name);
var script = new Script(uri, new MockFile(source));
var library = new LibraryElementX(script);
parseScript(source, library);
library.setExports(library.localScope.values.toList());
+ registerSource(uri, source);
return library;
}
@@ -342,3 +357,11 @@
map.remove(node);
}
}
+
+class MockDeferredLoadTask extends DeferredLoadTask {
+ MockDeferredLoadTask(Compiler compiler) : super(compiler);
+
+ void registerMainApp(LibraryElement mainApp) {
+ // Do nothing.
+ }
+}
diff --git a/tests/compiler/dart2js/resolution_test.dart b/tests/compiler/dart2js/resolution_test.dart
new file mode 100644
index 0000000..cd588bd
--- /dev/null
+++ b/tests/compiler/dart2js/resolution_test.dart
@@ -0,0 +1,118 @@
+// 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.
+
+// Test that resolution does not resolve things we know will not be
+// needed by the backend.
+
+import 'compiler_helper.dart';
+import 'parser_helper.dart';
+
+const String NO_RUNTIME_TYPE = r"""
+import 'dart:core' as prefix;
+class A {
+ A();
+ A.z();
+ static var bar;
+ static foo() {}
+}
+main() {
+ var print = prefix.print;
+ // Check when accessing a static field.
+ print(A.bar);
+ print(A.bar());
+ // Check when calling a static method.
+ print(A.foo());
+ print(A.foo);
+ // Check when using a constructor.
+ print(new A());
+ // Check when using a named constructor.
+ print(new A.z());
+ // Check when using a type annotation.
+ A a = new A();
+ // Check when using a prefix.
+ print(prefix.double.NAN);
+ print(prefix.double.NAN());
+ print(prefix.double.parse(''));
+ print(prefix.double.parse);
+ print(new prefix.DateTime(0));
+ print(new prefix.DateTime.utc(0));
+ prefix.DateTime c = new prefix.DateTime(0);
+}
+""";
+
+const String HAS_RUNTIME_TYPE_1 = r"""
+class A {
+}
+main() {
+ print(A);
+}
+""";
+
+const String HAS_RUNTIME_TYPE_2 = r"""
+class A {
+}
+main() {
+ print(2 + A);
+}
+""";
+
+const String HAS_RUNTIME_TYPE_3 = r"""
+class A {
+}
+main() {
+ print(A[0]);
+}
+""";
+
+const String HAS_RUNTIME_TYPE_4 = r"""
+class A {
+}
+main() {
+ var c = A;
+}
+""";
+
+const String HAS_RUNTIME_TYPE_5 = r"""
+import 'dart:core' as prefix;
+main() {
+ prefix.print(prefix.Object);
+}
+""";
+
+const String HAS_RUNTIME_TYPE_6 = r"""
+class A {
+ static var foo;
+}
+main() {
+ (A).foo;
+}
+""";
+
+void test(String code, void check(Compiler compiler)) {
+ Uri uri = new Uri.fromComponents(scheme: 'source');
+ var compiler = compilerFor(code, uri);
+ compiler.runCompiler(uri);
+ check(compiler);
+}
+
+void testHasRuntimeType(String code) {
+ test(code, (compiler) {
+ var element = compiler.findHelper(buildSourceString('createRuntimeType'));
+ Expect.isTrue(compiler.enqueuer.resolution.isProcessed(element));
+ });
+}
+
+main() {
+ test(NO_RUNTIME_TYPE, (compiler) {
+ var element = compiler.findHelper(buildSourceString('createRuntimeType'));
+ Expect.isFalse(compiler.enqueuer.resolution.isProcessed(element));
+ });
+
+ testHasRuntimeType(HAS_RUNTIME_TYPE_1);
+ testHasRuntimeType(HAS_RUNTIME_TYPE_2);
+ testHasRuntimeType(HAS_RUNTIME_TYPE_3);
+ testHasRuntimeType(HAS_RUNTIME_TYPE_4);
+ testHasRuntimeType(HAS_RUNTIME_TYPE_5);
+ testHasRuntimeType(HAS_RUNTIME_TYPE_6);
+}
diff --git a/tests/compiler/dart2js/resolver_test.dart b/tests/compiler/dart2js/resolver_test.dart
index a8b08ed..e1cbda1 100644
--- a/tests/compiler/dart2js/resolver_test.dart
+++ b/tests/compiler/dart2js/resolver_test.dart
@@ -3,18 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:collection';
-import 'dart:uri';
import "../../../sdk/lib/_internal/compiler/implementation/resolution/resolution.dart";
-import "../../../sdk/lib/_internal/compiler/implementation/elements/elements.dart";
-import "../../../sdk/lib/_internal/compiler/implementation/tree/tree.dart";
-import "../../../sdk/lib/_internal/compiler/implementation/util/util.dart";
import "compiler_helper.dart";
-import "mock_compiler.dart";
import "parser_helper.dart";
-import "../../../sdk/lib/_internal/compiler/implementation/dart2jslib.dart"
- hide TreeElementMapping, TreeElements, SourceString;
import '../../../sdk/lib/_internal/compiler/implementation/dart_types.dart';
Node buildIdentifier(String name) => new Identifier(scan(name));
diff --git a/tests/compiler/dart2js/size_test.dart b/tests/compiler/dart2js/size_test.dart
index d25c4b1..9b39765 100644
--- a/tests/compiler/dart2js/size_test.dart
+++ b/tests/compiler/dart2js/size_test.dart
@@ -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.
-import "mock_compiler.dart";
import "compiler_helper.dart";
const String TEST = "main() => [];";
@@ -26,5 +25,5 @@
var backend = compiler.backend;
// Make sure no class is emitted.
- Expect.isFalse(generated.contains(backend.emitter.defineClassName));
+ Expect.isFalse(generated.contains(backend.emitter.finishClassesName));
}
diff --git a/tests/compiler/dart2js/subtype_test.dart b/tests/compiler/dart2js/subtype_test.dart
new file mode 100644
index 0000000..daded9a
--- /dev/null
+++ b/tests/compiler/dart2js/subtype_test.dart
@@ -0,0 +1,218 @@
+// 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 subtype_test;
+
+import 'type_test_helper.dart';
+import '../../../sdk/lib/_internal/compiler/implementation/dart_types.dart';
+import "../../../sdk/lib/_internal/compiler/implementation/elements/elements.dart"
+ show Element, ClassElement;
+
+void main() {
+ testInterfaceSubtype();
+}
+
+void testInterfaceSubtype() {
+ var env = new TypeEnvironment(r"""
+ class A<T> {}
+ class B<T1,T2> extends A<T1> {}
+ // TODO(johnniwinther): Inheritance with different type arguments is
+ // currently not supported by the implementation.
+ class C<T1,T2> extends B<T2,T1> /*implements A<A<T1>>*/ {}
+ """);
+
+ void expect(bool value, DartType T, DartType S) {
+ Expect.equals(value, env.isSubtype(T, S), '$T <: $S');
+ }
+
+ ClassElement A = env.getElement('A');
+ ClassElement B = env.getElement('B');
+ ClassElement C = env.getElement('C');
+ DartType Object_ = env['Object'];
+ DartType num_ = env['num'];
+ DartType int_ = env['int'];
+ DartType String_ = env['String'];
+ DartType dynamic_ = env['dynamic'];
+
+ expect(true, Object_, Object_);
+ expect(true, num_, Object_);
+ expect(true, int_, Object_);
+ expect(true, String_, Object_);
+ expect(true, dynamic_, Object_);
+
+ expect(false, Object_, num_);
+ expect(true, num_, num_);
+ expect(true, int_, num_);
+ expect(false, String_, num_);
+ expect(true, dynamic_, num_);
+
+ expect(false, Object_, int_);
+ expect(false, num_, int_);
+ expect(true, int_, int_);
+ expect(false, String_, int_);
+ expect(true, dynamic_, int_);
+
+ expect(false, Object_, String_);
+ expect(false, num_, String_);
+ expect(false, int_, String_);
+ expect(true, String_, String_);
+ expect(true, dynamic_, String_);
+
+ expect(true, Object_, dynamic_);
+ expect(true, num_, dynamic_);
+ expect(true, int_, dynamic_);
+ expect(true, String_, dynamic_);
+ expect(true, dynamic_, dynamic_);
+
+ DartType A_Object = instantiate(A, [Object_]);
+ DartType A_num = instantiate(A, [num_]);
+ DartType A_int = instantiate(A, [int_]);
+ DartType A_String = instantiate(A, [String_]);
+ DartType A_dynamic = instantiate(A, [dynamic_]);
+
+ expect(true, A_Object, Object_);
+ expect(false, A_Object, num_);
+ expect(false, A_Object, int_);
+ expect(false, A_Object, String_);
+ expect(true, A_Object, dynamic_);
+
+ expect(true, A_Object, A_Object);
+ expect(true, A_num, A_Object);
+ expect(true, A_int, A_Object);
+ expect(true, A_String, A_Object);
+ expect(true, A_dynamic, A_Object);
+
+ expect(false, A_Object, A_num);
+ expect(true, A_num, A_num);
+ expect(true, A_int, A_num);
+ expect(false, A_String, A_num);
+ expect(true, A_dynamic, A_num);
+
+ expect(false, A_Object, A_int);
+ expect(false, A_num, A_int);
+ expect(true, A_int, A_int);
+ expect(false, A_String, A_int);
+ expect(true, A_dynamic, A_int);
+
+ expect(false, A_Object, A_String);
+ expect(false, A_num, A_String);
+ expect(false, A_int, A_String);
+ expect(true, A_String, A_String);
+ expect(true, A_dynamic, A_String);
+
+ expect(true, A_Object, A_dynamic);
+ expect(true, A_num, A_dynamic);
+ expect(true, A_int, A_dynamic);
+ expect(true, A_String, A_dynamic);
+ expect(true, A_dynamic, A_dynamic);
+
+ DartType B_Object_Object = instantiate(B, [Object_, Object_]);
+ DartType B_num_num = instantiate(B, [num_, num_]);
+ DartType B_int_num = instantiate(B, [int_, num_]);
+ DartType B_dynamic_dynamic = instantiate(B, [dynamic_, dynamic_]);
+ DartType B_String_dynamic = instantiate(B, [String_, dynamic_]);
+
+ expect(true, B_Object_Object, Object_);
+ expect(true, B_Object_Object, A_Object);
+ expect(false, B_Object_Object, A_num);
+ expect(false, B_Object_Object, A_int);
+ expect(false, B_Object_Object, A_String);
+ expect(true, B_Object_Object, A_dynamic);
+
+ expect(true, B_num_num, Object_);
+ expect(true, B_num_num, A_Object);
+ expect(true, B_num_num, A_num);
+ expect(false, B_num_num, A_int);
+ expect(false, B_num_num, A_String);
+ expect(true, B_num_num, A_dynamic);
+
+ expect(true, B_int_num, Object_);
+ expect(true, B_int_num, A_Object);
+ expect(true, B_int_num, A_num);
+ expect(true, B_int_num, A_int);
+ expect(false, B_int_num, A_String);
+ expect(true, B_int_num, A_dynamic);
+
+ expect(true, B_dynamic_dynamic, Object_);
+ expect(true, B_dynamic_dynamic, A_Object);
+ expect(true, B_dynamic_dynamic, A_num);
+ expect(true, B_dynamic_dynamic, A_int);
+ expect(true, B_dynamic_dynamic, A_String);
+ expect(true, B_dynamic_dynamic, A_dynamic);
+
+ expect(true, B_String_dynamic, Object_);
+ expect(true, B_String_dynamic, A_Object);
+ expect(false, B_String_dynamic, A_num);
+ expect(false, B_String_dynamic, A_int);
+ expect(true, B_String_dynamic, A_String);
+ expect(true, B_String_dynamic, A_dynamic);
+
+ expect(true, B_Object_Object, B_Object_Object);
+ expect(true, B_num_num, B_Object_Object);
+ expect(true, B_int_num, B_Object_Object);
+ expect(true, B_dynamic_dynamic, B_Object_Object);
+ expect(true, B_String_dynamic, B_Object_Object);
+
+ expect(false, B_Object_Object, B_num_num);
+ expect(true, B_num_num, B_num_num);
+ expect(true, B_int_num, B_num_num);
+ expect(true, B_dynamic_dynamic, B_num_num);
+ expect(false, B_String_dynamic, B_num_num);
+
+ expect(false, B_Object_Object, B_int_num);
+ expect(false, B_num_num, B_int_num);
+ expect(true, B_int_num, B_int_num);
+ expect(true, B_dynamic_dynamic, B_int_num);
+ expect(false, B_String_dynamic, B_int_num);
+
+ expect(true, B_Object_Object, B_dynamic_dynamic);
+ expect(true, B_num_num, B_dynamic_dynamic);
+ expect(true, B_int_num, B_dynamic_dynamic);
+ expect(true, B_dynamic_dynamic, B_dynamic_dynamic);
+ expect(true, B_String_dynamic, B_dynamic_dynamic);
+
+ expect(false, B_Object_Object, B_String_dynamic);
+ expect(false, B_num_num, B_String_dynamic);
+ expect(false, B_int_num, B_String_dynamic);
+ expect(true, B_dynamic_dynamic, B_String_dynamic);
+ expect(true, B_String_dynamic, B_String_dynamic);
+
+ DartType C_Object_Object = instantiate(C, [Object_, Object_]);
+ DartType C_num_num = instantiate(C, [num_, num_]);
+ DartType C_int_String = instantiate(C, [int_, String_]);
+ DartType C_dynamic_dynamic = instantiate(C, [dynamic_, dynamic_]);
+
+ expect(true, C_Object_Object, B_Object_Object);
+ expect(false, C_Object_Object, B_num_num);
+ expect(false, C_Object_Object, B_int_num);
+ expect(true, C_Object_Object, B_dynamic_dynamic);
+ expect(false, C_Object_Object, B_String_dynamic);
+
+ expect(true, C_num_num, B_Object_Object);
+ expect(true, C_num_num, B_num_num);
+ expect(false, C_num_num, B_int_num);
+ expect(true, C_num_num, B_dynamic_dynamic);
+ expect(false, C_num_num, B_String_dynamic);
+
+ expect(true, C_int_String, B_Object_Object);
+ expect(false, C_int_String, B_num_num);
+ expect(false, C_int_String, B_int_num);
+ expect(true, C_int_String, B_dynamic_dynamic);
+ expect(true, C_int_String, B_String_dynamic);
+
+ expect(true, C_dynamic_dynamic, B_Object_Object);
+ expect(true, C_dynamic_dynamic, B_num_num);
+ expect(true, C_dynamic_dynamic, B_int_num);
+ expect(true, C_dynamic_dynamic, B_dynamic_dynamic);
+ expect(true, C_dynamic_dynamic, B_String_dynamic);
+
+ expect(false, C_int_String, A_int);
+ expect(true, C_int_String, A_String);
+ // TODO(johnniwinther): Inheritance with different type arguments is
+ // currently not supported by the implementation.
+ //expect(true, C_int_String, instantiate(A, [A_int]));
+ expect(false, C_int_String, instantiate(A, [A_String]));
+}
+
+
diff --git a/tests/compiler/dart2js/tag_mapping_test.dart b/tests/compiler/dart2js/tag_mapping_test.dart
new file mode 100644
index 0000000..c9b5a82
--- /dev/null
+++ b/tests/compiler/dart2js/tag_mapping_test.dart
@@ -0,0 +1,39 @@
+// 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.
+
+// Test of import tag to library mapping.
+
+import 'compiler_helper.dart';
+
+const MAIN_CODE = """
+import 'library.dart';
+
+main() {
+}
+""";
+
+const LIB_CODE = """
+library lib;
+""";
+
+void main() {
+ var sources = <String, String>{
+ 'main.dart': MAIN_CODE,
+ 'library.dart': LIB_CODE,
+ };
+
+ compileSources(sources, (MockCompiler compiler) {
+ LibraryElement mainApp = compiler.libraries['source:/main.dart'];
+ LibraryElement lib = compiler.libraries['source:/library.dart'];
+ Expect.isNotNull(mainApp, 'Could not find main.dart library');
+ Expect.isNotNull(lib, 'Could not find library.dart library');
+
+ Import tag = mainApp.tags.single;
+ Expect.isNotNull(tag, 'Could not find import tag in $mainApp');
+
+ // Test that we can get from the import tag in main.dart to the
+ // library element representing library.dart.
+ Expect.identical(lib, mainApp.getLibraryFromTag(tag));
+ });
+}
diff --git a/tests/compiler/dart2js/type_checker_test.dart b/tests/compiler/dart2js/type_checker_test.dart
index f69acf9..39e688a 100644
--- a/tests/compiler/dart2js/type_checker_test.dart
+++ b/tests/compiler/dart2js/type_checker_test.dart
@@ -266,7 +266,14 @@
void testMethodInvocations() {
compiler.parseScript(CLASS_WITH_METHODS);
- final String header = "{ ClassWithMethods c; SubClass d; int i; int j; ";
+ final String header = """{
+ ClassWithMethods c;
+ SubClass d;
+ var e;
+ int i;
+ int j;
+ int localMethod(String str) { return 0; }
+ """;
analyze("${header}int k = c.untypedNoArgumentMethod(); }");
analyze("${header}ClassWithMethods x = c.untypedNoArgumentMethod(); }");
@@ -306,8 +313,71 @@
analyze("${header}ClassWithMethods x = c.intTwoArgumentMethod(i, j); }",
MessageKind.NOT_ASSIGNABLE);
- analyze("${header}c.intField(); }", MessageKind.METHOD_NOT_FOUND);
- analyze("${header}d.intField(); }", MessageKind.METHOD_NOT_FOUND);
+ analyze("${header}c.functionField(); }");
+ analyze("${header}d.functionField(); }");
+ analyze("${header}c.functionField(1); }");
+ analyze("${header}d.functionField('string'); }");
+
+ analyze("${header}c.intField(); }", MessageKind.NOT_CALLABLE);
+ analyze("${header}d.intField(); }", MessageKind.NOT_CALLABLE);
+
+ analyze("${header}c.untypedField(); }");
+ analyze("${header}d.untypedField(); }");
+ analyze("${header}c.untypedField(1); }");
+ analyze("${header}d.untypedField('string'); }");
+
+ // Invocation of dynamic variable.
+ analyze("${header}e(); }");
+ analyze("${header}e(1); }");
+ analyze("${header}e('string'); }");
+
+ // Invocation on local method.
+ analyze("${header}localMethod(); }", MessageKind.MISSING_ARGUMENT);
+ analyze("${header}localMethod(1); }", MessageKind.NOT_ASSIGNABLE);
+ analyze("${header}localMethod('string'); }");
+ analyze("${header}int k = localMethod('string'); }");
+ analyze("${header}String k = localMethod('string'); }",
+ MessageKind.NOT_ASSIGNABLE);
+
+ // Invocation on parenthesized expressions.
+ analyze("${header}(e)(); }");
+ analyze("${header}(e)(1); }");
+ analyze("${header}(e)('string'); }");
+ analyze("${header}(foo)(); }");
+ analyze("${header}(foo)(1); }");
+ analyze("${header}(foo)('string'); }");
+
+ // Invocations on function expressions.
+ analyze("${header}(foo){}(); }", MessageKind.MISSING_ARGUMENT);
+ analyze("${header}(foo){}(1); }");
+ analyze("${header}(foo){}('string'); }");
+ analyze("${header}(int foo){}('string'); }", MessageKind.NOT_ASSIGNABLE);
+ analyze("${header}(String foo){}('string'); }");
+ analyze("${header}int k = int bar(String foo){ return 0; }('string'); }");
+ analyze("${header}int k = String bar(String foo){ return foo; }('string'); }",
+ MessageKind.NOT_ASSIGNABLE);
+
+ // Static invocations.
+ analyze("${header}ClassWithMethods.staticMethod(); }",
+ MessageKind.MISSING_ARGUMENT);
+ analyze("${header}ClassWithMethods.staticMethod(1); }",
+ MessageKind.NOT_ASSIGNABLE);
+ analyze("${header}ClassWithMethods.staticMethod('string'); }");
+ analyze("${header}int k = ClassWithMethods.staticMethod('string'); }");
+ analyze("${header}String k = ClassWithMethods.staticMethod('string'); }",
+ MessageKind.NOT_ASSIGNABLE);
+
+ // Invocation on dynamic variable.
+ analyze("${header}e.foo(); }");
+ analyze("${header}e.foo(1); }");
+ analyze("${header}e.foo('string'); }");
+
+ // Invocation on unresolved variable.
+ analyze("${header}foo(); }");
+ analyze("${header}foo(1); }");
+ analyze("${header}foo('string'); }");
+
+ // TODO(johnniwinther): Add tests of invocations using implicit this.
}
/** Tests analysis of returns (not required by the specification). */
@@ -589,6 +659,8 @@
Function functionField;
var untypedField;
int intField;
+
+ static int staticMethod(String str) {}
}
interface I {
int intMethod();
diff --git a/tests/compiler/dart2js/type_equals_test.dart b/tests/compiler/dart2js/type_equals_test.dart
index 57d468b..4da86a6 100644
--- a/tests/compiler/dart2js/type_equals_test.dart
+++ b/tests/compiler/dart2js/type_equals_test.dart
@@ -2,14 +2,9 @@
// 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 "../../../sdk/lib/_internal/compiler/implementation/dart2jslib.dart";
import '../../../sdk/lib/_internal/compiler/implementation/dart_types.dart';
-import "../../../sdk/lib/_internal/compiler/implementation/elements/elements.dart";
-import "../../../sdk/lib/_internal/compiler/implementation/tree/tree.dart";
-import "../../../sdk/lib/_internal/compiler/implementation/util/util.dart";
import "compiler_helper.dart";
import "parser_helper.dart";
-import "dart:uri";
bool test(compiler, String name1, String name2, {bool expect}) {
Expect.isTrue(?expect, 'required parameter "expect" not given');
diff --git a/tests/compiler/dart2js/type_substitution_test.dart b/tests/compiler/dart2js/type_substitution_test.dart
index 0c4ee89..e54b628 100644
--- a/tests/compiler/dart2js/type_substitution_test.dart
+++ b/tests/compiler/dart2js/type_substitution_test.dart
@@ -4,27 +4,10 @@
library type_substitution_test;
-import "../../../sdk/lib/_internal/compiler/implementation/dart2jslib.dart";
import '../../../sdk/lib/_internal/compiler/implementation/dart_types.dart';
-import "../../../sdk/lib/_internal/compiler/implementation/elements/elements.dart";
-import "../../../sdk/lib/_internal/compiler/implementation/tree/tree.dart";
-import "../../../sdk/lib/_internal/compiler/implementation/util/util.dart";
import "compiler_helper.dart";
import "parser_helper.dart";
-import "dart:uri";
-
-Element getElement(compiler, String name) {
- var element = findElement(compiler, name);
- Expect.isNotNull(element);
- if (identical(element.kind, ElementKind.CLASS)) {
- element.ensureResolved(compiler);
- }
- return element;
-}
-
-DartType getElementType(compiler, String name) {
- return getElement(compiler, name).computeType(compiler);
-}
+import "type_test_helper.dart";
DartType getType(compiler, String name) {
var clazz = findElement(compiler, "Class");
@@ -63,46 +46,39 @@
testTypeSubstitution();
}
-InterfaceType instantiate(ClassElement element, List<DartType> arguments) {
- return new InterfaceType(element, new Link<DartType>.fromList(arguments));
-}
-
void testAsInstanceOf() {
- var uri = new Uri.fromComponents(scheme: 'source');
- Compiler compiler = compilerFor('''
- main() {}
+ var env = new TypeEnvironment('''
class A<T> {}
class B<T> {}
class C<T> extends A<T> {}
class D<T> extends A<int> {}
class E<T> extends A<A<T>> {}
- class F<T, U> extends B<F<T, String>> implements A<F<B<U>, int>> {}''',
- uri);
- compiler.runCompiler(uri);
+ class F<T, U> extends B<F<T, String>> implements A<F<B<U>, int>> {}''');
+ var compiler = env.compiler;
- ClassElement A = getElement(compiler, "A");
- ClassElement B = getElement(compiler, "B");
- ClassElement C = getElement(compiler, "C");
- ClassElement D = getElement(compiler, "D");
- ClassElement E = getElement(compiler, "E");
- ClassElement F = getElement(compiler, "F");
+ ClassElement A = env.getElement("A");
+ ClassElement B = env.getElement("B");
+ ClassElement C = env.getElement("C");
+ ClassElement D = env.getElement("D");
+ ClassElement E = env.getElement("E");
+ ClassElement F = env.getElement("F");
- DartType numType = compiler.numClass.computeType(compiler);
- DartType intType = compiler.intClass.computeType(compiler);
- DartType stringType = compiler.stringClass.computeType(compiler);
+ DartType numType = env['num'];
+ DartType intType = env['int'];
+ DartType stringType = env['String'];
- DartType C_int = instantiate(C, [intType]);
+ InterfaceType C_int = instantiate(C, [intType]);
Expect.equals(instantiate(C, [intType]), C_int);
Expect.equals(instantiate(A, [intType]), C_int.asInstanceOf(A));
- DartType D_int = instantiate(D, [stringType]);
+ InterfaceType D_int = instantiate(D, [stringType]);
Expect.equals(instantiate(A, [intType]), D_int.asInstanceOf(A));
- DartType E_int = instantiate(E, [intType]);
+ InterfaceType E_int = instantiate(E, [intType]);
Expect.equals(instantiate(A, [instantiate(A, [intType])]),
E_int.asInstanceOf(A));
- DartType F_int_string = instantiate(F, [intType, stringType]);
+ InterfaceType F_int_string = instantiate(F, [intType, stringType]);
Expect.equals(instantiate(B, [instantiate(F, [intType, stringType])]),
F_int_string.asInstanceOf(B));
Expect.equals(instantiate(A, [instantiate(F, [instantiate(B, [stringType]),
@@ -124,9 +100,7 @@
}
void testTypeSubstitution() {
- var uri = new Uri.fromComponents(scheme: 'source');
- var compiler = compilerFor(
- r"""
+ var env = new TypeEnvironment(r"""
typedef void Typedef1<X,Y>(X x1, Y y2);
typedef void Typedef2<Z>(Z z1);
@@ -178,13 +152,10 @@
void Typedef1e(Typedef2<S> a) {}
void Typedef2e(Typedef2<String> b) {}
}
+ """);
+ var compiler = env.compiler;
- void main() {}
- """,
- uri);
- compiler.runCompiler(uri);
-
- DartType Class_T_S = getElementType(compiler, "Class");
+ InterfaceType Class_T_S = env["Class"];
Expect.isNotNull(Class_T_S);
Expect.identical(Class_T_S.kind, TypeKind.INTERFACE);
Expect.equals(2, length(Class_T_S.typeArguments));
@@ -197,11 +168,11 @@
Expect.isNotNull(S);
Expect.identical(S.kind, TypeKind.TYPE_VARIABLE);
- DartType intType = getType(compiler, "int1");
+ DartType intType = env['int'];//getType(compiler, "int1");
Expect.isNotNull(intType);
Expect.identical(intType.kind, TypeKind.INTERFACE);
- DartType StringType = getType(compiler, "String1");
+ DartType StringType = env['String'];//getType(compiler, "String1");
Expect.isNotNull(StringType);
Expect.identical(StringType.kind, TypeKind.INTERFACE);
diff --git a/tests/compiler/dart2js/type_test_helper.dart b/tests/compiler/dart2js/type_test_helper.dart
new file mode 100644
index 0000000..94faace
--- /dev/null
+++ b/tests/compiler/dart2js/type_test_helper.dart
@@ -0,0 +1,50 @@
+// 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 type_test_helper;
+
+import '../../../sdk/lib/_internal/compiler/implementation/dart_types.dart';
+import "compiler_helper.dart";
+
+InterfaceType instantiate(ClassElement element, List<DartType> arguments) {
+ return new InterfaceType(element, new Link<DartType>.fromList(arguments));
+}
+
+class TypeEnvironment {
+ final MockCompiler compiler;
+
+ factory TypeEnvironment(String source) {
+ var uri = new Uri.fromComponents(scheme: 'source');
+ MockCompiler compiler = compilerFor('''
+ main() {}
+ $source''',
+ uri);
+ compiler.runCompiler(uri);
+ return new TypeEnvironment._(compiler);
+ }
+
+ TypeEnvironment._(MockCompiler this.compiler);
+
+ Element getElement(String name) {
+ var element = findElement(compiler, name);
+ Expect.isNotNull(element);
+ if (identical(element.kind, ElementKind.CLASS)) {
+ element.ensureResolved(compiler);
+ }
+ return element;
+ }
+
+ DartType getElementType(String name) {
+ return getElement(name).computeType(compiler);
+ }
+
+ DartType operator[] (String name) {
+ if (name == 'dynamic') return compiler.types.dynamicType;
+ return getElementType(name);
+ }
+
+ bool isSubtype(DartType T, DartType S) {
+ return compiler.types.isSubtype(T, S);
+ }
+}
diff --git a/tests/compiler/dart2js_extra/dart2js_extra.status b/tests/compiler/dart2js_extra/dart2js_extra.status
index ddf9a3c..1660de8d 100644
--- a/tests/compiler/dart2js_extra/dart2js_extra.status
+++ b/tests/compiler/dart2js_extra/dart2js_extra.status
@@ -8,6 +8,8 @@
typed_locals_test: Fail
no_such_method_test: Fail # Wrong InvocationMirror.memberName.
+deferred_semantics_test/none: Fail # TODO(ahe): Multitest cannot use import.
+
[ $compiler == dart2js && $checked ]
parameter_bailout_test: Fail, OK
diff --git a/tests/compiler/dart2js_extra/deferred/deferred_class_library.dart b/tests/compiler/dart2js_extra/deferred/deferred_class_library.dart
new file mode 100644
index 0000000..bf9a9dd
--- /dev/null
+++ b/tests/compiler/dart2js_extra/deferred/deferred_class_library.dart
@@ -0,0 +1,14 @@
+// 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.
+
+// Imported by deferred_class_test.dart.
+
+library deferred_class_library;
+
+class MyClass {
+ foo(x) {
+ print('MyClass.foo($x)');
+ return (x - 3) ~/ 2;
+ }
+}
diff --git a/tests/compiler/dart2js_extra/deferred/deferred_class_test.dart b/tests/compiler/dart2js_extra/deferred/deferred_class_test.dart
new file mode 100644
index 0000000..1486e75
--- /dev/null
+++ b/tests/compiler/dart2js_extra/deferred/deferred_class_test.dart
@@ -0,0 +1,38 @@
+// 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 'dart:async';
+
+@lazy import 'deferred_class_library.dart';
+
+const lazy = const DeferredLibrary('deferred_class_library');
+
+isNoSuchMethodError(e) => e is NoSuchMethodError;
+
+main() {
+ var x;
+ Expect.throws(() { x = new MyClass(); }, isNoSuchMethodError);
+ Expect.isNull(x);
+ int counter = 0;
+ lazy.load().then((bool didLoad) {
+ Expect.isTrue(didLoad);
+ Expect.equals(1, ++counter);
+ print('deferred_class_library was loaded');
+ x = new MyClass();
+ Expect.equals(42, x.foo(87));
+ });
+ Expect.equals(0, counter);
+ Expect.isNull(x);
+ lazy.load().then((bool didLoad) {
+ Expect.isFalse(didLoad);
+ Expect.equals(2, ++counter);
+ print('deferred_class_library was loaded');
+ x = new MyClass();
+ Expect.equals(42, x.foo(87));
+ });
+ Expect.equals(0, counter);
+ Expect.isNull(x);
+ Expect.throws(() { x = new MyClass(); }, isNoSuchMethodError);
+ Expect.isNull(x);
+}
diff --git a/tests/compiler/dart2js_extra/deferred/deferred_function_library.dart b/tests/compiler/dart2js_extra/deferred/deferred_function_library.dart
new file mode 100644
index 0000000..0eaebc7
--- /dev/null
+++ b/tests/compiler/dart2js_extra/deferred/deferred_function_library.dart
@@ -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.
+
+// Imported by deferred_function_test.dart and
+// deferred_semantics_test.dart.
+
+library deferred_function_library;
+
+foo(x) {
+ print('foo($x)');
+ return 42;
+}
diff --git a/tests/compiler/dart2js_extra/deferred/deferred_function_test.dart b/tests/compiler/dart2js_extra/deferred/deferred_function_test.dart
new file mode 100644
index 0000000..08eb6ac
--- /dev/null
+++ b/tests/compiler/dart2js_extra/deferred/deferred_function_test.dart
@@ -0,0 +1,36 @@
+// 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.
+
+// Test that loading of a library (with top-level functions only) can
+// be deferred.
+
+import 'dart:async';
+@lazy import 'deferred_function_library.dart';
+
+const lazy = const DeferredLibrary('deferred_function_library');
+
+isNoSuchMethodError(e) => e is NoSuchMethodError;
+
+main() {
+ print('unittest-suite-wait-for-done');
+
+ Expect.throws(() { foo('a'); }, isNoSuchMethodError);
+ int counter = 0;
+ lazy.load().then((bool didLoad) {
+ Expect.isTrue(didLoad);
+ Expect.equals(1, ++counter);
+ print('lazy was loaded');
+ Expect.equals(42, foo('b'));
+ });
+ Expect.equals(0, counter);
+ lazy.load().then((bool didLoad) {
+ Expect.isFalse(didLoad);
+ Expect.equals(2, ++counter);
+ print('lazy was loaded');
+ Expect.equals(42, foo('b'));
+ print('unittest-suite-success');
+ });
+ Expect.equals(0, counter);
+ Expect.throws(() { foo('a'); }, isNoSuchMethodError);
+}
diff --git a/tests/compiler/dart2js_extra/deferred/deferred_semantics_test.dart b/tests/compiler/dart2js_extra/deferred/deferred_semantics_test.dart
new file mode 100644
index 0000000..140faca
--- /dev/null
+++ b/tests/compiler/dart2js_extra/deferred/deferred_semantics_test.dart
@@ -0,0 +1,15 @@
+// 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.
+
+// Test that deferred loading requires same library names.
+
+import 'dart:async';
+
+@lazy /// 01: compile-time error
+import 'deferred_function_library.dart';
+
+const lazy = const DeferredLibrary('fisk');
+
+main() {
+}
diff --git a/tests/compiler/dart2js_extra/runtime_type_int_test.dart b/tests/compiler/dart2js_extra/runtime_type_int_test.dart
new file mode 100644
index 0000000..efb3861
--- /dev/null
+++ b/tests/compiler/dart2js_extra/runtime_type_int_test.dart
@@ -0,0 +1,19 @@
+// 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.
+
+// Test that in dart2js, the constant system matches the runtime
+// handling of numbers.
+
+main() {
+ var x = 10000000000000000;
+ var y = [x][0];
+ var a = x.runtimeType;
+ var b = y.runtimeType;
+
+ Expect.equals(x, y);
+ Expect.isTrue(x is int);
+ Expect.isTrue(y is int);
+ Expect.equals(x.runtimeType, int);
+ Expect.equals(y.runtimeType, int);
+}
diff --git a/tests/compiler/dart2js_native/compute_this_script_test.dart b/tests/compiler/dart2js_native/compute_this_script_test.dart
new file mode 100644
index 0000000..55092b4
--- /dev/null
+++ b/tests/compiler/dart2js_native/compute_this_script_test.dart
@@ -0,0 +1,18 @@
+// 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.
+
+// Test of IsolateNatives.computeThisScript().
+
+import 'dart:_isolate_helper';
+
+main() {
+ String script = IsolateNatives.computeThisScript();
+
+ // This is somewhat brittle and relies on an implementation detail
+ // of our test runner, but I can think of no other way to test this.
+ // -- ahe
+ if (!script.endsWith('/out.js')) {
+ throw 'Unexpected script: "$script"';
+ }
+}
diff --git a/tests/compiler/dart2js_native/undefined_bailout_test.dart b/tests/compiler/dart2js_native/undefined_bailout_test.dart
new file mode 100644
index 0000000..d55fe7d
--- /dev/null
+++ b/tests/compiler/dart2js_native/undefined_bailout_test.dart
@@ -0,0 +1,38 @@
+// 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.
+
+// dart2js regression test where the SSA backend would use the wrong
+// type for an instruction: when doing a speculative type propagation,
+// if an instruction gets analyzed multiple times, we used to save the
+// last inferred type, which could be speculative, instead of the
+// first inferred type, which is the actual, non-speculative, type.
+//
+// Because we are doing speculative type propagation at the very end,
+// before emitting the bailout version, the only place where we might
+// do wrong optimizations is during the codegen. It turns out that the
+// codegen optimizes '==' comparisons on null to '===' in case it knows
+// the receiver cannot be null. So in the following example, the
+// [:e == null:] comparison in [foo] got wrongly generated to a
+// JavaScript identity check (that is, using '==='). But this
+// comparison does not work for undefined, which the DOM sometimes
+// returns.
+
+import 'dart:_foreign_helper' show JS;
+
+var a = 42;
+var b = 0;
+
+foo(e) {
+ // Loop to force a bailout.
+ for (int i = 0; i < b; i++) {
+ a = e[i]; // Will desire a readable primitive.
+ }
+ // Our heuristics for '==' on an indexable primitive is that it's
+ // more common to do it on string, so we'll want e to be a string.
+ return a == e || e == null;
+}
+
+main() {
+ Expect.isTrue(foo(JS('', 'void 0')));
+}
diff --git a/tests/corelib/corelib.status b/tests/corelib/corelib.status
index 0e5f59b..8672c1c 100644
--- a/tests/corelib/corelib.status
+++ b/tests/corelib/corelib.status
@@ -47,7 +47,6 @@
*: Fail, Pass # TODO(ahe): Triage these tests.
[ $compiler == dart2js && $runtime == safari ]
-null_nosuchmethod_test: Fail # Issue: 7414
core_runtime_types_test: Fail
[ $compiler == dart2js && $runtime == ie9 ]
diff --git a/tests/html/crypto_test.dart b/tests/html/crypto_test.dart
new file mode 100644
index 0000000..0e06723
--- /dev/null
+++ b/tests/html/crypto_test.dart
@@ -0,0 +1,36 @@
+// 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 crypto_test;
+import '../../pkg/unittest/lib/unittest.dart';
+import '../../pkg/unittest/lib/html_config.dart';
+import 'dart:html';
+
+main() {
+ useHtmlConfiguration();
+
+ test('exists', () {
+ var crypto = window.crypto;
+ expect(crypto is Crypto, isTrue);
+ });
+
+ test('successful call', () {
+ var crypto = window.crypto;
+ var data = new Uint8Array(100);
+ expect(data.every((e) => e == 0), isTrue);
+ crypto.getRandomValues(data);
+ // In theory this is flaky. However, in practice you will get 100 zeroes
+ // in a row from a cryptographically secure random number generator so
+ // rarely that we don't have to worry about it.
+ expect(data.any((e) => e != 0), isTrue);
+ });
+
+ test('type mismatch', () {
+ var crypto = window.crypto;
+ var data = new Float32Array(100);
+ expect(() {
+ crypto.getRandomValues(data);
+ }, throws, reason: 'Only typed array views with integer types allowed');
+ });
+}
diff --git a/tests/html/dromaeo_smoke-html.html b/tests/html/dromaeo_smoke-html.html
index 96d65b4..1cbeca4 100644
--- a/tests/html/dromaeo_smoke-html.html
+++ b/tests/html/dromaeo_smoke-html.html
@@ -4,8 +4,8 @@
<html>
<head>
<script type="application/dart" src="dromaeo_noop/dromaeo_smoke.dart"></script>
-<script src='../../pkg/browser/lib/dart.js'></script>
-<script src='../../samples/third_party/dromaeo/htmlrunner.js'></script>
+<script src='/root_dart/pkg/browser/lib/dart.js'></script>
+<script src='/root_dart/samples/third_party/dromaeo/htmlrunner.js'></script>
</head>
<body>
</body>
diff --git a/tests/html/dromaeo_smoke_test.dart b/tests/html/dromaeo_smoke_test.dart
index 686fe85..0463eff 100644
--- a/tests/html/dromaeo_smoke_test.dart
+++ b/tests/html/dromaeo_smoke_test.dart
@@ -24,7 +24,7 @@
useHtmlConfiguration();
var scriptSrc = new ScriptElement();
- scriptSrc.src = '../../../../pkg/browser/lib/dart.js';
+ scriptSrc.src = '/root_dart/pkg/browser/lib/dart.js';
document.head.children.add(scriptSrc);
document.body.innerHtml = '''${document.body.innerHtml}
<div id="main">
diff --git a/tests/html/element_classes_test.dart b/tests/html/element_classes_test.dart
index 966e5eb..65fc38f 100644
--- a/tests/html/element_classes_test.dart
+++ b/tests/html/element_classes_test.dart
@@ -5,6 +5,7 @@
library ElementTest;
import '../../pkg/unittest/lib/unittest.dart';
import '../../pkg/unittest/lib/html_config.dart';
+import 'dart:collection';
import 'dart:html';
main() {
@@ -22,31 +23,31 @@
Set<String> extractClasses(Element el) {
final match = new RegExp('class="([^"]+)"').firstMatch(el.outerHtml);
- return new Set.from(match[1].split(' '));
+ return new LinkedHashSet.from(match[1].split(' '));
}
test('affects the "class" attribute', () {
final el = makeElementWithClasses();
el.classes.add('qux');
- expect(extractClasses(el), unorderedEquals(['foo', 'bar', 'baz', 'qux']));
+ expect(extractClasses(el), orderedEquals(['foo', 'bar', 'baz', 'qux']));
});
test('is affected by the "class" attribute', () {
final el = makeElementWithClasses();
el.attributes['class'] = 'foo qux';
- expect(el.classes, unorderedEquals(['foo', 'qux']));
+ expect(el.classes, orderedEquals(['foo', 'qux']));
});
test('classes=', () {
final el = makeElementWithClasses();
el.classes = ['foo', 'qux'];
- expect(el.classes, unorderedEquals(['foo', 'qux']));
- expect(extractClasses(el), unorderedEquals(['foo', 'qux']));
+ expect(el.classes, orderedEquals(['foo', 'qux']));
+ expect(extractClasses(el), orderedEquals(['foo', 'qux']));
});
test('toString', () {
expect(makeClassSet().toString().split(' '),
- unorderedEquals(['foo', 'bar', 'baz']));
+ orderedEquals(['foo', 'bar', 'baz']));
expect(makeElement().classes.toString(), '');
});
@@ -55,7 +56,7 @@
// TODO: Change to this when Issue 3484 is fixed.
// makeClassSet().forEach(classes.add);
makeClassSet().forEach((c) => classes.add(c));
- expect(classes, unorderedEquals(['foo', 'bar', 'baz']));
+ expect(classes, orderedEquals(['foo', 'bar', 'baz']));
});
test('iterator', () {
@@ -63,17 +64,17 @@
for (var el in makeClassSet()) {
classes.add(el);
}
- expect(classes, unorderedEquals(['foo', 'bar', 'baz']));
+ expect(classes, orderedEquals(['foo', 'bar', 'baz']));
});
test('map', () {
expect(makeClassSet().map((c) => c.toUpperCase()).toList(),
- unorderedEquals(['FOO', 'BAR', 'BAZ']));
+ orderedEquals(['FOO', 'BAR', 'BAZ']));
});
test('where', () {
expect(makeClassSet().where((c) => c.contains('a')).toSet(),
- unorderedEquals(['bar', 'baz']));
+ orderedEquals(['bar', 'baz']));
});
test('every', () {
@@ -104,41 +105,41 @@
test('add', () {
final classes = makeClassSet();
classes.add('qux');
- expect(classes, unorderedEquals(['foo', 'bar', 'baz', 'qux']));
+ expect(classes, orderedEquals(['foo', 'bar', 'baz', 'qux']));
classes.add('qux');
final list = new List.from(classes);
list.sort((a, b) => a.compareTo(b));
- expect(list, unorderedEquals(['bar', 'baz', 'foo', 'qux']),
+ expect(list, orderedEquals(['bar', 'baz', 'foo', 'qux']),
reason: "The class set shouldn't have duplicate elements.");
});
test('remove', () {
final classes = makeClassSet();
classes.remove('bar');
- expect(classes, unorderedEquals(['foo', 'baz']));
+ expect(classes, orderedEquals(['foo', 'baz']));
classes.remove('qux');
- expect(classes, unorderedEquals(['foo', 'baz']));
+ expect(classes, orderedEquals(['foo', 'baz']));
});
test('toggle', () {
final classes = makeClassSet();
classes.toggle('bar');
- expect(classes, unorderedEquals(['foo', 'baz']));
+ expect(classes, orderedEquals(['foo', 'baz']));
classes.toggle('qux');
- expect(classes, unorderedEquals(['foo', 'baz', 'qux']));
+ expect(classes, orderedEquals(['foo', 'baz', 'qux']));
});
test('addAll', () {
final classes = makeClassSet();
classes.addAll(['bar', 'qux', 'bip']);
- expect(classes, unorderedEquals(['foo', 'bar', 'baz', 'qux', 'bip']));
+ expect(classes, orderedEquals(['foo', 'bar', 'baz', 'qux', 'bip']));
});
test('removeAll', () {
final classes = makeClassSet();
classes.removeAll(['bar', 'baz', 'qux']);
- expect(classes, unorderedEquals(['foo']));
+ expect(classes, orderedEquals(['foo']));
});
test('isSubsetOf', () {
@@ -165,4 +166,14 @@
classes.clear();
expect(classes, equals([]));
});
+
+ test('order', () {
+ var classes = makeClassSet();
+ classes.add('aardvark');
+ expect(classes, orderedEquals(['foo', 'bar', 'baz', 'aardvark']));
+ classes.toggle('baz');
+ expect(classes, orderedEquals(['foo', 'bar', 'aardvark']));
+ classes.toggle('baz');
+ expect(classes, orderedEquals(['foo', 'bar', 'aardvark', 'baz']));
+ });
}
diff --git a/tests/html/geolocation_test.dart b/tests/html/geolocation_test.dart
new file mode 100644
index 0000000..e6dd1b3
--- /dev/null
+++ b/tests/html/geolocation_test.dart
@@ -0,0 +1,18 @@
+ // 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 geolocation_test;
+import '../../pkg/unittest/lib/unittest.dart';
+import '../../pkg/unittest/lib/html_config.dart';
+import 'dart:html';
+
+main() {
+ useHtmlConfiguration();
+
+ // Actual tests require browser interaction. This just makes sure the API
+ // is present.
+ test('is not null', () {
+ expect(window.navigator.geolocation, isNotNull);
+ });
+}
diff --git a/tests/html/html.status b/tests/html/html.status
index 0596b92..1f04561 100644
--- a/tests/html/html.status
+++ b/tests/html/html.status
@@ -1,4 +1,4 @@
-# Copyright (c) 2012, 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.
@@ -7,21 +7,25 @@
event_test: Skip # Issue 1996
custom_elements_test: Skip # Not yet implemented.
+interactive_test: Skip # Must be run manually.
# Layout tests are only supported on DRT.
[ $runtime == ie9 || $runtime == ie10 || $runtime == safari || $runtime == ff || $runtime == chrome || $runtime == opera ]
*layout_test: Skip
+[ $runtime == drt || $runtime == dartium ]
+input_element_test/supported_datetime: Fail
+
[ $runtime == chrome ]
-element_types_test/supported_content: Fail
-element_types_test/supported_shadow: Fail
+element_types_test/supported_content: Pass, Fail # Issue 8700
+element_types_test/supported_shadow: Pass, Fail # Issue 8700
input_element_test/supported_date: Pass, Fail # Chrome stable does not support this input type.
input_element_test/supported_datetime: Fail
-input_element_test/supported_datetime-local: Fail
-input_element_test/supported_month: Fail
-input_element_test/supported_week: Fail
+input_element_test/supported_datetime-local: Pass, Fail # Issue 8700
+input_element_test/supported_month: Pass, Fail # Issue 8700
+input_element_test/supported_week: Pass, Fail # Issue 8700
speechrecognition_test/supported: Pass, Fail # Chrome stable does not support it.
-shadow_dom_test/supported: Fail
+shadow_dom_test/supported: Pass, Fail # Issue 8700
speechrecognition_test/types: Pass, Fail
touchevent_test/supported: Fail
@@ -52,6 +56,7 @@
# TODO(efortuna, blois): Triage.
audiobuffersourcenode_test: Fail
audiocontext_test: Fail
+crypto_test: Fail
css_test/supported_CssMatrix: Fail
css_test/supported_DomPoint: Fail
document_test/supports_cssCanvasContext: Fail
@@ -102,8 +107,10 @@
xhr_test/supported_HttpRequestProgressEvent: Fail
xsltprocessor_test/supported: Fail
history_test/history: Pass, Fail # issue 8183
+canvasrenderingcontext2d_test: Fail # issue 8678
[ $runtime == ie9 ]
+crypto_test: Fail
document_test/supports_cssCanvasContext: Fail
dromaeo_smoke_test: Skip #TODO(efortuna): investigating.
element_test/click: Fail # IE does not support firing this event.
@@ -236,6 +243,7 @@
wheelevent_test: Fail # Issue: 7414
[ $runtime == opera ]
+crypto_test: Fail
document_test/supports_cssCanvasContext: Fail
document_test/document: Fail # Issue: 7413
form_data_test: Fail # Issue: 7413
@@ -279,6 +287,7 @@
[ $runtime == ff ]
audiobuffersourcenode_test: Fail # FF only has Audio element.
audiocontext_test: Fail # FF only has Audio element
+crypto_test: Fail
css_test/supported_CssMatrix: Fail
css_test/supported_DomPoint: Fail
dart_object_local_storage_test: Skip # sessionStorage NS_ERROR_DOM_NOT_SUPPORTED_ERR
diff --git a/tests/html/interactive_test.dart b/tests/html/interactive_test.dart
new file mode 100644
index 0000000..f688ee5b
--- /dev/null
+++ b/tests/html/interactive_test.dart
@@ -0,0 +1,65 @@
+ // 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 interactive_test;
+
+import 'dart:async';
+import 'dart:html';
+import '../../pkg/unittest/lib/unittest.dart';
+import '../../pkg/unittest/lib/html_individual_config.dart';
+import 'utils.dart';
+
+
+main() {
+ useHtmlIndividualConfiguration();
+
+ group('Geolocation', () {
+ futureTest('getCurrentPosition', () {
+ return window.navigator.geolocation.getCurrentPosition().then(
+ (position) {
+ expect(position.coords.latitude, isNotNull);
+ expect(position.coords.longitude, isNotNull);
+ expect(position.coords.accuracy, isNotNull);
+ });
+ });
+
+ futureTest('watchPosition', () {
+ return window.navigator.geolocation.watchPosition().first.then(
+ (position) {
+ expect(position.coords.latitude, isNotNull);
+ expect(position.coords.longitude, isNotNull);
+ expect(position.coords.accuracy, isNotNull);
+ });
+ });
+ });
+
+ group('MediaStream', () {
+ if (MediaStream.supported) {
+ futureTest('getUserMedia', () {
+ return window.navigator.getUserMedia(video: true).then((stream) {
+ expect(stream, isNotNull);
+
+ var url = Url.createObjectUrl(stream);
+ expect(url, isNotNull);
+
+ var video = new VideoElement()
+ ..autoplay = true;
+
+ var completer = new Completer();
+ video.onError.listen((e) {
+ completer.completeError(e);
+ });
+ video.onPlaying.first.then((e) {
+ completer.complete(video);
+ });
+
+ document.body.append(video);
+ video.src = url;
+
+ return completer.future;
+ });
+ });
+ }
+ });
+}
diff --git a/tests/html/websql_test.dart b/tests/html/websql_test.dart
index 16871b8..f98c6bb 100644
--- a/tests/html/websql_test.dart
+++ b/tests/html/websql_test.dart
@@ -3,6 +3,7 @@
import '../../pkg/unittest/lib/html_individual_config.dart';
import 'dart:async';
import 'dart:html';
+import 'dart:web_sql';
void fail(message) {
guardAsync(() {
@@ -10,7 +11,7 @@
});
}
-Future<SqlTransaction> createTransaction(Database db) {
+Future<SqlTransaction> createTransaction(SqlDatabase db) {
final completer = new Completer<SqlTransaction>();
db.transaction((SqlTransaction transaction) {
@@ -91,13 +92,13 @@
group('supported', () {
test('supported', () {
- expect(Database.supported, true);
+ expect(SqlDatabase.supported, true);
});
});
group('functional', () {
test('unsupported throws', () {
- var expectation = Database.supported ? returnsNormally : throws;
+ var expectation = SqlDatabase.supported ? returnsNormally : throws;
expect(() {
window.openDatabase('test_db', '1.0', 'test_db', 1024 * 1024);
}, expectation);
@@ -105,7 +106,7 @@
});
test('Web Database', () {
// Skip if not supported.
- if (!Database.supported) {
+ if (!SqlDatabase.supported) {
return;
}
diff --git a/tests/html/wheelevent_test.dart b/tests/html/wheelevent_test.dart
index 6de1528..2dc9adc 100644
--- a/tests/html/wheelevent_test.dart
+++ b/tests/html/wheelevent_test.dart
@@ -20,6 +20,7 @@
expect(e.screenX, 100);
expect(e.deltaX, 0);
expect(e.deltaY, 240);
+ expect(e.deltaMode, isNotNull);
}));
var event = new WheelEvent(eventType,
deltaX: 0,
diff --git a/tests/html/xhr_cross_origin_test.dart b/tests/html/xhr_cross_origin_test.dart
index 3bbb5ca..5f22ee7 100644
--- a/tests/html/xhr_cross_origin_test.dart
+++ b/tests/html/xhr_cross_origin_test.dart
@@ -30,7 +30,8 @@
var port = crossOriginPort;
test('XHR Cross-domain', () {
- var url = "http://localhost:$port/tests/html/xhr_cross_origin_data.txt";
+ var url = "http://localhost:$port/"
+ "root_dart/tests/html/xhr_cross_origin_data.txt";
var xhr = new HttpRequest();
xhr.open('GET', url, true);
var validate = expectAsync1((data) {
@@ -49,7 +50,8 @@
});
test('XHR.get Cross-domain', () {
- var url = "http://localhost:$port/tests/html/xhr_cross_origin_data.txt";
+ var url = "http://localhost:$port/"
+ "root_dart/tests/html/xhr_cross_origin_data.txt";
HttpRequest.request(url).then(expectAsync1((xhr) {
var data = json.parse(xhr.response);
expect(data, contains('feed'));
@@ -59,7 +61,8 @@
});
test('XHR.getWithCredentials Cross-domain', () {
- var url = "http://localhost:$port/tests/html/xhr_cross_origin_data.txt";
+ var url = "http://localhost:$port/"
+ "root_dart/tests/html/xhr_cross_origin_data.txt";
HttpRequest.request(url, withCredentials: true).then(expectAsync1((xhr) {
var data = json.parse(xhr.response);
expect(data, contains('feed'));
diff --git a/tests/html/xhr_test.dart b/tests/html/xhr_test.dart
index 6c8393c..91e86d5 100644
--- a/tests/html/xhr_test.dart
+++ b/tests/html/xhr_test.dart
@@ -17,7 +17,7 @@
main() {
useHtmlIndividualConfiguration();
- var url = "/tests/html/xhr_cross_origin_data.txt";
+ var url = "/root_dart/tests/html/xhr_cross_origin_data.txt";
void validate200Response(xhr) {
expect(xhr.status, equals(200));
diff --git a/tests/language/field_override2_test.dart b/tests/language/field_override2_test.dart
new file mode 100644
index 0000000..0a08402
--- /dev/null
+++ b/tests/language/field_override2_test.dart
@@ -0,0 +1,20 @@
+// 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.
+
+// Test that we are accessing the right field in a method of a super
+// class, when that field is overridden.
+
+class A {
+ final a = [42];
+ foo() => a[0];
+}
+
+class B extends A {
+ final a = new Map();
+}
+
+main() {
+ Expect.equals(null, new B().foo());
+ Expect.equals(42, new A().foo());
+}
diff --git a/tests/language/inline_test.dart b/tests/language/inline_test.dart
new file mode 100644
index 0000000..3470a3e
--- /dev/null
+++ b/tests/language/inline_test.dart
@@ -0,0 +1,25 @@
+// 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.
+
+// Regression test for dart2js that used to produce a non-valid SSA
+// graph when inlining within a loop.
+
+class X {
+ void x(a, b) {
+ do {
+ if (identical(a, b)) {
+ break;
+ }
+ } while (p(a, b));
+ }
+
+ bool p(a, b) {
+ return identical(a, b);
+ }
+}
+
+main() {
+ var x = new X();
+ x.x(1, 2);
+}
diff --git a/tests/language/language.status b/tests/language/language.status
index 8d65af7..10d2a54 100644
--- a/tests/language/language.status
+++ b/tests/language/language.status
@@ -56,6 +56,8 @@
type_parameter_literal_test: Fail # Issue 7551
+on_catch_malformed_type_test: Fail # Issue 8601
+
mixin_illegal_syntax_test/none: Fail
mixin_type_parameters_mixin_test: Fail
mixin_type_parameters_super_test: Fail
@@ -349,6 +351,8 @@
many_overridden_no_such_method_test: Fail, Pass, OK # Fails in minified mode, test depends on method names.
overridden_no_such_method_test: Fail, Pass, OK # Fails in minified mode, test depends on method names.
+on_catch_malformed_type_test: Fail # Issue 8601
+
# Mixins fail on the VM.
mixin_illegal_syntax_test/none: Fail # VM issue
mixin_illegal_constructor_test/none: Fail # VM issue
@@ -389,7 +393,6 @@
compile_time_constant_arguments_test/05: Fail # http://dartbug.com/5519
compile_time_constant_arguments_test/06: Fail # http://dartbug.com/5519
const_constructor_syntax_test/04: Fail # http://dartbug.com/5519
-const_constructor_syntax_test/05: Fail # http://dartbug.com/5519
const_syntax_test/01: Fail # http://dartbug.com/5519
const_syntax_test/02: Fail # http://dartbug.com/5519
const_syntax_test/03: Fail # http://dartbug.com/5519
@@ -398,7 +401,6 @@
const_syntax_test/06: Fail # http://dartbug.com/5519
const_syntax_test/07: Fail # http://dartbug.com/5519
const_syntax_test/08: Fail # http://dartbug.com/5519
-const_syntax_test/09: Fail # http://dartbug.com/5519
const_syntax_test/10: Fail # http://dartbug.com/5519
constructor_named_arguments_test/01: Fail # http://dartbug.com/5519
final_for_in_variable_test/01: Fail # http://dartbug.com/5519
@@ -407,7 +409,6 @@
final_syntax_test/02: Fail # http://dartbug.com/5519
final_syntax_test/03: Fail # http://dartbug.com/5519
final_syntax_test/04: Fail # http://dartbug.com/5519
-final_syntax_test/09: Fail # http://dartbug.com/5519
getter_no_setter_test/01: Fail # http://dartbug.com/5519
getter_no_setter2_test/01: Fail # http://dartbug.com/5519
illegal_invocation_test/03: Fail # http://dartbug.com/5519
diff --git a/tests/language/language_dart2js.status b/tests/language/language_dart2js.status
index de512aa..332f08d 100644
--- a/tests/language/language_dart2js.status
+++ b/tests/language/language_dart2js.status
@@ -48,7 +48,6 @@
f_bounded_quantification_test/01: Fail
f_bounded_quantification_test/02: Fail
closure_type_test: Fail # does not detect type error in checked mode.
-function_type_test: Fail # does not detect type error in checked mode.
factory1_test/00: Fail
factory1_test/01: Fail
type_annotation_test/09: Fail # Named constructors interpreted as a type.
@@ -198,6 +197,7 @@
super_implicit_closure_test: Fail # internal error: super property read not implemented
super_operator_test: Fail # internal error: super property store not implemented
switch_label_test: Fail # error: target of continue is not a loop or switch case
+on_catch_malformed_type_test: Fail # Malformed types cause compile-time errors.
# Failing tests for mixin type parameters.
@@ -266,8 +266,6 @@
field5_negative_test: Fail # Negative language test.
field6a_negative_test: Fail # Negative language test.
final_for_in_variable_test/01: Fail # Negative language test
-final_param_negative_test: Fail # Negative language test.
-final_var_negative_test: Fail # Negative language test.
instantiate_type_variable_negative_test: Pass # For the wrong reason.
interface_factory3_negative_test: Fail # Negative language test.
interface_factory_constructor_negative_test: Fail # Negative language test.
@@ -283,9 +281,6 @@
pseudo_kw_illegal_test/03: Fail # Negative language test.
pseudo_kw_illegal_test/14: Fail # Negative language test.
scope_negative_test: Fail # Negative language test.
-static_field_test/01: Fail # Negative language test.
-static_field_test/03: Fail # Negative language test.
-static_final_field2_negative_test: Fail # Negative language test.too
static_final_field_negative_test: Fail # Negative language test.
static_top_level_test/00: Fail # Negative language test.
static_top_level_test/01: Fail # Negative language test.
@@ -331,11 +326,10 @@
[ $compiler == dart2js && $runtime == none ]
*: Fail, Pass # TODO(ahe): Triage these tests.
+
[ $compiler == dart2js && ($runtime == ff || $runtime == jsshell || $runtime == ie9)]
arithmetic_test: Fail # Issue 7881
-[ $compiler == dart2js && ($runtime == ff || $runtime == jsshell)]
-call_through_null_getter_test: Fail # Expected: ObjectNotClosureException got: Instance of 'TypeError'
[ $compiler == dart2js && $runtime == ie9 ]
div_by_zero_test: Fail
@@ -353,14 +347,12 @@
execute_finally8_test: Fail # Issue: 7414
execute_finally9_test: Fail # Issue: 7414
null_access_error_test: Fail # Issue: 7414
-string_interpolate_null_test: Fail # Issue: 7414
-call_through_null_getter_test: Fail # Expected: ObjectNotClosureException got: Instance of 'TypeError'
closure3_test: Fail # Uncaught error: Instance of 'TypeError'
-method_invocation_test: Fail # Uncaught error: Instance of 'TypeError'
null_pointer_exception_test: Fail # Uncaught error: Instance of 'TypeError'
string_interpolate_npe_test: Fail # Uncaught error: Instance of 'TypeError'
arithmetic_test: Fail # Issue: 7414
+
[ $runtime == opera ]
null_access_error_test: Fail # Issue: 7413
string_interpolate_null_test: Fail # Issue: 7413
diff --git a/tests/language/mixin_extends_method_test.dart b/tests/language/mixin_extends_method_test.dart
index 9465ff0..e9423e4 100644
--- a/tests/language/mixin_extends_method_test.dart
+++ b/tests/language/mixin_extends_method_test.dart
@@ -8,7 +8,8 @@
}
class M1 {
- bar() => "M1-bar";
+ static m1bar() => "M1-bar";
+ bar() => m1bar();
}
class M2 {
diff --git a/tests/language/mixin_override_regression_test.dart b/tests/language/mixin_override_regression_test.dart
new file mode 100644
index 0000000..bbc9b89
--- /dev/null
+++ b/tests/language/mixin_override_regression_test.dart
@@ -0,0 +1,22 @@
+// 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.
+
+class C0 {
+ int m1() => 5;
+ int m2() => m1();
+}
+
+typedef C1 = Object with C0;
+
+class D {
+ int m1() => 7;
+}
+
+class E0 extends C0 with D {}
+class E1 extends C1 with D {}
+
+main() {
+ Expect.equals(7, new E0().m2());
+ Expect.equals(7, new E1().m2());
+}
diff --git a/tests/language/on_catch_malformed_type_test.dart b/tests/language/on_catch_malformed_type_test.dart
new file mode 100644
index 0000000..ae5e043
--- /dev/null
+++ b/tests/language/on_catch_malformed_type_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.
+
+// Check that malformed types in on-catch are handled correctly, that is
+// catches all in production mode and throws a type error in checked mode.
+
+isCheckedMode() {
+ try {
+ String s = 1;
+ return false;
+ } on TypeError catch(e) {
+ return true;
+ }
+}
+
+checkTypeError(f()) {
+ if(isCheckedMode()) {
+ try {
+ f();
+ Expect.fail("Type error expected in checking mode");
+ } on TypeError catch(ok) {
+ }
+ } else {
+ f();
+ }
+}
+
+catchUnresolvedBefore() {
+ try {
+ throw "foo";
+ Expect.fail("This code shouldn't be executed");
+ } on String catch(oks) {
+ // This is tested before the catch block below.
+ } on Unavailable catch(ex) {
+ Expect.fail("This code shouldn't be executed");
+ }
+}
+
+catchUnresolvedAfter() {
+ try {
+ throw "foo";
+ Expect.fail("This code shouldn't be executed");
+ } on Unavailable catch(ex) {
+ // This is tested before the catch block below.
+ // In production mode the test is always true, in checked mode
+ // it throws a type error.
+ } on String catch(oks) {
+ Expect.fail("This code shouldn't be executed");
+ }
+}
+
+main() {
+ catchUnresolvedBefore();
+ checkTypeError(catchUnresolvedAfter);
+}
diff --git a/tests/lib/async/deferred/deferred_api_library.dart b/tests/lib/async/deferred/deferred_api_library.dart
new file mode 100644
index 0000000..b590770
--- /dev/null
+++ b/tests/lib/async/deferred/deferred_api_library.dart
@@ -0,0 +1,12 @@
+// 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.
+
+// Imported by deferred_api_test.dart.
+
+library deferred_api_library;
+
+foo(x) {
+ print('foo($x)');
+ return 42;
+}
diff --git a/tests/lib/async/deferred/deferred_api_test.dart b/tests/lib/async/deferred/deferred_api_test.dart
new file mode 100644
index 0000000..a144468
--- /dev/null
+++ b/tests/lib/async/deferred/deferred_api_test.dart
@@ -0,0 +1,37 @@
+// 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.
+
+// Test that that the basic API for deferred/lazy loading works. This
+// test deliberately does not test that the deferred elements throw a
+// NoSuchMethodError before the deferred library is loaded. This
+// makes it possible to pass this test without having implemented
+// deferred loading correctly.
+
+import 'dart:async';
+
+@lazy
+import 'deferred_api_library.dart';
+
+const lazy = const DeferredLibrary('deferred_api_library');
+
+main() {
+ print('unittest-suite-wait-for-done');
+
+ int counter = 0;
+ lazy.load().then((bool didLoad) {
+ Expect.isTrue(didLoad);
+ Expect.equals(1, ++counter);
+ Expect.equals(42, foo('b'));
+ print('lazy was loaded');
+ });
+ Expect.equals(0, counter);
+ lazy.load().then((bool didLoad) {
+ Expect.isFalse(didLoad);
+ Expect.equals(2, ++counter);
+ Expect.equals(42, foo('b'));
+ print('lazy was loaded');
+ print('unittest-suite-success');
+ });
+ Expect.equals(0, counter);
+}
diff --git a/tests/lib/async/slow_consumer_test.dart b/tests/lib/async/slow_consumer_test.dart
index 4b14924..2d40658 100644
--- a/tests/lib/async/slow_consumer_test.dart
+++ b/tests/lib/async/slow_consumer_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.
-// VMOptions=--old_gen_heap_size=32
+// VMOptions=--old_gen_heap_size=64
library slow_consumer_test;
@@ -20,6 +20,7 @@
SlowConsumer(int this.bytesPerSecond);
Future consume(Stream stream) {
+ bool done = false;
Completer completer = new Completer();
var subscription;
subscription = stream.listen(
@@ -29,15 +30,18 @@
// Simulated amount of time it takes to handle the data.
int ms = data.length * 1000 ~/ bytesPerSecond;
Duration duration = new Duration(milliseconds: ms);
- subscription.pause();
+ if (!done) subscription.pause();
return new Future.delayed(duration, () {
- subscription.resume();
+ if (!done) subscription.resume();
// Make sure we use data here to keep tracking it.
return count + data.length;
});
});
},
- onDone: () { current.then((count) { completer.complete(count); }); });
+ onDone: () {
+ done = true;
+ current.then((count) { completer.complete(count); });
+ });
return completer.future;
}
}
@@ -48,6 +52,7 @@
int sentCount = 0;
int targetCount;
StreamController controller;
+ Timer pendingSend;
DataProvider(int this.bytesPerSecond, int this.targetCount, this.chunkSize) {
controller = new StreamController(onPauseStateChange: onPauseStateChange);
@@ -57,6 +62,10 @@
Stream get stream => controller.stream;
send() {
+ if (pendingSend != null) {
+ pendingSend.cancel();
+ pendingSend = null;
+ }
if (controller.isPaused) return;
if (sentCount == targetCount) {
controller.close();
@@ -71,7 +80,9 @@
controller.add(new List.fixedLength(listSize));
int ms = listSize * 1000 ~/ bytesPerSecond;
Duration duration = new Duration(milliseconds: ms);
- if (!controller.isPaused) new Timer(duration, send);
+ if (!controller.isPaused) {
+ pendingSend = new Timer(duration, send);
+ }
}
onPauseStateChange() {
@@ -87,7 +98,7 @@
// the slower consumer who can only read 200MB/s. The data is sent in 1MB
// chunks.
//
- // This test is limited to 32MB of heap-space (see VMOptions on top of the
+ // This test is limited to 64MB of heap-space (see VMOptions on top of the
// file). If the consumer doesn't pause the data-provider it will run out of
// heap-space.
diff --git a/tests/lib/lib.status b/tests/lib/lib.status
index 8381332..73efcec 100644
--- a/tests/lib/lib.status
+++ b/tests/lib/lib.status
@@ -2,10 +2,15 @@
# 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.
+async/slow_consumer_test: Skip # Issue 8647
+
[ $compiler == dart2js ]
math/*: Skip
mirrors/*: Skip
+[ $compiler == dart2js && $checked ]
+async/stream_event_transform_test: Fail # Issue 7733.
+
[ $compiler == dart2js && $jscl ]
async/future_test: Fail # Timer interface not supported; dartbug.com/7728.
async/slow_consumer2_test: Fail # Timer interface not supported; dartbug.com/7728.
@@ -17,9 +22,6 @@
crypto/sha256_test: Slow, Pass
crypto/sha1_test: Slow, Pass
-[ $runtime == safari]
-crypto/hmac_md5_test: Fail # Bug in JSC: the test only passes when being debugged.
-
[ $compiler == dart2dart ]
# Skip until we stabilize language tests.
*: Skip
@@ -29,9 +31,16 @@
crypto/hmac_sha1_test: Fail
crypto/hmac_sha256_test: Fail
+[ $runtime == safari]
+ # Bug in JSC: the test only passes when being debugged.
+crypto/hmac_md5_test: Fail, Pass
+
[ $runtime == vm && $arch == x64 ]
async/slow_consumer2_test: Fail # Issue 7726
+[ $compiler == none && $runtime == drt ]
+async/deferred/deferred_api_test: Fail # http://dartbug.com/2264
+
[ $arch == arm ]
*: Skip
diff --git a/tests/standalone/debugger/debug_lib.dart b/tests/standalone/debugger/debug_lib.dart
index 6a38ca4..d71f3e6 100644
--- a/tests/standalone/debugger/debug_lib.dart
+++ b/tests/standalone/debugger/debug_lib.dart
@@ -310,8 +310,6 @@
Process targetProcess;
int portNumber;
Socket socket;
- OutputStream to;
- StringInputStream from;
JsonBuffer responses = new JsonBuffer();
DebugScript script;
@@ -324,22 +322,33 @@
String scriptUrl = null;
bool shutdownEventSeen = false;
int isolateId = 0;
-
+
+ // stdin subscription to allow terminating the test via command-line.
+ var stdinSubscription;
+
Debugger(this.targetProcess, this.portNumber) {
- var targetStdout = new StringInputStream(targetProcess.stdout);
- targetStdout.onLine = () {
- var s = targetStdout.readLine();
+ stdinSubscription =
+ stdin.listen((d) {},
+ onError: (error) => close(killDebugee: true),
+ onDone: () => close(killDebugee: true));
+
+ var stdoutStringStream = targetProcess.stdout
+ .transform(new StringDecoder())
+ .transform(new LineTransformer());
+ stdoutStringStream.listen((line) {
if (showDebuggeeOutput) {
- print("TARG: $s");
+ print("TARG: $line");
}
- };
- var targetStderr = new StringInputStream(targetProcess.stderr);
- targetStderr.onLine = () {
- var s = targetStderr.readLine();
+ });
+
+ var stderrStringStream = targetProcess.stderr
+ .transform(new StringDecoder())
+ .transform(new LineTransformer());
+ stderrStringStream.listen((line) {
if (showDebuggeeOutput) {
- print("TARG: $s");
+ print("TARG: $line");
}
- };
+ });
}
// Handle debugger events for which there is no explicit
@@ -451,7 +460,7 @@
void sendMessage(Map<String,dynamic> msg) {
String jsonMsg = JSON.stringify(msg);
if (verboseWire) print("SEND: $jsonMsg");
- to.writeString(jsonMsg, Encoding.UTF_8);
+ socket.addString(jsonMsg);
}
bool get errorsDetected => errors.length > 0;
@@ -462,34 +471,36 @@
}
void openConnection() {
- socket = new Socket("127.0.0.1", portNumber);
- to = socket.outputStream;
- from = new StringInputStream(socket.inputStream, Encoding.UTF_8);
- from.onData = () {
- try {
- responses.append(from.read());
- handleMessages();
- } catch(e, trace) {
- print("Unexpected exception:\n$e\n$trace");
- close(killDebugee: true);
- }
- };
- from.onClosed = () {
- print("Connection closed by debug target");
- close(killDebugee: true);
- };
- from.onError = (e) {
- print("Error '$e' detected in input stream from debug target");
- close(killDebugee: true);
- };
+ Socket.connect("127.0.0.1", portNumber).then((s) {
+ socket = s;
+ var stringStream = socket.transform(new StringDecoder());
+ stringStream.listen(
+ (str) {
+ try {
+ responses.append(str);
+ handleMessages();
+ } catch(e, trace) {
+ print("Unexpected exception:\n$e\n$trace");
+ close(killDebugee: true);
+ }
+ },
+ onDone: () {
+ print("Connection closed by debug target");
+ close(killDebugee: true);
+ },
+ onError: (e) {
+ print("Error '$e' detected in input stream from debug target");
+ close(killDebugee: true);
+ });
+ });
}
void close({killDebugee: false}) {
if (errorsDetected) {
for (int i = 0; i < errors.length; i++) print(errors[i]);
}
- to.close();
socket.close();
+ stdinSubscription.cancel();
if (killDebugee) {
targetProcess.kill();
print("Target process killed");
@@ -518,15 +529,12 @@
Process.start(options.executable, targetOpts).then((Process process) {
print("Debug target process started");
process.stdin.close();
- process.stdout.onData = process.stdout.read;
- process.stderr.onData = process.stderr.read;
- process.onExit = (int exitCode) {
+ process.exitCode.then((int exitCode) {
+ Expect.equals(0, exitCode);
Expect.equals(0, exitCode);
print("Debug target process exited with exit code $exitCode");
- };
+ });
var debugger = new Debugger(process, debugPort);
- stdin.onClosed = () => debugger.close(killDebugee: true);
- stdin.onError = (error) => debugger.close(killDebugee: true);
debugger.runScript(script);
});
return true;
diff --git a/tests/standalone/io/chunked_stream_test.dart b/tests/standalone/io/chunked_stream_test.dart
deleted file mode 100644
index 2f34378..0000000
--- a/tests/standalone/io/chunked_stream_test.dart
+++ /dev/null
@@ -1,140 +0,0 @@
-// 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.
-
-import "dart:io";
-import "dart:isolate";
-
-void test1() {
- void testWithChunkSize(var data, int chunkSize, Function testDone) {
- ListInputStream list_input_stream = new ListInputStream();
- list_input_stream.write(data);
- list_input_stream.markEndOfStream();
- ChunkedInputStream stream = new ChunkedInputStream(list_input_stream);
- int chunkCount = 0;
- int byteCount = 0;
- void chunkData() {
- List<int> chunk = stream.read();
- if (byteCount + chunkSize < data.length) {
- Expect.equals(chunkSize, chunk.length);
- } else {
- if (byteCount == data.length) {
- Expect.equals(null, chunk);
- } else {
- Expect.equals(data.length - byteCount, chunk.length);
- }
- }
- if (chunk != null) {
- for (int i = 0; i < chunk.length;i++) {
- Expect.equals(data[byteCount], chunk[i]);
- byteCount++;
- }
- chunkCount++;
- }
- }
-
- void closeHandler() {
- Expect.equals(data.length, byteCount);
- testDone(byteCount);
- }
-
- stream.onData = chunkData;
- stream.onClosed = closeHandler;
- stream.chunkSize = chunkSize;
- }
-
- for (int i = 1; i <= 10; i++) {
- var data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
-
- void testDone(int byteCount) {
- Expect.equals(data.length, byteCount);
- }
-
- testWithChunkSize(data, i, testDone);
- }
-
- var _16k = 1024 * 16;
- var data = new List<int>.fixedLength(_16k);
- for (int i = 0; i < _16k; i++) { data[i] = i % 256; }
-
- void testDone(int byteCount) {
- Expect.equals(data.length, byteCount);
- }
-
- testWithChunkSize(data, 512, testDone);
- testWithChunkSize(data, 1024, testDone);
- testWithChunkSize(data, 2048, testDone);
-}
-
-
-void test2() {
- ListInputStream s = new ListInputStream();
- ChunkedInputStream stream = new ChunkedInputStream(s);
- stream.chunkSize = 5;
- ReceivePort donePort = new ReceivePort();
-
- var stage = 0;
-
- void chunkData() {
- var chunk;
- if (stage == 0) {
- Expect.equals(stream.chunkSize, 5);
- chunk = stream.read(); // 5 bytes read from stream.
- Expect.equals(stream.chunkSize, chunk.length);
- chunk = stream.read(); // 5 bytes read from stream.
- Expect.equals(null, chunk);
- stage++;
- s.write([7, 8, 9]); // 10 bytes written to stream.
- } else if (stage == 1) {
- Expect.equals(stream.chunkSize, 5);
- chunk = stream.read(); // 10 bytes read from stream.
- Expect.equals(stream.chunkSize, chunk.length);
- chunk = stream.read(); // 10 bytes read from stream.
- Expect.equals(null, chunk);
- stage++;
- s.write([10, 11, 12, 13, 14]); // 15 bytes written to stream.
- s.write([15, 16, 17, 18]); // 19 bytes written to stream.
- } else if (stage == 2) {
- Expect.equals(stream.chunkSize, 5);
- chunk = stream.read(); // 15 bytes read from stream.
- Expect.equals(stream.chunkSize, chunk.length);
- chunk = stream.read(); // 15 bytes read from stream.
- Expect.equals(null, chunk);
- stage++;
- stream.chunkSize = 3;
- } else if (stage == 3) {
- Expect.equals(stream.chunkSize, 3);
- chunk = stream.read(); // 18 bytes read from stream.
- Expect.equals(stream.chunkSize, chunk.length);
- chunk = stream.read(); // 18 bytes read from stream.
- Expect.equals(null, chunk);
- stage++;
- s.markEndOfStream(); // 18 bytes written to stream.
- } else if (stage == 4) {
- chunk = stream.read(); // 19 bytes read from stream.
- Expect.equals(1, chunk.length);
- chunk = stream.read(); // 19 bytes read from stream.
- Expect.equals(null, chunk);
- stage++;
- donePort.toSendPort().send(stage);
- }
- }
-
- void streamClosed() {
- Expect.equals(5, stage);
- }
-
- stream.onData = chunkData;
- stream.onClosed = streamClosed;
- s.write([0, 1, 2, 3]); // 4 bytes written to stream.
- Expect.equals(0, stage);
- s.write([4, 5, 6]); // 7 bytes written to stream.
-
- donePort.receive((x,y) => donePort.close());
-}
-
-
-main() {
- test1();
- test2();
-}
diff --git a/tests/standalone/io/dart_std_io_pipe_test.dart b/tests/standalone/io/dart_std_io_pipe_test.dart
index 967c48a..a4d4f24 100644
--- a/tests/standalone/io/dart_std_io_pipe_test.dart
+++ b/tests/standalone/io/dart_std_io_pipe_test.dart
@@ -42,7 +42,7 @@
[executable, dartScript, type, pipeOutFile, redirectOutFile];
var future = Process.start(shellScript, args);
future.then((process) {
- process.onExit = (exitCode) {
+ process.exitCode.then((exitCode) {
Expect.equals(0, exitCode);
// Check the expected file contents.
@@ -66,10 +66,10 @@
// Cleanup test directory.
dir.deleteSync(recursive: true);
- };
+ });
// Drain out and err streams so they close.
- process.stdout.onData = process.stdout.read;
- process.stderr.onData = process.stderr.read;
+ process.stdout.listen((_) {});
+ process.stderr.listen((_) {});
});
future.catchError((error) {
dir.deleteSync(recursive: true);
diff --git a/tests/standalone/io/delete_symlink_test.dart b/tests/standalone/io/delete_symlink_test.dart
new file mode 100644
index 0000000..3161eb4
--- /dev/null
+++ b/tests/standalone/io/delete_symlink_test.dart
@@ -0,0 +1,50 @@
+// 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 'dart:io';
+import 'dart:isolate';
+
+void main() {
+ // temp/
+ // a/
+ // file.txt
+ // b/
+ // a_link -> a
+ var d = new Directory("").createTempSync();
+ var a = new Directory("${d.path}/a");
+ a.createSync();
+
+ var b = new Directory("${d.path}/b");
+ b.createSync();
+
+ var f = new File("${d.path}/a/file.txt");
+ f.createSync();
+ Expect.isTrue(f.existsSync());
+
+ // Create a symlink (or junction on Windows) from
+ // temp/b/a_link to temp/a.
+ var cmd = "ln";
+ var args = ['-s', "${d.path}/b/a_link", "${d.path}/a"];
+
+ if (Platform.operatingSystem == "windows") {
+ cmd = "cmd";
+ args = ["/c", "mklink", "/j", "${d.path}\\b\\a_link", "${d.path}\\a"];
+ }
+
+ var keepAlive = new ReceivePort();
+
+ Process.run(cmd, args).then((_) {
+ // Delete the directory containing the junction.
+ b.deleteSync(recursive: true);
+
+ // We should not have recursed through a_link into a.
+ Expect.isTrue(f.existsSync());
+
+ // Clean up after ourselves.
+ d.deleteSync(recursive: true);
+
+ // Terminate now that we are done with everything.
+ keepAlive.close();
+ });
+}
diff --git a/tests/standalone/io/directory_error_test.dart b/tests/standalone/io/directory_error_test.dart
index ed2b60c..950f025 100644
--- a/tests/standalone/io/directory_error_test.dart
+++ b/tests/standalone/io/directory_error_test.dart
@@ -4,6 +4,7 @@
//
// Dart test program for testing error handling in directory I/O.
+import "dart:async";
import "dart:io";
import "dart:isolate";
@@ -133,14 +134,21 @@
}
+bool checkAsyncListNonExistentFileException(e) {
+ Expect.isTrue(e is AsyncError);
+ return checkListNonExistentFileException(e.error);
+}
+
+
void testListNonExistent(Directory temp, Function done) {
Directory nonExistent = new Directory("${temp.path}/nonExistent");
Expect.throws(() => nonExistent.listSync(), (e) => e is DirectoryIOException);
- var lister = nonExistent.list();
- lister.onError = (e) {
- checkListNonExistentFileException(e);
- done();
- };
+ nonExistent.list().listen(
+ (_) => Expect.fail("listing should not succeed"),
+ onError: (e) {
+ checkAsyncListNonExistentFileException(e);
+ done();
+ });
}
diff --git a/tests/standalone/io/directory_fuzz_test.dart b/tests/standalone/io/directory_fuzz_test.dart
index 967ac0e..c1dadef 100644
--- a/tests/standalone/io/directory_fuzz_test.dart
+++ b/tests/standalone/io/directory_fuzz_test.dart
@@ -30,7 +30,6 @@
typeMapping.forEach((k2, v2) {
doItSync(() => d.renameSync(v2));
doItSync(() => d.listSync(recursive: v2));
- doItSync(() => d.list(recursive: v2).onError = (e) => null);
});
});
});
@@ -57,6 +56,9 @@
}));
typeMapping.forEach((k2, v2) {
futures.add(doItAsync(() => d.rename(v2)));
+ futures.add(doItAsync(() {
+ d.list(recursive: v2).listen((_) {}, onError: (e) => null);
+ }));
});
});
Future.wait(futures).then((ignore) => port.close());
diff --git a/tests/standalone/io/directory_invalid_arguments_test.dart b/tests/standalone/io/directory_invalid_arguments_test.dart
index 029eccd..54cdfda 100644
--- a/tests/standalone/io/directory_invalid_arguments_test.dart
+++ b/tests/standalone/io/directory_invalid_arguments_test.dart
@@ -3,55 +3,52 @@
// BSD-style license that can be found in the LICENSE file.
import "dart:io";
+import "dart:isolate";
-class DirectoryInvalidArgumentsTest {
- static void testFailingList(Directory d, var recursive) {
- int errors = 0;
- var lister = d.list(recursive: recursive);
- lister.onError = (error) {
+void testFailingList(Directory d, var recursive) {
+ var port = new ReceivePort();
+ int errors = 0;
+ d.list(recursive: recursive).listen(
+ () => Expect.fail("Unexpected listing result"),
+ onError: (error) {
errors += 1;
- };
- lister.onDone = (completed) {
+ },
+ onDone: () {
+ port.close();
Expect.equals(1, errors);
- Expect.isFalse(completed);
- };
- Expect.equals(0, errors);
- }
+ });
+ Expect.equals(0, errors);
+}
- static void testInvalidArguments() {
- Directory d = new Directory(12);
- try {
- d.existsSync();
- Expect.fail("No exception thrown");
- } catch (e) {
- Expect.isTrue(e is ArgumentError);
- }
- try {
- d.deleteSync();
- Expect.fail("No exception thrown");
- } catch (e) {
- Expect.isTrue(e is ArgumentError);
- }
- try {
- d.createSync();
- Expect.fail("No exception thrown");
- } catch (e) {
- Expect.isTrue(e is ArgumentError);
- }
- testFailingList(d, false);
- Expect.throws(() => d.listSync(recursive: true),
- (e) => e is ArgumentError);
- d = new Directory(".");
- testFailingList(d, 1);
- Expect.throws(() => d.listSync(recursive: 1),
- (e) => e is ArgumentError);
+void testInvalidArguments() {
+ Directory d = new Directory(12);
+ try {
+ d.existsSync();
+ Expect.fail("No exception thrown");
+ } catch (e) {
+ Expect.isTrue(e is ArgumentError);
}
-
- static void testMain() {
- testInvalidArguments();
+ try {
+ d.deleteSync();
+ Expect.fail("No exception thrown");
+ } catch (e) {
+ Expect.isTrue(e is ArgumentError);
}
+ try {
+ d.createSync();
+ Expect.fail("No exception thrown");
+ } catch (e) {
+ Expect.isTrue(e is ArgumentError);
+ }
+ testFailingList(d, false);
+ Expect.throws(() => d.listSync(recursive: true),
+ (e) => e is ArgumentError);
+ d = new Directory(".");
+ testFailingList(d, 1);
+ Expect.throws(() => d.listSync(recursive: 1),
+ (e) => e is ArgumentError);
}
main() {
- DirectoryInvalidArgumentsTest.testMain();
+ testInvalidArguments();
}
diff --git a/tests/standalone/io/directory_list_nonexistent_test.dart b/tests/standalone/io/directory_list_nonexistent_test.dart
index 81c9ac0..e07cb22 100644
--- a/tests/standalone/io/directory_list_nonexistent_test.dart
+++ b/tests/standalone/io/directory_list_nonexistent_test.dart
@@ -11,16 +11,19 @@
import "dart:isolate";
void testListNonExistent() {
+ var keepAlive = new ReceivePort();
new Directory("").createTemp().then((d) {
d.delete().then((ignore) {
Expect.throws(() => d.listSync(), (e) => e is DirectoryIOException);
Expect.throws(() => d.listSync(recursive: true),
(e) => e is DirectoryIOException);
+ keepAlive.close();
});
});
}
void testListTooLongName() {
+ var keepAlive = new ReceivePort();
new Directory("").createTemp().then((d) {
var subDirName = 'subdir';
var subDir = new Directory("${d.path}/$subDirName");
@@ -37,6 +40,8 @@
(e) => e is DirectoryIOException);
Expect.throws(() => long.listSync(recursive: true),
(e) => e is DirectoryIOException);
+ d.deleteSync(recursive: true);
+ keepAlive.close();
});
});
}
diff --git a/tests/standalone/io/directory_test.dart b/tests/standalone/io/directory_test.dart
index 8a3776a..0d6500e 100644
--- a/tests/standalone/io/directory_test.dart
+++ b/tests/standalone/io/directory_test.dart
@@ -50,31 +50,33 @@
testSyncListing(false);
Expect.equals(f.fullPathSync(), fLong.fullPathSync());
- var lister = directory.list(recursive: true);
-
- lister.onDir = (dir) {
- listedDir = true;
- Expect.isTrue(dir.contains(directory.path));
- Expect.isTrue(dir.contains('subdir'));
- };
-
- lister.onFile = (f) {
- listedFile = true;
- Expect.isTrue(f.contains(directory.path));
- Expect.isTrue(f.contains('subdir'));
- Expect.isTrue(f.contains('file.txt'));
- };
-
- lister.onDone = (completed) {
- Expect.isTrue(completed, "directory listing did not complete");
- Expect.isTrue(listedDir, "directory not found");
- Expect.isTrue(listedFile, "file not found");
- directory.delete(recursive: true).then((ignore) {
- f.exists().then((exists) => Expect.isFalse(exists));
- directory.exists().then((exists) => Expect.isFalse(exists));
- subDirectory.exists().then((exists) => Expect.isFalse(exists));
- });
- };
+ var listingDonePort = new ReceivePort();
+ directory.list(recursive: true).listen(
+ (FileSystemEntity entity) {
+ if (entity is File) {
+ var path = entity.name;
+ listedFile = true;
+ Expect.isTrue(path.contains(directory.path));
+ Expect.isTrue(path.contains('subdir'));
+ Expect.isTrue(path.contains('file.txt'));
+ } else {
+ var path = entity.path;
+ Expect.isTrue(entity is Directory);
+ listedDir = true;
+ Expect.isTrue(path.contains(directory.path));
+ Expect.isTrue(path.contains('subdir'));
+ }
+ },
+ onDone: () {
+ Expect.isTrue(listedDir, "directory not found");
+ Expect.isTrue(listedFile, "file not found");
+ directory.delete(recursive: true).then((ignore) {
+ f.exists().then((exists) => Expect.isFalse(exists));
+ directory.exists().then((exists) => Expect.isFalse(exists));
+ subDirectory.exists().then((exists) => Expect.isFalse(exists));
+ listingDonePort.close();
+ });
+ });
// Listing is asynchronous, so nothing should be listed at this
// point.
@@ -83,20 +85,13 @@
}
static void testListNonExistent() {
- setupListerHandlers(DirectoryLister lister) {
- // Test that listing a non-existing directory fails.
- lister.onError = (e) {
- Expect.isTrue(e is DirectoryIOException);
- };
- lister.onFile = (file) {
- Expect.fail("Listing of non-existing directory should fail");
- };
- lister.onDir = (dir) {
- Expect.fail("Listing of non-existing directory should fail");
- };
- lister.onDone = (success) {
- Expect.isFalse(success);
- };
+ setupListerHandlers(Stream<FileSystemEntity> stream) {
+ stream.listen(
+ (_) => Expect.fail("Listing of non-existing directory should fail"),
+ onError: (e) {
+ Expect.isTrue(e is AsyncError);
+ Expect.isTrue(e.error is DirectoryIOException);
+ });
}
new Directory("").createTemp().then((d) {
d.delete().then((ignore) {
@@ -109,22 +104,19 @@
static void testListTooLongName() {
new Directory("").createTemp().then((d) {
var errors = 0;
- setupListHandlers(DirectoryLister lister) {
- lister.onError = (e) {
- Expect.isTrue(e is DirectoryIOException);
- if (++errors == 2) {
- d.delete(recursive: true);
- }
- };
- lister.onFile = (file) {
- Expect.fail("Listing of non-existing directory should fail");
- };
- lister.onDir = (dir) {
- Expect.fail("Listing of non-existing directory should fail");
- };
- lister.onDone = (success) {
- Expect.isFalse(success);
- };
+ var port = new ReceivePort();
+ setupListHandlers(Stream<FileSystemEntity> stream) {
+ stream.listen(
+ (_) => Expect.fail("Listing of non-existing directory should fail"),
+ onError: (e) {
+ Expect.isTrue(e is AsyncError);
+ Expect.isTrue(e.error is DirectoryIOException);
+ if (++errors == 2) {
+ d.delete(recursive: true).then((_) {
+ port.close();
+ });
+ }
+ });
}
var subDirName = 'subdir';
var subDir = new Directory("${d.path}/$subDirName");
@@ -214,45 +206,6 @@
d.deleteSync(recursive: true);
}
- static void testDeleteSymlink() {
- // temp/
- // a/
- // file.txt
- // b/
- // a_link -> a
- var d = new Directory("").createTempSync();
- var a = new Directory("${d.path}/a");
- a.createSync();
-
- var b = new Directory("${d.path}/b");
- b.createSync();
-
- var f = new File("${d.path}/a/file.txt");
- f.createSync();
- Expect.isTrue(f.existsSync());
-
- // Create a symlink (or junction on Windows) from
- // temp/b/a_link to temp/a.
- var cmd = "ln";
- var args = ['-s', "${d.path}/b/a_link", "${d.path}/a"];
-
- if (Platform.operatingSystem == "windows") {
- cmd = "cmd";
- args = ["/c", "mklink", "/j", "${d.path}\\b\\a_link", "${d.path}\\a"];
- }
-
- Process.run(cmd, args).then((_) {
- // Delete the directory containing the junction.
- b.deleteSync(recursive: true);
-
- // We should not have recursed through a_link into a.
- Expect.isTrue(f.existsSync());
-
- // Clean up after ourselves.
- d.deleteSync(recursive: true);
- });
- }
-
static void testExistsCreateDelete() {
new Directory("").createTemp().then((d) {
d.exists().then((bool exists) {
@@ -359,7 +312,6 @@
testDeleteTooLongName();
testDeleteNonExistentSync();
testDeleteTooLongNameSync();
- testDeleteSymlink();
testExistsCreateDelete();
testExistsCreateDeleteSync();
testCreateTemp();
diff --git a/tests/standalone/io/echo_server_stream_test.dart b/tests/standalone/io/echo_server_stream_test.dart
index a350b59..3a59aba 100644
--- a/tests/standalone/io/echo_server_stream_test.dart
+++ b/tests/standalone/io/echo_server_stream_test.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
//
@@ -34,83 +34,44 @@
}
void sendData() {
+ int offset = 0;
+ List<int> data;
- void errorHandler(Exception e) {
+ void onData(List<int> data) {
+ int bytesRead = data.length;
+ for (int i = 0; i < data.length; i++) {
+ Expect.equals(FIRSTCHAR + i + offset, data[i]);
+ }
+ offset += bytesRead;
+ }
+
+ void onClosed() {
+ Expect.equals(MSGSIZE, offset);
+ _messages++;
+ if (_messages < MESSAGES) {
+ sendData();
+ } else {
+ shutdown();
+ }
+ }
+
+ void errorHandler(e) {
Expect.fail("Socket error $e");
}
void connectHandler() {
-
- OutputStream stream = _socket.outputStream;
-
- void dataSent() {
- InputStream inputStream = _socket.inputStream;
- int offset = 0;
- List<int> data;
-
- void onClosed() {
- Expect.equals(MSGSIZE, offset);
- _messages++;
- if (_messages < MESSAGES) {
- sendData();
- } else {
- shutdown();
- }
- }
-
- void onData() {
- // Test both read and readInto.
- int bytesRead = 0;
- if (_messages % 2 == 0) {
- bytesRead = inputStream.readInto(data, offset, MSGSIZE - offset);
- for (int i = 0; i < offset + bytesRead; i++) {
- Expect.equals(FIRSTCHAR + i, data[i]);
- }
- } else {
- data = inputStream.read();
- bytesRead = data.length;
- for (int i = 0; i < data.length; i++) {
- Expect.equals(FIRSTCHAR + i + offset, data[i]);
- }
- }
-
- offset += bytesRead;
- }
-
- if (_messages % 2 == 0) data = new List<int>.fixedLength(MSGSIZE);
- inputStream.onData = onData;
- inputStream.onClosed = onClosed;
- }
-
- _socket.onError = errorHandler;
-
- // Test both write and writeFrom in different forms.
- switch (_messages % 4) {
- case 0:
- stream.write(_buffer);
- break;
- case 1:
- stream.write(_buffer, false);
- break;
- case 2:
- stream.writeFrom(_buffer);
- break;
- case 3:
- Expect.equals(0, _buffer.length % 2);
- stream.writeFrom(_buffer, 0, _buffer.length ~/ 2);
- stream.writeFrom(_buffer, _buffer.length ~/ 2);
- break;
- }
- stream.close();
- dataSent();
+ _socket.listen(onData,
+ onError: errorHandler,
+ onDone: onClosed);
+ _socket.add(_buffer);
+ _socket.close();
+ data = new List<int>.fixedLength(MSGSIZE);
}
- _socket = new Socket(TestingServer.HOST, _port);
- if (_socket != null) {
- _socket.onConnect = connectHandler;
- } else {
- Expect.fail("socket creation failed");
- }
+ Socket.connect(TestingServer.HOST, _port).then((s) {
+ _socket = s;
+ connectHandler();
+ });
}
void initialize() {
@@ -146,33 +107,30 @@
static const int MSGSIZE = EchoServerGame.MSGSIZE;
void onConnection(Socket connection) {
- InputStream inputStream;
List<int> buffer = new List<int>.fixedLength(MSGSIZE);
int offset = 0;
- void dataReceived() {
+ void dataReceived(List<int> data) {
int bytesRead;
- OutputStream outputStream = connection.outputStream;
- bytesRead = inputStream.readInto(buffer, offset, MSGSIZE - offset);
+ bytesRead = data.length;
if (bytesRead > 0) {
+ buffer.setRange(offset, data.length, data);
offset += bytesRead;
for (int i = 0; i < offset; i++) {
Expect.equals(EchoServerGame.FIRSTCHAR + i, buffer[i]);
}
if (offset == MSGSIZE) {
- outputStream.write(buffer);
- outputStream.close();
+ connection.add(buffer);
+ connection.close();
}
}
}
- void errorHandler(Exception e) {
+ void errorHandler(e) {
Expect.fail("Socket error $e");
}
- inputStream = connection.inputStream;
- inputStream.onData = dataReceived;
- connection.onError = errorHandler;
+ connection.listen(dataReceived, onError: errorHandler);
}
}
diff --git a/tests/standalone/io/echo_server_test.dart b/tests/standalone/io/echo_server_test.dart
deleted file mode 100644
index 4208bb8..0000000
--- a/tests/standalone/io/echo_server_test.dart
+++ /dev/null
@@ -1,203 +0,0 @@
-// 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.
-//
-// Echo server test program for testing sockets.
-//
-// VMOptions=
-// VMOptions=--short_socket_read
-// VMOptions=--short_socket_write
-// VMOptions=--short_socket_read --short_socket_write
-
-library ServerTest;
-import "dart:io";
-import "dart:isolate";
-part "testing_server.dart";
-
-class EchoServerTest {
-
- static void testMain() {
- EchoServerGame echoServerGame = new EchoServerGame.start();
- }
-}
-
-class EchoServerGame {
-
- static const MSGSIZE = 10;
- static const MESSAGES = 100;
- static const FIRSTCHAR = 65;
-
- EchoServerGame.start()
- : _receivePort = new ReceivePort(),
- _sendPort = null,
- _buffer = new List<int>.fixedLength(MSGSIZE),
- _messages = 0 {
- for (int i = 0; i < MSGSIZE; i++) {
- _buffer[i] = FIRSTCHAR + i;
- }
- _sendPort = spawnFunction(startEchoServer);
- initialize();
- }
-
- void sendData() {
- Socket _socket;
-
- void messageHandler() {
-
- List<int> bufferReceived = new List<int>.fixedLength(MSGSIZE);
- int bytesRead = 0;
-
- void handleRead() {
- bytesRead += _socket.readList(
- bufferReceived, bytesRead, MSGSIZE - bytesRead);
- if (bytesRead < MSGSIZE) {
- // We check every time the whole buffer to verify data integrity.
- for (int i = 0; i < bytesRead; i++) {
- Expect.equals(FIRSTCHAR + i, bufferReceived[i]);
- }
- _socket.onData = handleRead;
- } else {
- // We check every time the whole buffer to verify data integrity.
- for (int i = 0; i < MSGSIZE; i++) {
- Expect.equals(FIRSTCHAR + i, bufferReceived[i]);
- }
- _messages++;
- _socket.close();
- if (_messages < MESSAGES) {
- sendData();
- } else {
- shutdown();
- }
- }
- }
-
- handleRead();
- }
-
- void errorHandler(Exception e) {
- Expect.fail("Socket error $e");
- }
-
- void connectHandler() {
-
- void writeMessage() {
- int bytesWritten = 0;
-
- void handleWrite() {
- bytesWritten += _socket.writeList(
- _buffer, bytesWritten, MSGSIZE - bytesWritten);
- if (bytesWritten < MSGSIZE) {
- _socket.onWrite = handleWrite;
- }
- }
-
- handleWrite();
- }
-
- _socket.onData = messageHandler;
- _socket.onError = errorHandler;
- writeMessage();
- }
-
- _socket = new Socket(TestingServer.HOST, _port);
- if (_socket != null) {
- _socket.onConnect = connectHandler;
- } else {
- Expect.fail("Socket creation failed");
- }
- }
-
- void initialize() {
- _receivePort.receive((var message, SendPort replyTo) {
- _port = message;
- sendData();
- });
- _sendPort.send(TestingServer.INIT, _receivePort.toSendPort());
- }
-
- void shutdown() {
- _sendPort.send(TestingServer.SHUTDOWN, _receivePort.toSendPort());
- _receivePort.close();
- }
-
- int _port;
- ReceivePort _receivePort;
- SendPort _sendPort;
- List<int> _buffer;
- int _messages;
-}
-
-
-void startEchoServer() {
- var server = new EchoServer();
- port.receive(server.dispatch);
-}
-
-class EchoServer extends TestingServer {
-
- static const msgSize = EchoServerGame.MSGSIZE;
-
- void onConnection(Socket connection) {
-
- void messageHandler() {
-
- List<int> buffer = new List<int>.fixedLength(msgSize);
- int bytesRead = 0;
-
- void handleRead() {
- int read = connection.readList(buffer, bytesRead, msgSize - bytesRead);
- if (read > 0) {
- bytesRead += read;
- if (bytesRead < msgSize) {
- // We check every time the whole buffer to verify data integrity.
- for (int i = 0; i < bytesRead; i++) {
- Expect.equals(EchoServerGame.FIRSTCHAR + i, buffer[i]);
- }
- connection.onData = handleRead;
- } else {
- // We check every time the whole buffer to verify data integrity.
- for (int i = 0; i < msgSize; i++) {
- Expect.equals(EchoServerGame.FIRSTCHAR + i, buffer[i]);
- }
-
- void writeMessage() {
-
- int bytesWritten = 0;
-
- void handleWrite() {
- int written = connection.writeList(
- buffer, bytesWritten, msgSize - bytesWritten);
- bytesWritten += written;
- if (bytesWritten < msgSize) {
- connection.onWrite = handleWrite;
- } else {
- connection.close(true);
- }
- }
- handleWrite();
- }
- writeMessage();
- }
- }
- }
-
- handleRead();
- }
-
- void closeHandler() {
- connection.close();
- }
-
- void errorHandler(Exception e) {
- Expect.fail("Socket error $e");
- }
-
- connection.onData = messageHandler;
- connection.onClosed = closeHandler;
- connection.onError = errorHandler;
- }
-}
-
-main() {
- EchoServerTest.testMain();
-}
diff --git a/tests/standalone/io/file_fuzz_test.dart b/tests/standalone/io/file_fuzz_test.dart
index 2d02795..ffc32d9 100644
--- a/tests/standalone/io/file_fuzz_test.dart
+++ b/tests/standalone/io/file_fuzz_test.dart
@@ -22,13 +22,13 @@
doItSync(f.lengthSync);
doItSync(f.modifiedSync);
doItSync(f.fullPathSync);
- doItSync(() => f.openInputStream().onError = (e) => null);
+ doItSync(() => f.openRead().listen(() {}, onError: (e) {}));
doItSync(f.readAsBytesSync);
doItSync(f.readAsStringSync);
doItSync(f.readAsLinesSync);
typeMapping.forEach((k2, v2) {
doItSync(() => f.openSync(v2));
- doItSync(() => f.openOutputStream(v2).onError = (e) => null);
+ doItSync(() => f.openWrite(v2));
doItSync(() => f.readAsStringSync(v2));
doItSync(() => f.readAsLinesSync(v2));
});
diff --git a/tests/standalone/io/file_input_stream_test.dart b/tests/standalone/io/file_input_stream_test.dart
index c46bd42..4e2e2c4 100644
--- a/tests/standalone/io/file_input_stream_test.dart
+++ b/tests/standalone/io/file_input_stream_test.dart
@@ -11,14 +11,15 @@
String getFilename(String path) =>
new File(path).existsSync() ? path : '../$path';
-void testStringInputStreamSync() {
+void testStringLineTransformer() {
String fileName = getFilename("tests/standalone/io/readuntil_test.dat");
// File contains "Hello Dart\nwassup!\n"
File file = new File(fileName);
int linesRead = 0;
- StringInputStream x = new StringInputStream(file.openInputStream());
- x.onLine = () {
- String line = x.readLine();
+ var lineStream = file.openRead()
+ .transform(new StringDecoder())
+ .transform(new LineTransformer());
+ lineStream.listen((line) {
linesRead++;
if (linesRead == 1) {
Expect.equals("Hello Dart", line);
@@ -27,101 +28,179 @@
} else {
Expect.fail("More or less than 2 lines read ($linesRead lines read).");
}
- };
+ });
}
-void testInputStreamAsync() {
+
+void testOpenStreamAsync() {
+ var keepAlive = new ReceivePort();
String fileName = getFilename("tests/standalone/io/readuntil_test.dat");
// File contains "Hello Dart\nwassup!\n"
var expected = "Hello Dart\nwassup!\n".charCodes;
- InputStream x = (new File(fileName)).openInputStream();
var byteCount = 0;
- x.onData = () {
- Expect.equals(expected[byteCount], x.read(1)[0]);
- byteCount++;
- };
- x.onClosed = () {
- Expect.equals(expected.length, byteCount);
- };
+ (new File(fileName)).openRead().listen(
+ (d) => byteCount += d.length,
+ onDone: () {
+ Expect.equals(expected.length, byteCount);
+ keepAlive.close();
+ });
}
-void testStringInputStreamAsync(String name, int length) {
+
+// Create a file that is big enough that a file stream will
+// read it in multiple chunks.
+int writeLongFileSync(File file) {
+ file.createSync();
+ StringBuffer buffer = new StringBuffer();
+ for (var i = 0; i < 10000; i++) {
+ buffer.add("Hello, world");
+ }
+ file.writeAsStringSync(buffer.toString());
+ var length = file.lengthSync();
+ Expect.equals(buffer.length, length);
+ return length;
+}
+
+
+void testInputStreamTruncate() {
+ var keepAlive = new ReceivePort();
+ var temp = new Directory('').createTempSync();
+ var file = new File('${temp.path}/input_stream_truncate.txt');
+ var originalLength = writeLongFileSync(file);
+ // Start streaming the file. Pause after first chunk. Truncate
+ // underlying file and check that the streaming stops with or
+ // without getting all data.
+ var streamedBytes = 0;
+ var subscription;
+ subscription = file.openRead().listen(
+ (d) {
+ if (streamedBytes == 0) {
+ subscription.pause();
+ // Truncate the file by opening it for writing.
+ file.open(FileMode.WRITE).then((opened) {
+ opened.close().then((_) {
+ Expect.equals(0, file.lengthSync());
+ subscription.resume();
+ });
+ });
+ }
+ streamedBytes += d.length;
+ },
+ onDone: () {
+ Expect.isTrue(streamedBytes > 0 && streamedBytes <= originalLength);
+ temp.delete(recursive: true).then((_) => keepAlive.close());
+ },
+ onError: (e) {
+ Expect.fail("Unexpected error");
+ });
+}
+
+
+void testInputStreamDelete() {
+ var keepAlive = new ReceivePort();
+ var temp = new Directory('').createTempSync();
+ var file = new File('${temp.path}/input_stream_delete.txt');
+ var originalLength = writeLongFileSync(file);
+ // Start streaming the file. Pause after first chunk. Truncate
+ // underlying file and check that the streaming stops with or
+ // without getting all data.
+ var streamedBytes = 0;
+ var subscription;
+ subscription = file.openRead().listen(
+ (d) {
+ if (streamedBytes == 0) {
+ subscription.pause();
+ // Delete the underlying file by opening it for writing.
+ file.delete()
+ .then((deleted) {
+ Expect.isFalse(deleted.existsSync());
+ subscription.resume();
+ })
+ .catchError((e) {
+ // On Windows, you cannot delete a file that is open
+ // somewhere else. The stream has this file open
+ // and therefore we get an error on deletion on Windows.
+ Expect.equals('windows', Platform.operatingSystem);
+ subscription.resume();
+ });
+ }
+ streamedBytes += d.length;
+ },
+ onDone: () {
+ Expect.equals(originalLength, streamedBytes);
+ temp.delete(recursive: true).then((_) => keepAlive.close());
+ },
+ onError: (e) {
+ Expect.fail("Unexpected error");
+ });
+}
+
+
+void testInputStreamAppend() {
+ var keepAlive = new ReceivePort();
+ var temp = new Directory('').createTempSync();
+ var file = new File('${temp.path}/input_stream_append.txt');
+ var originalLength = writeLongFileSync(file);
+ // Start streaming the file. Pause after first chunk. Append to
+ // underlying file and check that the stream gets all the data.
+ var streamedBytes = 0;
+ var subscription;
+ subscription = file.openRead().listen(
+ (d) {
+ if (streamedBytes == 0) {
+ subscription.pause();
+ // Double the length of the underlying file.
+ file.readAsBytes().then((bytes) {
+ file.writeAsBytes(bytes, FileMode.APPEND).then((_) {
+ Expect.equals(2 * originalLength, file.lengthSync());
+ subscription.resume();
+ });
+ });
+ }
+ streamedBytes += d.length;
+ },
+ onDone: () {
+ Expect.equals(2 * originalLength, streamedBytes);
+ temp.delete(recursive: true).then((_) => keepAlive.close());
+ },
+ onError: (e) {
+ Expect.fail("Unexpected error");
+ });
+}
+
+
+void testStringLineTransformerEnding(String name, int length) {
String fileName = getFilename("tests/standalone/io/$name");
// File contains 10 lines.
File file = new File(fileName);
Expect.equals(length, file.openSync().lengthSync());
- StringInputStream x = new StringInputStream(file.openInputStream());
+ var lineStream = file.openRead()
+ .transform(new StringDecoder())
+ .transform(new LineTransformer());
int lineCount = 0;
- x.onLine = () {
- var line = x.readLine();
- lineCount++;
- Expect.isTrue(lineCount <= 10);
- if (line[0] != "#") {
- Expect.equals("Line $lineCount", line);
- }
- };
- x.onClosed = () {
- Expect.equals(10, lineCount);
- };
-}
-
-void testChunkedInputStream() {
- // Force the test to timeout if it does not finish.
- ReceivePort done = new ReceivePort();
- done.receive((message, replyTo) { done.close(); });
-
- String fileName = getFilename("tests/standalone/io/readuntil_test.dat");
- // File contains 19 bytes ("Hello Dart\nwassup!")
- File file = new File(fileName);
- ChunkedInputStream x = new ChunkedInputStream(file.openInputStream());
- x.chunkSize = 9;
- x.onData = () {
- List<int> chunk = x.read();
- Expect.equals(9, chunk.length);
- x.chunkSize = 5;
- x.onData = () {
- chunk = x.read();
- Expect.equals(5, chunk.length);
- x.onData = () {
- chunk = x.read();
- Expect.equals(5, chunk.length);
- chunk = x.read();
- Expect.equals(null, chunk);
- done.toSendPort().send(null);
- };
- };
- };
-}
-
-void testUnreadyInputStream() {
- String fileName = getFilename("tests/standalone/io/readuntil_test.dat");
- var expected = "Hello Dart\nwassup!\n".charCodes;
- InputStream x = (new File(fileName)).openInputStream();
- List<int> buffer = new List<int>.fixedLength(100);
-
- x.onData = () {
- Expect.fail("Input stream closed before opening called onData handler.");
- };
-
- x.onClosed = () { };
-
- // Called before stream is ready.
- int read = x.readInto(buffer);
- Expect.equals(0, read);
-
- // Called before stream is ready.
- x.close();
+ lineStream.listen(
+ (line) {
+ lineCount++;
+ Expect.isTrue(lineCount <= 10);
+ if (line[0] != "#") {
+ Expect.equals("Line $lineCount", line);
+ }
+ },
+ onDone: () {
+ Expect.equals(10, lineCount);
+ });
}
main() {
- testStringInputStreamSync();
- testInputStreamAsync();
+ testStringLineTransformer();
+ testOpenStreamAsync();
+ testInputStreamTruncate();
+ testInputStreamDelete();
+ testInputStreamAppend();
// Check the length of these files as both are text files where one
// is without a terminating line separator which can easily be added
// back if accidentally opened in a text editor.
- testStringInputStreamAsync("readline_test1.dat", 111);
- testStringInputStreamAsync("readline_test2.dat", 114);
- testChunkedInputStream();
- testUnreadyInputStream();
+ testStringLineTransformerEnding("readline_test1.dat", 111);
+ testStringLineTransformerEnding("readline_test2.dat", 114);
}
diff --git a/tests/standalone/io/file_output_stream_test.dart b/tests/standalone/io/file_output_stream_test.dart
index e700c5c..645399a 100644
--- a/tests/standalone/io/file_output_stream_test.dart
+++ b/tests/standalone/io/file_output_stream_test.dart
@@ -19,88 +19,18 @@
String fileName = "${tempDirectory.path}/test";
File file = new File(fileName);
file.createSync();
- OutputStream x = file.openOutputStream();
- x.write([65, 66, 67]);
- Expect.isFalse(x.closed);
+ IOSink x = file.openWrite();
+ var data = [65, 66, 67];
+ x.add(data);
x.close();
- Expect.isTrue(x.closed);
- x.onClosed = () {
- Expect.isTrue(x.closed);
+ x.done.then((_) {
+ Expect.listEquals(file.readAsBytesSync(), data);
file.deleteSync();
done.toSendPort().send("done");
- };
-}
-
-
-void testOutputStreamNoPendingWrite() {
- var tempDirectory;
-
- // Create a port for waiting on the final result of this test.
- ReceivePort done = new ReceivePort();
- done.receive((message, replyTo) {
- tempDirectory.delete(recursive: true).then((ignore) => done.close());
- });
-
- new Directory('').createTemp().then((temp) {
- tempDirectory = temp;
- String fileName = "${tempDirectory.path}/test";
- File file = new File(fileName);
- file.create().then((ignore) {
- OutputStream stream = file.openOutputStream();
- final total = 100;
- var count = 0;
- stream.onNoPendingWrites = () {
- stream.write([count++]);
- if (count == total) {
- stream.close();
- }
- stream.onClosed = () {
- List buffer = new List<int>.fixedLength(total);
- File fileSync = new File(fileName);
- var openedFile = fileSync.openSync();
- openedFile.readListSync(buffer, 0, total);
- for (var i = 0; i < total; i++) {
- Expect.equals(i, buffer[i]);
- }
- openedFile.closeSync();
- fileSync.deleteSync();
- done.toSendPort().send("done");
- };
- };
- });
});
}
-void testOutputStreamFlush() {
- Directory tempDirectory = new Directory('').createTempSync();
-
- // Create a port for waiting on the final result of this test.
- ReceivePort done = new ReceivePort();
- done.receive((message, replyTo) {
- tempDirectory.deleteSync();
- done.close();
- });
-
- String fileName = "${tempDirectory.path}/test";
- File file = new File(fileName);
- file.createSync();
- OutputStream x = file.openOutputStream();
- x.write([65, 66, 67]);
- x.flush();
- x.write([68, 69, 70]);
- x.flush();
- x.write([71, 72, 73]);
- x.onClosed = () {
- file.deleteSync();
- done.toSendPort().send("done");
- };
- x.close();
- x.onError = (e) => Expect.fail("No error expected");
-}
-
main() {
testOpenOutputStreamSync();
- testOutputStreamNoPendingWrite();
- testOutputStreamFlush();
}
diff --git a/tests/standalone/io/file_system_links_test.dart b/tests/standalone/io/file_system_links_test.dart
index 361f7e6..90e9692 100644
--- a/tests/standalone/io/file_system_links_test.dart
+++ b/tests/standalone/io/file_system_links_test.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import "dart:io";
+import "dart:isolate";
createLink(String dst, String link, bool symbolic, void callback()) {
@@ -66,16 +67,16 @@
new File(x).createSync();
createLink(x, y, true, () {
var data = "asdf".charCodes;
- var output = new File(y).openOutputStream(FileMode.WRITE);
- output.write(data);
- output.onNoPendingWrites = () {
- output.close();
+ var output = new File(y).openWrite(FileMode.WRITE);
+ output.add(data);
+ output.close();
+ output.done.then((_) {
var read = new File(y).readAsBytesSync();
Expect.listEquals(data, read);
var read2 = new File(x).readAsBytesSync();
Expect.listEquals(data, read2);
temp.deleteSync(recursive: true);
- };
+ });
});
}
@@ -119,6 +120,7 @@
testDirectoryListing() {
+ var keepAlive = new ReceivePort();
var temp = new Directory('').createTempSync();
var temp2 = new Directory('').createTempSync();
var y = '${temp.path}${Platform.pathSeparator}y';
@@ -136,29 +138,36 @@
}
}
Expect.equals(1, files.length);
- Expect.isTrue(files[0].endsWith(x));
+ Expect.isTrue(files[0].endsWith('$y${Platform.pathSeparator}x'));
Expect.equals(1, dirs.length);
Expect.isTrue(dirs[0].endsWith(y));
files = [];
dirs = [];
- var lister = temp.list(recursive: true);
- lister.onFile = (f) => files.add(f);
- lister.onDir = (d) => dirs.add(d);
- lister.onDone = (success) {
- Expect.isTrue(success);
- Expect.equals(1, files.length);
- Expect.isTrue(files[0].endsWith(x));
- Expect.equals(1, dirs.length);
- Expect.isTrue(dirs[0].endsWith(y));
- temp.deleteSync(recursive: true);
- temp2.deleteSync(recursive: true);
- };
+ var lister = temp.list(recursive: true).listen(
+ (entity) {
+ if (entity is File) {
+ files.add(entity.name);
+ } else {
+ Expect.isTrue(entity is Directory);
+ dirs.add(entity.path);
+ }
+ },
+ onDone: () {
+ Expect.equals(1, files.length);
+ Expect.isTrue(files[0].endsWith('$y${Platform.pathSeparator}x'));
+ Expect.equals(1, dirs.length);
+ Expect.isTrue(dirs[0].endsWith(y));
+ temp.deleteSync(recursive: true);
+ temp2.deleteSync(recursive: true);
+ keepAlive.close();
+ });
});
}
testDirectoryListingBrokenLink() {
+ var keepAlive = new ReceivePort();
var temp = new Directory('').createTempSync();
var x = '${temp.path}${Platform.pathSeparator}x';
var link = '${temp.path}${Platform.pathSeparator}link';
@@ -170,19 +179,25 @@
var files = [];
var dirs = [];
var errors = [];
- var lister = temp.list(recursive: true);
- lister.onFile = (f) => files.add(f);
- lister.onDir = (d) => dirs.add(d);
- lister.onError = (d) => errors.add(d);
- lister.onDone = (success) {
- Expect.isFalse(success);
- Expect.equals(1, files.length);
- Expect.isTrue(files[0].endsWith(x));
- Expect.equals(0, dirs.length);
- Expect.equals(1, errors.length);
- Expect.isTrue(errors[0].toString().contains(link));
- temp.deleteSync(recursive: true);
- };
+ temp.list(recursive: true).listen(
+ (entity) {
+ if (entity is File) {
+ files.add(entity.name);
+ } else {
+ Expect.isTrue(entity is Directory);
+ dirs.add(entity.path);
+ }
+ },
+ onError: (e) => errors.add(e),
+ onDone: () {
+ Expect.equals(1, files.length);
+ Expect.isTrue(files[0].endsWith(x));
+ Expect.equals(0, dirs.length);
+ Expect.equals(1, errors.length);
+ Expect.isTrue(errors[0].toString().contains(link));
+ temp.deleteSync(recursive: true);
+ keepAlive.close();
+ });
});
}
diff --git a/tests/standalone/io/file_test.dart b/tests/standalone/io/file_test.dart
index 4d7093d..a8daa32 100644
--- a/tests/standalone/io/file_test.dart
+++ b/tests/standalone/io/file_test.dart
@@ -46,27 +46,27 @@
String filename = getFilename("bin/file_test.cc");
File file = new File(filename);
Expect.isTrue('$file'.contains(file.name));
- InputStream input = file.openInputStream();
- input.onData = () {
- List<int> buffer = new List<int>.fixedLength(42);
- int bytesRead = input.readInto(buffer, 0, 12);
- Expect.equals(12, bytesRead);
- bytesRead = input.readInto(buffer, 12, 30);
- input.close();
- Expect.equals(30, bytesRead);
- Expect.equals(47, buffer[0]); // represents '/' in the file.
- Expect.equals(47, buffer[1]); // represents '/' in the file.
- Expect.equals(32, buffer[2]); // represents ' ' in the file.
- Expect.equals(67, buffer[3]); // represents 'C' in the file.
- Expect.equals(111, buffer[4]); // represents 'o' in the file.
- Expect.equals(112, buffer[5]); // represents 'p' in the file.
- Expect.equals(121, buffer[6]); // represents 'y' in the file.
- Expect.equals(114, buffer[7]); // represents 'r' in the file.
- Expect.equals(105, buffer[8]); // represents 'i' in the file.
- 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.
- };
+ var subscription;
+ List<int> buffer = new List<int>();
+ subscription = file.openRead().listen(
+ (d) {
+ buffer.addAll(d);
+ if (buffer.length >= 12) {
+ subscription.cancel();
+ Expect.equals(47, buffer[0]); // represents '/' in the file.
+ Expect.equals(47, buffer[1]); // represents '/' in the file.
+ Expect.equals(32, buffer[2]); // represents ' ' in the file.
+ Expect.equals(67, buffer[3]); // represents 'C' in the file.
+ Expect.equals(111, buffer[4]); // represents 'o' in the file.
+ Expect.equals(112, buffer[5]); // represents 'p' in the file.
+ Expect.equals(121, buffer[6]); // represents 'y' in the file.
+ Expect.equals(114, buffer[7]); // represents 'r' in the file.
+ Expect.equals(105, buffer[8]); // represents 'i' in the file.
+ 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.
+ }
+ });
}
// Test for file read and write functionality.
@@ -76,94 +76,44 @@
// Read a file.
String inFilename = getFilename("tests/vm/data/fixed_length_file");
File file;
- InputStream input;
int bytesRead;
- // Test reading all using readInto.
var file1 = new File(inFilename);
- var input1 = file1.openInputStream();
- List<int> buffer1;
- input1.onData = () {
- buffer1 = new List<int>.fixedLength(42);
- bytesRead = input1.readInto(buffer1, 0, 42);
- Expect.equals(42, bytesRead);
- };
- input1.onError = (e) { throw e; };
- input1.onClosed = () {
- Expect.isTrue(input1.closed);
-
- // Test reading all using readInto and read.
- var file2 = new File(inFilename);
- var input2 = file2.openInputStream();
- input2.onData = () {
- bytesRead = input2.readInto(buffer1, 0, 21);
- Expect.equals(21, bytesRead);
- buffer1 = input2.read();
- Expect.equals(21, buffer1.length);
- };
- input2.onError = (e) { throw e; };
- input2.onClosed = () {
- Expect.isTrue(input2.closed);
-
- // Test reading all using read and readInto.
- var file3 = new File(inFilename);
- var input3 = file3.openInputStream();
- input3.onData = () {
- buffer1 = input3.read(21);
- Expect.equals(21, buffer1.length);
- bytesRead = input3.readInto(buffer1, 0, 21);
- Expect.equals(21, bytesRead);
- };
- input3.onError = (e) { throw e; };
- input3.onClosed = () {
- Expect.isTrue(input3.closed);
-
- // Test reading all using read.
- var file4 = new File(inFilename);
- var input4 = file4.openInputStream();
- input4.onData = () {
- buffer1 = input4.read();
- Expect.equals(42, buffer1.length);
- };
- input4.onError = (e) { throw e; };
- input4.onClosed = () {
- Expect.isTrue(input4.closed);
-
- // Write the contents of the file just read into another file.
- String outFilename =
- tempDirectory.path.concat("/out_read_write_stream");
- file = new File(outFilename);
- OutputStream output = file.openOutputStream();
- bool writeDone = output.writeFrom(buffer1, 0, 42);
- Expect.equals(false, writeDone);
- output.onNoPendingWrites = () {
- output.close();
- output.onClosed = () {
- // Now read the contents of the file just written.
- List<int> buffer2 = new List<int>.fixedLength(42);
- var file6 = new File(outFilename);
- var input6 = file6.openInputStream();
- input6.onData = () {
- bytesRead = input6.readInto(buffer2, 0, 42);
- Expect.equals(42, bytesRead);
- // Now compare the two buffers to check if they are identical.
- for (int i = 0; i < buffer1.length; i++) {
- Expect.equals(buffer1[i], buffer2[i]);
- }
- };
- input6.onError = (e) { throw e; };
- input6.onClosed = () {
- // Delete the output file.
- file6.deleteSync();
- Expect.isFalse(file6.existsSync());
- asyncTestDone("testReadWriteStream");
- };
- };
- };
- };
- };
- };
- };
+ List<int> buffer = new List<int>();
+ file1.openRead().listen(
+ (d) {
+ buffer.addAll(d);
+ },
+ onDone: () {
+ Expect.equals(42, buffer.length);
+ // Write the contents of the file just read into another file.
+ String outFilename =
+ tempDirectory.path.concat("/out_read_write_stream");
+ var file2 = new File(outFilename);
+ var output = file2.openWrite();
+ output.add(buffer);
+ output.close();
+ output.done.then((_) {
+ // Now read the contents of the file just written.
+ List<int> buffer2 = new List<int>();
+ new File(outFilename).openRead().listen(
+ (d) {
+ buffer2.addAll(d);
+ },
+ onDone: () {
+ Expect.equals(42, buffer2.length);
+ // Now compare the two buffers to check if they are
+ // identical.
+ for (int i = 0; i < buffer.length; i++) {
+ Expect.equals(buffer[i], buffer2[i]);
+ }
+ // Delete the output file.
+ file2.deleteSync();
+ Expect.isFalse(file2.existsSync());
+ asyncTestDone("testReadWriteStream");
+ });
+ });
+ });
}
// Test for file stream buffered handling of large files.
@@ -178,22 +128,12 @@
String filename =
tempDirectory.path.concat("/out_read_write_stream_large_file");
File file = new File(filename);
- OutputStream output = file.openOutputStream();
- // Test a write immediately after the output stream is created.
- output.writeFrom(buffer, 0, 20000);
-
- output.onNoPendingWrites = () {
- output.writeFrom(buffer, 20000, 60000);
- output.writeFrom(buffer, 80000, 20000);
- output.onNoPendingWrites = () {
- output.writeFrom(buffer, 0, 0);
- output.writeFrom(buffer, 0, 0);
- output.writeFrom(buffer, 0, 100000);
- output.close();
- };
- };
- output.onClosed = () {
- InputStream input = file.openInputStream();
+ IOSink output = file.openWrite();
+ output.add(buffer);
+ output.add(buffer);
+ output.close();
+ output.done.then((_) {
+ Stream input = file.openRead();
int position = 0;
final int expectedLength = 200000;
// Start an independent asynchronous check on the length.
@@ -203,86 +143,55 @@
asyncTestDone('testReadWriteStreamLargeFile: length check');
});
- List<int> inputBuffer =
- new List<int>.fixedLength(expectedLength + 100000);
// Immediate read should read 0 bytes.
- Expect.equals(0, input.available());
- Expect.equals(false, input.closed);
- int bytesRead = input.readInto(inputBuffer);
- Expect.equals(0, bytesRead);
- Expect.equals(0, input.available());
- Expect.isFalse(input.closed);
- input.onError = (e) {
- print('Error handler called on input in testReadWriteStreamLargeFile');
- print('with error $e');
- throw e;
- };
- input.onData = () {
- Expect.isFalse(input.closed);
- bytesRead = input.readInto(inputBuffer, position,
- inputBuffer.length - position);
- position += bytesRead;
- // The buffer is large enough to hold all available data.
- // So there should be no data left to read.
- Expect.equals(0, input.available());
- bytesRead = input.readInto(inputBuffer, position,
- expectedLength - position);
- Expect.equals(0, bytesRead);
- Expect.equals(0, input.available());
- Expect.isFalse(input.closed);
- };
- input.onClosed = () {
- Expect.equals(0, input.available());
- Expect.isTrue(input.closed);
- input.close(); // This should be safe to call.
-
- Expect.equals(expectedLength, position);
- for (int i = 0; i < position; ++i) {
- Expect.equals(buffer[i % buffer.length], inputBuffer[i]);
- }
-
- Future testPipeDone = testPipe(file, buffer);
-
- Future futureDeleted = testPipeDone.then((ignored) => file.delete());
- futureDeleted.then((ignored) {
- asyncTestDone('testReadWriteStreamLargeFile: main test');
- }).catchError((e) {
- print('Exception while deleting ReadWriteStreamLargeFile file');
- print('Exception $e');
+ input.listen(
+ (d) {
+ for (int i = 0; i < d.length; ++i) {
+ Expect.equals(buffer[(i + position) % buffer.length], d[i]);
+ }
+ position += d.length;
+ },
+ onError: (e) {
+ print('Error on input in testReadWriteStreamLargeFile');
+ print('with error $e');
+ throw e;
+ },
+ onDone: () {
+ Expect.equals(expectedLength, position);
+ testPipe(file, buffer)
+ .then((_) => file.delete())
+ .then((_) {
+ asyncTestDone('testReadWriteStreamLargeFile: main test');
+ })
+ .catchError((e) {
+ print('Exception while deleting ReadWriteStreamLargeFile file');
+ print('Exception $e');
+ });
});
- };
- // Try a read again after handlers are set.
- bytesRead = input.readInto(inputBuffer);
- Expect.equals(0, bytesRead);
- Expect.equals(0, input.available());
- Expect.isFalse(input.closed);
- };
+ });
}
static Future testPipe(File file, buffer) {
String outputFilename = '${file.name}_copy';
File outputFile = new File(outputFilename);
- InputStream input = file.openInputStream();
- OutputStream output = outputFile.openOutputStream();
- input.pipe(output);
+ var input = file.openRead();
+ var output = outputFile.openWrite();
Completer done = new Completer();
- output.onClosed = () {
- InputStream copy = outputFile.openInputStream();
+ input.pipe(output).then((_) {
+ var copy = outputFile.openRead();
int position = 0;
- copy.onData = () {
- var data;
- while ((data = copy.read()) != null) {
- for (int value in data) {
- Expect.equals(buffer[position % buffer.length], value);
- position++;
- }
- }
- };
- copy.onClosed = () {
- Expect.equals(2 * buffer.length, position);
- outputFile.delete().then((ignore) { done.complete(null); });
- };
- };
+ copy.listen(
+ (d) {
+ for (int i = 0; i < d.length; i++) {
+ Expect.equals(buffer[(position + i) % buffer.length], d[i]);
+ }
+ position += d.length;
+ },
+ onDone: () {
+ Expect.equals(2 * buffer.length, position);
+ outputFile.delete().then((ignore) { done.complete(); });
+ });
+ });
return done.future;
}
@@ -423,33 +332,28 @@
File file = new File(filename);
file.createSync();
List<int> buffer = content.charCodes;
- OutputStream outStream = file.openOutputStream();
- outStream.write(buffer);
- outStream.onNoPendingWrites = () {
- outStream.close();
- outStream.onClosed = () {
- File file2 = new File(filename);
- OutputStream appendingOutput =
- file2.openOutputStream(FileMode.APPEND);
- appendingOutput.write(buffer);
- appendingOutput.onNoPendingWrites = () {
- appendingOutput.close();
- appendingOutput.onClosed = () {
- File file3 = new File(filename);
- file3.open(FileMode.READ).then((RandomAccessFile openedFile) {
- openedFile.length().then((int length) {
- Expect.equals(content.length * 2, length);
- openedFile.close().then((ignore) {
- file3.delete().then((ignore) {
- asyncTestDone("testOutputStreamWriteAppend");
- });
- });
+ var output = file.openWrite();
+ output.add(buffer);
+ output.close();
+ output.done.then((_) {
+ File file2 = new File(filename);
+ var appendingOutput = file2.openWrite(FileMode.APPEND);
+ appendingOutput.add(buffer);
+ appendingOutput.close();
+ appendingOutput.done.then((_) {
+ File file3 = new File(filename);
+ file3.open(FileMode.READ).then((RandomAccessFile openedFile) {
+ openedFile.length().then((int length) {
+ Expect.equals(content.length * 2, length);
+ openedFile.close().then((ignore) {
+ file3.delete().then((ignore) {
+ asyncTestDone("testOutputStreamWriteAppend");
});
});
- };
- };
- };
- };
+ });
+ });
+ });
+ });
asyncTestStarted();
}
@@ -460,22 +364,20 @@
File file = new File(filename);
file.createSync();
List<int> buffer = content.charCodes;
- OutputStream outStream = file.openOutputStream();
- outStream.writeString("abcdABCD");
- outStream.writeString("abcdABCD", Encoding.UTF_8);
- outStream.writeString("abcdABCD", Encoding.ISO_8859_1);
- outStream.writeString("abcdABCD", Encoding.ASCII);
- outStream.writeString("æøå", Encoding.UTF_8);
- outStream.onNoPendingWrites = () {
- outStream.close();
- outStream.onClosed = () {
- RandomAccessFile raf = file.openSync();
- Expect.equals(38, raf.lengthSync());
- raf.close().then((ignore) {
- asyncTestDone("testOutputStreamWriteString");
- });
- };
- };
+ var output = file.openWrite();
+ output.addString("abcdABCD");
+ output.addString("abcdABCD", Encoding.UTF_8);
+ output.addString("abcdABCD", Encoding.ISO_8859_1);
+ output.addString("abcdABCD", Encoding.ASCII);
+ output.addString("æøå", Encoding.UTF_8);
+ output.close();
+ output.done.then((_) {
+ RandomAccessFile raf = file.openSync();
+ Expect.equals(38, raf.lengthSync());
+ raf.close().then((ignore) {
+ asyncTestDone("testOutputStreamWriteString");
+ });
+ });
asyncTestStarted();
}
@@ -846,18 +748,13 @@
File file =
new File(tempDirectory.path.concat("/out_close_exception_stream"));
file.createSync();
- InputStream input = file.openInputStream();
- input.onClosed = () {
- Expect.isTrue(input.closed);
- Expect.equals(0, input.readInto(buffer, 0, 12));
- OutputStream output = file.openOutputStream();
- output.close();
- Expect.throws(() => output.writeFrom(buffer, 0, 12));
- output.onClosed = () {
- file.deleteSync();
- asyncTestDone("testCloseExceptionStream");
- };
- };
+ var output = file.openWrite();
+ output.close();
+ Expect.throws(() => output.add(buffer));
+ output.done.then((_) {
+ file.deleteSync();
+ asyncTestDone("testCloseExceptionStream");
+ });
}
// Tests buffer out of bounds exception.
@@ -1083,7 +980,10 @@
Expect.equals(6, text.length);
var expected = [955, 120, 46, 32, 120, 10];
Expect.listEquals(expected, text.charCodes);
- Expect.throws(() { new File(name).readAsStringSync(Encoding.ASCII); });
+ text = new File(name).readAsStringSync(Encoding.ASCII);
+ // Default replacement character is '?', char code 63.
+ expected = [63, 63, 120, 46, 32, 120, 10];
+ Expect.listEquals(expected, text.charCodes);
text = new File(name).readAsStringSync(Encoding.ISO_8859_1);
expected = [206, 187, 120, 46, 32, 120, 10];
Expect.equals(7, text.length);
diff --git a/tests/standalone/io/http_advanced_test.dart b/tests/standalone/io/http_advanced_test.dart
index d6e1ad4..e19365f 100644
--- a/tests/standalone/io/http_advanced_test.dart
+++ b/tests/standalone/io/http_advanced_test.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
//
@@ -11,11 +11,11 @@
import 'dart:io';
import 'dart:isolate';
-class TestServerMain {
- TestServerMain()
+class IsolatedHttpServer {
+ IsolatedHttpServer()
: _statusPort = new ReceivePort(),
_serverPort = null {
- _serverPort = spawnFunction(startTestServer);
+ _serverPort = spawnFunction(startIsolatedHttpServer);
}
void setServerStartedHandler(void startedCallback(int port)) {
@@ -31,20 +31,22 @@
});
// Send server start message to the server.
- var command = new TestServerCommand.start();
+ var command = new IsolatedHttpServerCommand.start();
_serverPort.send(command, _statusPort.toSendPort());
}
void shutdown() {
// Send server stop message to the server.
- _serverPort.send(new TestServerCommand.stop(), _statusPort.toSendPort());
+ _serverPort.send(new IsolatedHttpServerCommand.stop(),
+ _statusPort.toSendPort());
_statusPort.close();
}
void chunkedEncoding() {
// Send chunked encoding message to the server.
_serverPort.send(
- new TestServerCommand.chunkedEncoding(), _statusPort.toSendPort());
+ new IsolatedHttpServerCommand.chunkedEncoding(),
+ _statusPort.toSendPort());
}
ReceivePort _statusPort; // Port for receiving messages from the server.
@@ -53,14 +55,14 @@
}
-class TestServerCommand {
+class IsolatedHttpServerCommand {
static const START = 0;
static const STOP = 1;
static const CHUNKED_ENCODING = 2;
- TestServerCommand.start() : _command = START;
- TestServerCommand.stop() : _command = STOP;
- TestServerCommand.chunkedEncoding() : _command = CHUNKED_ENCODING;
+ IsolatedHttpServerCommand.start() : _command = START;
+ IsolatedHttpServerCommand.stop() : _command = STOP;
+ IsolatedHttpServerCommand.chunkedEncoding() : _command = CHUNKED_ENCODING;
bool get isStart => _command == START;
bool get isStop => _command == STOP;
@@ -70,14 +72,14 @@
}
-class TestServerStatus {
+class IsolatedHttpServerStatus {
static const STARTED = 0;
static const STOPPED = 1;
static const ERROR = 2;
- TestServerStatus.started(this._port) : _state = STARTED;
- TestServerStatus.stopped() : _state = STOPPED;
- TestServerStatus.error() : _state = ERROR;
+ IsolatedHttpServerStatus.started(this._port) : _state = STARTED;
+ IsolatedHttpServerStatus.stopped() : _state = STOPPED;
+ IsolatedHttpServerStatus.error() : _state = ERROR;
bool get isStarted => _state == STARTED;
bool get isStopped => _state == STOPPED;
@@ -90,7 +92,7 @@
}
-void startTestServer() {
+void startIsolatedHttpServer() {
var server = new TestServer();
server.init();
port.receive(server.dispatch);
@@ -99,40 +101,45 @@
class TestServer {
// Return a 404.
- void _notFoundHandler(HttpRequest request, HttpResponse response) {
+ void _notFoundHandler(HttpRequest request) {
+ var response = request.response;
response.statusCode = HttpStatus.NOT_FOUND;
response.headers.set("Content-Type", "text/html; charset=UTF-8");
response.outputStream.writeString("Page not found");
- response.outputStream.close();
+ response.close();
}
// Check the "Host" header.
- void _hostHandler(HttpRequest request, HttpResponse response) {
+ void _hostHandler(HttpRequest request) {
+ var response = request.response;
Expect.equals(1, request.headers["Host"].length);
Expect.equals("www.dartlang.org:1234", request.headers["Host"][0]);
Expect.equals("www.dartlang.org", request.headers.host);
Expect.equals(1234, request.headers.port);
response.statusCode = HttpStatus.OK;
- response.outputStream.close();
+ response.close();
}
// Set the "Expires" header using the expires property.
- void _expires1Handler(HttpRequest request, HttpResponse response) {
+ void _expires1Handler(HttpRequest request) {
+ var response = request.response;
DateTime date = new DateTime.utc(1999, DateTime.JUN, 11, 18, 46, 53, 0);
response.headers.expires = date;
Expect.equals(date, response.headers.expires);
- response.outputStream.close();
+ response.close();
}
// Set the "Expires" header.
- void _expires2Handler(HttpRequest request, HttpResponse response) {
+ void _expires2Handler(HttpRequest request) {
+ var response = request.response;
response.headers.set("Expires", "Fri, 11 Jun 1999 18:46:53 GMT");
DateTime date = new DateTime.utc(1999, DateTime.JUN, 11, 18, 46, 53, 0);
Expect.equals(date, response.headers.expires);
- response.outputStream.close();
+ response.close();
}
- void _contentType1Handler(HttpRequest request, HttpResponse response) {
+ void _contentType1Handler(HttpRequest request) {
+ var response = request.response;
Expect.equals("text/html", request.headers.contentType.value);
Expect.equals("text", request.headers.contentType.primaryType);
Expect.equals("html", request.headers.contentType.subType);
@@ -141,10 +148,11 @@
ContentType contentType = new ContentType("text", "html");
contentType.parameters["charset"] = "utf-8";
response.headers.contentType = contentType;
- response.outputStream.close();
+ response.close();
}
- void _contentType2Handler(HttpRequest request, HttpResponse response) {
+ void _contentType2Handler(HttpRequest request) {
+ var response = request.response;
Expect.equals("text/html", request.headers.contentType.value);
Expect.equals("text", request.headers.contentType.primaryType);
Expect.equals("html", request.headers.contentType.subType);
@@ -152,10 +160,12 @@
response.headers.set(HttpHeaders.CONTENT_TYPE,
"text/html; charset = utf-8");
- response.outputStream.close();
+ response.close();
}
- void _cookie1Handler(HttpRequest request, HttpResponse response) {
+ void _cookie1Handler(HttpRequest request) {
+ var response = request.response;
+
// No cookies passed with this request.
Expect.equals(0, request.cookies.length);
@@ -170,84 +180,56 @@
cookie2.domain = ".example.com";
cookie2.path = "/shop";
response.cookies.add(cookie2);
- response.outputStream.close();
+ response.close();
}
- void _cookie2Handler(HttpRequest request, HttpResponse response) {
+ void _cookie2Handler(HttpRequest request) {
+ var response = request.response;
+
// Two cookies passed with this request.
Expect.equals(2, request.cookies.length);
- response.outputStream.close();
- }
-
- void _flushHandler(HttpRequest request, HttpResponse response) {
- response.outputStream.flush();
- response.outputStream.close();
+ response.close();
}
void init() {
// Setup request handlers.
_requestHandlers = new Map();
- _requestHandlers["/host"] =
- (HttpRequest request, HttpResponse response) {
- _hostHandler(request, response);
- };
- _requestHandlers["/expires1"] =
- (HttpRequest request, HttpResponse response) {
- _expires1Handler(request, response);
- };
- _requestHandlers["/expires2"] =
- (HttpRequest request, HttpResponse response) {
- _expires2Handler(request, response);
- };
- _requestHandlers["/contenttype1"] =
- (HttpRequest request, HttpResponse response) {
- _contentType1Handler(request, response);
- };
- _requestHandlers["/contenttype2"] =
- (HttpRequest request, HttpResponse response) {
- _contentType2Handler(request, response);
- };
- _requestHandlers["/cookie1"] =
- (HttpRequest request, HttpResponse response) {
- _cookie1Handler(request, response);
- };
- _requestHandlers["/cookie2"] =
- (HttpRequest request, HttpResponse response) {
- _cookie2Handler(request, response);
- };
- _requestHandlers["/flush"] =
- (HttpRequest request, HttpResponse response) {
- _flushHandler(request, response);
- };
+ _requestHandlers["/host"] = _hostHandler;
+ _requestHandlers["/expires1"] = _expires1Handler;
+ _requestHandlers["/expires2"] = _expires2Handler;
+ _requestHandlers["/contenttype1"] = _contentType1Handler;
+ _requestHandlers["/contenttype2"] = _contentType2Handler;
+ _requestHandlers["/cookie1"] = _cookie1Handler;
+ _requestHandlers["/cookie2"] = _cookie2Handler;
}
void dispatch(message, replyTo) {
if (message.isStart) {
- _server = new HttpServer();
try {
- _server.listen("127.0.0.1", 0);
- _server.defaultRequestHandler = (HttpRequest req, HttpResponse rsp) {
- _requestReceivedHandler(req, rsp);
- };
- replyTo.send(new TestServerStatus.started(_server.port), null);
+ HttpServer.bind().then((server) {
+ _server = server;
+ _server.listen(_requestReceivedHandler);
+ replyTo.send(new IsolatedHttpServerStatus.started(_server.port),
+ null);
+ });
} catch (e) {
- replyTo.send(new TestServerStatus.error(), null);
+ replyTo.send(new IsolatedHttpServerStatus.error(), null);
}
} else if (message.isStop) {
_server.close();
port.close();
- replyTo.send(new TestServerStatus.stopped(), null);
+ replyTo.send(new IsolatedHttpServerStatus.stopped(), null);
} else if (message.isChunkedEncoding) {
_chunkedEncoding = true;
}
}
- void _requestReceivedHandler(HttpRequest request, HttpResponse response) {
- var requestHandler =_requestHandlers[request.path];
+ void _requestReceivedHandler(HttpRequest request) {
+ var requestHandler =_requestHandlers[request.uri.path];
if (requestHandler != null) {
- requestHandler(request, response);
+ requestHandler(request);
} else {
- _notFoundHandler(request, response);
+ _notFoundHandler(request);
}
}
@@ -258,52 +240,52 @@
Future testHost() {
Completer completer = new Completer();
- TestServerMain testServerMain = new TestServerMain();
- testServerMain.setServerStartedHandler((int port) {
+ IsolatedHttpServer server = new IsolatedHttpServer();
+ server.setServerStartedHandler((int port) {
HttpClient httpClient = new HttpClient();
- HttpClientConnection conn =
- httpClient.get("127.0.0.1", port, "/host");
- conn.onRequest = (HttpClientRequest request) {
- Expect.equals("127.0.0.1:$port", request.headers["host"][0]);
- request.headers.host = "www.dartlang.com";
- Expect.equals("www.dartlang.com:$port", request.headers["host"][0]);
- Expect.equals("www.dartlang.com", request.headers.host);
- Expect.equals(port, request.headers.port);
- request.headers.port = 1234;
- Expect.equals("www.dartlang.com:1234", request.headers["host"][0]);
- Expect.equals(1234, request.headers.port);
- request.headers.port = HttpClient.DEFAULT_HTTP_PORT;
- Expect.equals(HttpClient.DEFAULT_HTTP_PORT, request.headers.port);
- Expect.equals("www.dartlang.com", request.headers["host"][0]);
- request.headers.set("Host", "www.dartlang.org");
- Expect.equals("www.dartlang.org", request.headers.host);
- Expect.equals(HttpClient.DEFAULT_HTTP_PORT, request.headers.port);
- request.headers.set("Host", "www.dartlang.org:");
- Expect.equals("www.dartlang.org", request.headers.host);
- Expect.equals(HttpClient.DEFAULT_HTTP_PORT, request.headers.port);
- request.headers.set("Host", "www.dartlang.org:1234");
- Expect.equals("www.dartlang.org", request.headers.host);
- Expect.equals(1234, request.headers.port);
- request.outputStream.close();
- };
- conn.onResponse = (HttpClientResponse response) {
- Expect.equals(HttpStatus.OK, response.statusCode);
- response.inputStream.onData = response.inputStream.read;
- response.inputStream.onClosed = () {
- httpClient.shutdown();
- testServerMain.shutdown();
- completer.complete(true);
- };
- };
+ httpClient.get("127.0.0.1", port, "/host")
+ .then((request) {
+ Expect.equals("127.0.0.1:$port", request.headers["host"][0]);
+ request.headers.host = "www.dartlang.com";
+ Expect.equals("www.dartlang.com:$port", request.headers["host"][0]);
+ Expect.equals("www.dartlang.com", request.headers.host);
+ Expect.equals(port, request.headers.port);
+ request.headers.port = 1234;
+ Expect.equals("www.dartlang.com:1234", request.headers["host"][0]);
+ Expect.equals(1234, request.headers.port);
+ request.headers.port = HttpClient.DEFAULT_HTTP_PORT;
+ Expect.equals(HttpClient.DEFAULT_HTTP_PORT, request.headers.port);
+ Expect.equals("www.dartlang.com", request.headers["host"][0]);
+ request.headers.set("Host", "www.dartlang.org");
+ Expect.equals("www.dartlang.org", request.headers.host);
+ Expect.equals(HttpClient.DEFAULT_HTTP_PORT, request.headers.port);
+ request.headers.set("Host", "www.dartlang.org:");
+ Expect.equals("www.dartlang.org", request.headers.host);
+ Expect.equals(HttpClient.DEFAULT_HTTP_PORT, request.headers.port);
+ request.headers.set("Host", "www.dartlang.org:1234");
+ Expect.equals("www.dartlang.org", request.headers.host);
+ Expect.equals(1234, request.headers.port);
+ return request.close();
+ })
+ .then((response) {
+ Expect.equals(HttpStatus.OK, response.statusCode);
+ response.listen(
+ (_) { },
+ onDone: () {
+ httpClient.close();
+ server.shutdown();
+ completer.complete(true);
+ });
+ });
});
- testServerMain.start();
+ server.start();
return completer.future;
}
Future testExpires() {
Completer completer = new Completer();
- TestServerMain testServerMain = new TestServerMain();
- testServerMain.setServerStartedHandler((int port) {
+ IsolatedHttpServer server = new IsolatedHttpServer();
+ server.setServerStartedHandler((int port) {
int responses = 0;
HttpClient httpClient = new HttpClient();
@@ -313,34 +295,32 @@
response.headers["expires"][0]);
Expect.equals(new DateTime.utc(1999, DateTime.JUN, 11, 18, 46, 53, 0),
response.headers.expires);
- response.inputStream.onData = response.inputStream.read;
- response.inputStream.onClosed = () {
- responses++;
- if (responses == 2) {
- httpClient.shutdown();
- testServerMain.shutdown();
- completer.complete(true);
- }
- };
+ response.listen((_) { },
+ onDone: () {
+ responses++;
+ if (responses == 2) {
+ httpClient.close();
+ server.shutdown();
+ completer.complete(true);
+ }
+ });
}
- HttpClientConnection conn1 = httpClient.get("127.0.0.1", port, "/expires1");
- conn1.onResponse = (HttpClientResponse response) {
- processResponse(response);
- };
- HttpClientConnection conn2 = httpClient.get("127.0.0.1", port, "/expires2");
- conn2.onResponse = (HttpClientResponse response) {
- processResponse(response);
- };
+ httpClient.get("127.0.0.1", port, "/expires1")
+ .then((request) => request.close())
+ .then(processResponse);
+ httpClient.get("127.0.0.1", port, "/expires2")
+ .then((request) => request.close())
+ .then(processResponse);
});
- testServerMain.start();
+ server.start();
return completer.future;
}
Future testContentType() {
Completer completer = new Completer();
- TestServerMain testServerMain = new TestServerMain();
- testServerMain.setServerStartedHandler((int port) {
+ IsolatedHttpServer server = new IsolatedHttpServer();
+ server.setServerStartedHandler((int port) {
int responses = 0;
HttpClient httpClient = new HttpClient();
@@ -353,132 +333,100 @@
Expect.equals("html", response.headers.contentType.subType);
Expect.equals("utf-8",
response.headers.contentType.parameters["charset"]);
- response.inputStream.onData = response.inputStream.read;
- response.inputStream.onClosed = () {
- responses++;
- if (responses == 2) {
- httpClient.shutdown();
- testServerMain.shutdown();
- completer.complete(true);
- }
- };
+ response.listen(
+ (_) { },
+ onDone: () {
+ responses++;
+ if (responses == 2) {
+ httpClient.close();
+ server.shutdown();
+ completer.complete(true);
+ }
+ });
}
- HttpClientConnection conn1 =
- httpClient.get("127.0.0.1", port, "/contenttype1");
- conn1.onRequest = (HttpClientRequest request) {
- ContentType contentType = new ContentType();
- contentType.value = "text/html";
- contentType.parameters["charset"] = "utf-8";
- request.headers.contentType = contentType;
- request.outputStream.close();
- };
- conn1.onResponse = (HttpClientResponse response) {
- processResponse(response);
- };
- HttpClientConnection conn2 =
- httpClient.get("127.0.0.1", port, "/contenttype2");
- conn2.onRequest = (HttpClientRequest request) {
- request.headers.set(HttpHeaders.CONTENT_TYPE,
- "text/html; charset = utf-8");
- request.outputStream.close();
- };
- conn2.onResponse = (HttpClientResponse response) {
- processResponse(response);
- };
+ httpClient.get("127.0.0.1", port, "/contenttype1")
+ .then((request) {
+ ContentType contentType = new ContentType();
+ contentType.value = "text/html";
+ contentType.parameters["charset"] = "utf-8";
+ request.headers.contentType = contentType;
+ return request.close();
+ })
+ .then(processResponse);
+
+ httpClient.get("127.0.0.1", port, "/contenttype2")
+ .then((request) {
+ request.headers.set(HttpHeaders.CONTENT_TYPE,
+ "text/html; charset = utf-8");
+ return request.close();
+ })
+ .then(processResponse);
});
- testServerMain.start();
+ server.start();
return completer.future;
}
Future testCookies() {
Completer completer = new Completer();
- TestServerMain testServerMain = new TestServerMain();
- testServerMain.setServerStartedHandler((int port) {
+ IsolatedHttpServer server = new IsolatedHttpServer();
+ server.setServerStartedHandler((int port) {
int responses = 0;
HttpClient httpClient = new HttpClient();
- HttpClientConnection conn1 =
- httpClient.get("127.0.0.1", port, "/cookie1");
- conn1.onResponse = (HttpClientResponse response) {
- Expect.equals(2, response.cookies.length);
- response.cookies.forEach((cookie) {
- if (cookie.name == "name1") {
- Expect.equals("value1", cookie.value);
- DateTime date = new DateTime.utc(2014, DateTime.JAN, 5, 23, 59, 59, 0);
- Expect.equals(date, cookie.expires);
- Expect.equals("www.example.com", cookie.domain);
- Expect.isTrue(cookie.httpOnly);
- } else if (cookie.name == "name2") {
- Expect.equals("value2", cookie.value);
- Expect.equals(100, cookie.maxAge);
- Expect.equals(".example.com", cookie.domain);
- Expect.equals("/shop", cookie.path);
- } else {
- Expect.fail("Unexpected cookie");
- }
- });
- HttpClientConnection conn2 =
- httpClient.get("127.0.0.1", port, "/cookie2");
- conn2.onRequest = (HttpClientRequest request) {
- request.cookies.add(response.cookies[0]);
- request.cookies.add(response.cookies[1]);
- request.outputStream.close();
- };
- conn2.onResponse = (HttpClientResponse response) {
- response.inputStream.onData = response.inputStream.read;
- response.inputStream.onClosed = () {
- httpClient.shutdown();
- testServerMain.shutdown();
- completer.complete(true);
- };
- };
- };
- });
- testServerMain.start();
- return completer.future;
-}
+ httpClient.get("127.0.0.1", port, "/cookie1")
+ .then((request) => request.close())
+ .then((response) {
+ Expect.equals(2, response.cookies.length);
+ response.cookies.forEach((cookie) {
+ if (cookie.name == "name1") {
+ Expect.equals("value1", cookie.value);
+ DateTime date =
+ new DateTime.utc(2014, DateTime.JAN, 5, 23, 59, 59, 0);
+ Expect.equals(date, cookie.expires);
+ Expect.equals("www.example.com", cookie.domain);
+ Expect.isTrue(cookie.httpOnly);
+ } else if (cookie.name == "name2") {
+ Expect.equals("value2", cookie.value);
+ Expect.equals(100, cookie.maxAge);
+ Expect.equals(".example.com", cookie.domain);
+ Expect.equals("/shop", cookie.path);
+ } else {
+ Expect.fail("Unexpected cookie");
+ }
+ });
-Future testFlush() {
- Completer completer = new Completer();
- TestServerMain testServerMain = new TestServerMain();
- testServerMain.setServerStartedHandler((int port) {
- HttpClient httpClient = new HttpClient();
-
- HttpClientConnection conn = httpClient.get("127.0.0.1", port, "/flush");
- conn.onRequest = (HttpClientRequest request) {
- request.outputStream.flush();
- request.outputStream.close();
- };
- conn.onResponse = (HttpClientResponse response) {
- Expect.equals(HttpStatus.OK, response.statusCode);
- response.inputStream.onData = response.inputStream.read;
- response.inputStream.onClosed = () {
- httpClient.shutdown();
- testServerMain.shutdown();
- completer.complete(true);
- };
- };
+ response.listen(
+ (_) { },
+ onDone: () {
+ httpClient.get("127.0.0.1", port, "/cookie2")
+ .then((request) {
+ request.cookies.add(response.cookies[0]);
+ request.cookies.add(response.cookies[1]);
+ return request.close();
+ })
+ .then((response) {
+ response.listen(
+ (_) { },
+ onDone: () {
+ httpClient.close();
+ server.shutdown();
+ completer.complete(true);
+ });
+ });
+ });
+ });
});
- testServerMain.start();
+ server.start();
return completer.future;
}
void main() {
- print('testHost()');
testHost().then((_) {
- print('testExpires()');
return testExpires().then((_) {
- print('testContentType()');
return testContentType().then((_) {
- print('testCookies()');
- return testCookies().then((_) {
- print('testFlush()');
- return testFlush();
- });
+ return testCookies();
});
});
- }).then((_) {
- print('done');
});
}
diff --git a/tests/standalone/io/http_auth_test.dart b/tests/standalone/io/http_auth_test.dart
index 7c8274f..cfc692b 100644
--- a/tests/standalone/io/http_auth_test.dart
+++ b/tests/standalone/io/http_auth_test.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
@@ -13,50 +13,53 @@
HttpServer server;
bool passwordChanged = false;
- Server() : server = new HttpServer();
+ Future<Server> start() {
+ var completer = new Completer();
+ HttpServer.bind().then((s) {
+ server = s;
+ server.listen((HttpRequest request) {
+ var response = request.response;
+ if (request.uri.path == "/passwdchg") {
+ passwordChanged = true;
+ response.close();
+ return;
+ };
- void start() {
- server.listen("127.0.0.1", 0);
- server.defaultRequestHandler =
- (HttpRequest request, HttpResponse response) {
- String username;
- String password;
- if (request.path == "/") {
- username = "username";
- password = "password";
- } else {
- username = request.path.substring(1, 6);
- password = request.path.substring(1, 6);
- }
- if (passwordChanged) password = "${password}1";
- if (request.headers[HttpHeaders.AUTHORIZATION] != null) {
- Expect.equals(1, request.headers[HttpHeaders.AUTHORIZATION].length);
- String authorization =
- request.headers[HttpHeaders.AUTHORIZATION][0];
- List<String> tokens = authorization.split(" ");
- Expect.equals("Basic", tokens[0]);
- String auth =
- CryptoUtils.bytesToBase64(encodeUtf8("$username:$password"));
- if (passwordChanged && auth != tokens[1]) {
- response.statusCode = HttpStatus.UNAUTHORIZED;
- response.headers.set(HttpHeaders.WWW_AUTHENTICATE,
- "Basic, realm=realm");
- } else {
- Expect.equals(auth, tokens[1]);
- }
- } else {
+ String username;
+ String password;
+ if (request.uri.path == "/") {
+ username = "username";
+ password = "password";
+ } else {
+ username = request.uri.path.substring(1, 6);
+ password = request.uri.path.substring(1, 6);
+ }
+ if (passwordChanged) password = "${password}1";
+ if (request.headers[HttpHeaders.AUTHORIZATION] != null) {
+ Expect.equals(1, request.headers[HttpHeaders.AUTHORIZATION].length);
+ String authorization =
+ request.headers[HttpHeaders.AUTHORIZATION][0];
+ List<String> tokens = authorization.split(" ");
+ Expect.equals("Basic", tokens[0]);
+ String auth =
+ CryptoUtils.bytesToBase64(encodeUtf8("$username:$password"));
+ if (passwordChanged && auth != tokens[1]) {
response.statusCode = HttpStatus.UNAUTHORIZED;
response.headers.set(HttpHeaders.WWW_AUTHENTICATE,
"Basic, realm=realm");
+ } else {
+ Expect.equals(auth, tokens[1]);
}
- response.outputStream.close();
- };
- server.addRequestHandler(
- (HttpRequest request) => request.path == "/passwdchg",
- (HttpRequest request, HttpResponse response) {
- passwordChanged = true;
- response.outputStream.close();
- });
+ } else {
+ response.statusCode = HttpStatus.UNAUTHORIZED;
+ response.headers.set(HttpHeaders.WWW_AUTHENTICATE,
+ "Basic, realm=realm");
+ }
+ response.close();
+ });
+ completer.complete(this);
+ });
+ return completer.future;
}
void shutdown() {
@@ -66,128 +69,39 @@
int get port => server.port;
}
-Server setupServer() {
- Server server = new Server();
- server.start();
- return server;
+Future<Server> setupServer() {
+ return new Server().start();
}
void testUrlUserInfo() {
- Server server = setupServer();
- HttpClient client = new HttpClient();
+ setupServer().then((server) {
+ HttpClient client = new HttpClient();
- HttpClientConnection conn =
- client.getUrl(
- Uri.parse(
- "http://username:password@127.0.0.1:${server.port}/"));
- conn.onResponse = (HttpClientResponse response) {
- response.inputStream.onData = response.inputStream.read;
- response.inputStream.onClosed = () {
- server.shutdown();
- client.shutdown();
- };
- };
+ client.getUrl(Uri.parse(
+ "http://username:password@127.0.0.1:${server.port}/"))
+ .then((request) => request.close())
+ .then((HttpClientResponse response) {
+ response.listen((_) {}, onDone: () {
+ server.shutdown();
+ client.close();
+ });
+ });
+ });
}
void testBasicNoCredentials() {
- Server server = setupServer();
- HttpClient client = new HttpClient();
+ setupServer().then((server) {
+ HttpClient client = new HttpClient();
- Future makeRequest(Uri url) {
- Completer completer = new Completer();
- HttpClientConnection conn = client.getUrl(url);
- conn.onResponse = (HttpClientResponse response) {
- Expect.equals(HttpStatus.UNAUTHORIZED, response.statusCode);
- response.inputStream.onData = response.inputStream.read;
- response.inputStream.onClosed = () => completer.complete(null);
- };
- return completer.future;
- }
+ Future makeRequest(Uri url) {
+ return client.getUrl(url)
+ .then((HttpClientRequest request) => request.close())
+ .then((HttpClientResponse response) {
+ Expect.equals(HttpStatus.UNAUTHORIZED, response.statusCode);
+ return response.reduce(null, (x, y) {});
+ });
+ }
- var futures = [];
- for (int i = 0; i < 5; i++) {
- futures.add(
- makeRequest(
- Uri.parse("http://127.0.0.1:${server.port}/test$i")));
- futures.add(
- makeRequest(
- Uri.parse("http://127.0.0.1:${server.port}/test$i/xxx")));
- }
- Future.wait(futures).then((_) {
- server.shutdown();
- client.shutdown();
- });
-}
-
-void testBasicCredentials() {
- Server server = setupServer();
- HttpClient client = new HttpClient();
-
- Future makeRequest(Uri url) {
- Completer completer = new Completer();
- HttpClientConnection conn = client.getUrl(url);
- conn.onResponse = (HttpClientResponse response) {
- Expect.equals(HttpStatus.OK, response.statusCode);
- response.inputStream.onData = response.inputStream.read;
- response.inputStream.onClosed = () => completer.complete(null);
- };
- return completer.future;
- }
-
- for (int i = 0; i < 5; i++) {
- client.addCredentials(
- Uri.parse("http://127.0.0.1:${server.port}/test$i"),
- "realm",
- new HttpClientBasicCredentials("test$i", "test$i"));
- }
-
- var futures = [];
- for (int i = 0; i < 5; i++) {
- futures.add(
- makeRequest(
- Uri.parse("http://127.0.0.1:${server.port}/test$i")));
- futures.add(
- makeRequest(
- Uri.parse("http://127.0.0.1:${server.port}/test$i/xxx")));
- }
- Future.wait(futures).then((_) {
- server.shutdown();
- client.shutdown();
- });
-}
-
-void testBasicAuthenticateCallback() {
- Server server = setupServer();
- HttpClient client = new HttpClient();
- bool passwordChanged = false;
-
- client.authenticate = (Uri url, String scheme, String realm) {
- Expect.equals("Basic", scheme);
- Expect.equals("realm", realm);
- String username = url.path.substring(1, 6);
- String password = url.path.substring(1, 6);
- if (passwordChanged) password = "${password}1";
- Completer completer = new Completer();
- new Timer(const Duration(milliseconds: 10), () {
- client.addCredentials(
- url, realm, new HttpClientBasicCredentials(username, password));
- completer.complete(true);
- });
- return completer.future;
- };
-
- Future makeRequest(Uri url) {
- Completer completer = new Completer();
- HttpClientConnection conn = client.getUrl(url);
- conn.onResponse = (HttpClientResponse response) {
- Expect.equals(HttpStatus.OK, response.statusCode);
- response.inputStream.onData = response.inputStream.read;
- response.inputStream.onClosed = () => completer.complete(null);
- };
- return completer.future;
- }
-
- List<Future> makeRequests() {
var futures = [];
for (int i = 0; i < 5; i++) {
futures.add(
@@ -198,17 +112,100 @@
Uri.parse(
"http://127.0.0.1:${server.port}/test$i/xxx")));
}
- return futures;
- }
+ Future.wait(futures).then((_) {
+ server.shutdown();
+ client.close();
+ });
+ });
+}
- Future.wait(makeRequests()).then((_) {
- makeRequest(
- Uri.parse(
- "http://127.0.0.1:${server.port}/passwdchg")).then((_) {
- passwordChanged = true;
- Future.wait(makeRequests()).then((_) {
- server.shutdown();
- client.shutdown();
+void testBasicCredentials() {
+ setupServer().then((server) {
+ HttpClient client = new HttpClient();
+
+ Future makeRequest(Uri url) {
+ return client.getUrl(url)
+ .then((HttpClientRequest request) => request.close())
+ .then((HttpClientResponse response) {
+ Expect.equals(HttpStatus.OK, response.statusCode);
+ return response.reduce(null, (x, y) {});
+ });
+ }
+
+ for (int i = 0; i < 5; i++) {
+ client.addCredentials(
+ Uri.parse("http://127.0.0.1:${server.port}/test$i"),
+ "realm",
+ new HttpClientBasicCredentials("test$i", "test$i"));
+ }
+
+ var futures = [];
+ for (int i = 0; i < 5; i++) {
+ futures.add(
+ makeRequest(
+ Uri.parse("http://127.0.0.1:${server.port}/test$i")));
+ futures.add(
+ makeRequest(
+ Uri.parse(
+ "http://127.0.0.1:${server.port}/test$i/xxx")));
+ }
+ Future.wait(futures).then((_) {
+ server.shutdown();
+ client.close();
+ });
+ });
+}
+
+void testBasicAuthenticateCallback() {
+ setupServer().then((server) {
+ HttpClient client = new HttpClient();
+ bool passwordChanged = false;
+
+ client.authenticate = (Uri url, String scheme, String realm) {
+ Expect.equals("Basic", scheme);
+ Expect.equals("realm", realm);
+ String username = url.path.substring(1, 6);
+ String password = url.path.substring(1, 6);
+ if (passwordChanged) password = "${password}1";
+ Completer completer = new Completer();
+ new Timer(10, (_) {
+ client.addCredentials(
+ url, realm, new HttpClientBasicCredentials(username, password));
+ completer.complete(true);
+ });
+ return completer.future;
+ };
+
+ Future makeRequest(Uri url) {
+ return client.getUrl(url)
+ .then((HttpClientRequest request) => request.close())
+ .then((HttpClientResponse response) {
+ Expect.equals(HttpStatus.OK, response.statusCode);
+ return response.reduce(null, (x, y) {});
+ });
+ }
+
+ List<Future> makeRequests() {
+ var futures = [];
+ for (int i = 0; i < 5; i++) {
+ futures.add(
+ makeRequest(
+ Uri.parse("http://127.0.0.1:${server.port}/test$i")));
+ futures.add(
+ makeRequest(
+ Uri.parse("http://127.0.0.1:${server.port}/test$i/xxx")));
+ }
+ return futures;
+ }
+
+ Future.wait(makeRequests()).then((_) {
+ makeRequest(
+ Uri.parse("http://127.0.0.1:${server.port}/passwdchg")).then((_) {
+ passwordChanged = true;
+ Future.wait(makeRequests()).then((_) {
+ server.shutdown();
+ client.close();
+ });
});
});
});
diff --git a/tests/standalone/io/http_basic_test.dart b/tests/standalone/io/http_basic_test.dart
index 9ec9a6a..eff6f5d 100644
--- a/tests/standalone/io/http_basic_test.dart
+++ b/tests/standalone/io/http_basic_test.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
//
@@ -34,7 +34,7 @@
_serverPort.send(command, _statusPort.toSendPort());
}
- void shutdown() {
+ void close() {
// Send server stop message to the server.
_serverPort.send(new TestServerCommand.stop(), _statusPort.toSendPort());
_statusPort.close();
@@ -98,45 +98,49 @@
class TestServer {
// Echo the request content back to the response.
- void _echoHandler(HttpRequest request, HttpResponse response) {
+ void _echoHandler(HttpRequest request) {
+ var response = request.response;
Expect.equals("POST", request.method);
response.contentLength = request.contentLength;
- request.inputStream.pipe(response.outputStream);
+ request.pipe(response);
}
// Echo the request content back to the response.
- void _zeroToTenHandler(HttpRequest request, HttpResponse response) {
+ void _zeroToTenHandler(HttpRequest request) {
+ var response = request.response;
Expect.equals("GET", request.method);
- request.inputStream.onData = () {};
- request.inputStream.onClosed = () {
- response.outputStream.writeString("01234567890");
- response.outputStream.close();
- };
+ request.listen((_) {}, onDone: () {
+ response.addString("01234567890");
+ response.close();
+ });
}
// Return a 404.
- void _notFoundHandler(HttpRequest request, HttpResponse response) {
+ void _notFoundHandler(HttpRequest request) {
+ var response = request.response;
response.statusCode = HttpStatus.NOT_FOUND;
response.headers.set("Content-Type", "text/html; charset=UTF-8");
- response.outputStream.writeString("Page not found");
- response.outputStream.close();
+ response.addString("Page not found");
+ response.close();
}
// Return a 301 with a custom reason phrase.
- void _reasonForMovingHandler(HttpRequest request, HttpResponse response) {
+ void _reasonForMovingHandler(HttpRequest request) {
+ var response = request.response;
response.statusCode = HttpStatus.MOVED_PERMANENTLY;
response.reasonPhrase = "Don't come looking here any more";
- response.outputStream.close();
+ response.close();
}
// Check the "Host" header.
- void _hostHandler(HttpRequest request, HttpResponse response) {
+ void _hostHandler(HttpRequest request) {
+ var response = request.response;
Expect.equals(1, request.headers["Host"].length);
Expect.equals("www.dartlang.org:1234", request.headers["Host"][0]);
Expect.equals("www.dartlang.org", request.headers.host);
Expect.equals(1234, request.headers.port);
response.statusCode = HttpStatus.OK;
- response.outputStream.close();
+ response.close();
}
void init() {
@@ -150,11 +154,12 @@
void dispatch(var message, SendPort replyTo) {
if (message.isStart) {
- _server = new HttpServer();
try {
- _server.listen("127.0.0.1", 0);
- _server.defaultRequestHandler = _requestReceivedHandler;
- replyTo.send(new TestServerStatus.started(_server.port), null);
+ HttpServer.bind().then((server) {
+ _server = server;
+ _server.listen(_requestReceivedHandler);
+ replyTo.send(new TestServerStatus.started(_server.port), null);
+ });
} catch (e) {
replyTo.send(new TestServerStatus.error(), null);
}
@@ -167,12 +172,12 @@
}
}
- void _requestReceivedHandler(HttpRequest request, HttpResponse response) {
- var requestHandler =_requestHandlers[request.path];
+ void _requestReceivedHandler(HttpRequest request) {
+ var requestHandler =_requestHandlers[request.uri.path];
if (requestHandler != null) {
- requestHandler(request, response);
+ requestHandler(request);
} else {
- _notFoundHandler(request, response);
+ _notFoundHandler(request);
}
}
@@ -184,7 +189,7 @@
void testStartStop() {
TestServerMain testServerMain = new TestServerMain();
testServerMain.setServerStartedHandler((int port) {
- testServerMain.shutdown();
+ testServerMain.close();
});
testServerMain.start();
}
@@ -193,19 +198,19 @@
TestServerMain testServerMain = new TestServerMain();
testServerMain.setServerStartedHandler((int port) {
HttpClient httpClient = new HttpClient();
- HttpClientConnection conn =
- httpClient.get("127.0.0.1", port, "/0123456789");
- conn.onResponse = (HttpClientResponse response) {
- Expect.equals(HttpStatus.OK, response.statusCode);
- StringInputStream stream = new StringInputStream(response.inputStream);
- StringBuffer body = new StringBuffer();
- stream.onData = () => body.add(stream.read());
- stream.onClosed = () {
- Expect.equals("01234567890", body.toString());
- httpClient.shutdown();
- testServerMain.shutdown();
- };
- };
+ httpClient.get("127.0.0.1", port, "/0123456789")
+ .then((request) => request.close())
+ .then((response) {
+ Expect.equals(HttpStatus.OK, response.statusCode);
+ StringBuffer body = new StringBuffer();
+ response.listen(
+ (data) => body.add(new String.fromCharCodes(data)),
+ onDone: () {
+ Expect.equals("01234567890", body.toString());
+ httpClient.close();
+ testServerMain.close();
+ });
+ });
});
testServerMain.start();
}
@@ -220,34 +225,33 @@
int count = 0;
HttpClient httpClient = new HttpClient();
void sendRequest() {
- HttpClientConnection conn =
- httpClient.post("127.0.0.1", port, "/echo");
- conn.onRequest = (HttpClientRequest request) {
- if (chunkedEncoding) {
- request.outputStream.writeString(data.substring(0, 10));
- request.outputStream.writeString(data.substring(10, data.length));
- } else {
- request.contentLength = data.length;
- request.outputStream.write(data.charCodes);
- }
- request.outputStream.close();
- };
- conn.onResponse = (HttpClientResponse response) {
- Expect.equals(HttpStatus.OK, response.statusCode);
- StringInputStream stream = new StringInputStream(response.inputStream);
- StringBuffer body = new StringBuffer();
- stream.onData = () => body.add(stream.read());
- stream.onClosed = () {
- Expect.equals(data, body.toString());
- count++;
- if (count < kMessageCount) {
- sendRequest();
- } else {
- httpClient.shutdown();
- testServerMain.shutdown();
- }
- };
- };
+ httpClient.post("127.0.0.1", port, "/echo")
+ .then((request) {
+ if (chunkedEncoding) {
+ request.addString(data.substring(0, 10));
+ request.addString(data.substring(10, data.length));
+ } else {
+ request.contentLength = data.length;
+ request.addString(data);
+ }
+ return request.close();
+ })
+ .then((response) {
+ Expect.equals(HttpStatus.OK, response.statusCode);
+ StringBuffer body = new StringBuffer();
+ response.listen(
+ (data) => body.add(new String.fromCharCodes(data)),
+ onDone: () {
+ Expect.equals(data, body.toString());
+ count++;
+ if (count < kMessageCount) {
+ sendRequest();
+ } else {
+ httpClient.close();
+ testServerMain.close();
+ }
+ });
+ });
}
sendRequest();
@@ -264,19 +268,19 @@
TestServerMain testServerMain = new TestServerMain();
testServerMain.setServerStartedHandler((int port) {
HttpClient httpClient = new HttpClient();
- HttpClientConnection conn =
- httpClient.get("127.0.0.1", port, "/thisisnotfound");
- conn.onResponse = (HttpClientResponse response) {
- Expect.equals(HttpStatus.NOT_FOUND, response.statusCode);
- var body = new StringBuffer();
- var stream = response.inputStream;
- stream.onData = () => body.add(new String.fromCharCodes(stream.read()));
- stream.onClosed = () {
- Expect.equals("Page not found", body.toString());
- httpClient.shutdown();
- testServerMain.shutdown();
- };
- };
+ httpClient.get("127.0.0.1", port, "/thisisnotfound")
+ .then((request) => request.close())
+ .then((response) {
+ Expect.equals(HttpStatus.NOT_FOUND, response.statusCode);
+ var body = new StringBuffer();
+ response.listen(
+ (data) => body.add(new String.fromCharCodes(data)),
+ onDone: () {
+ Expect.equals("Page not found", body.toString());
+ httpClient.close();
+ testServerMain.close();
+ });
+ });
});
testServerMain.start();
}
@@ -285,19 +289,22 @@
TestServerMain testServerMain = new TestServerMain();
testServerMain.setServerStartedHandler((int port) {
HttpClient httpClient = new HttpClient();
- HttpClientConnection conn =
- httpClient.get("127.0.0.1", port, "/reasonformoving");
- conn.followRedirects = false;
- conn.onResponse = (HttpClientResponse response) {
- Expect.equals(HttpStatus.MOVED_PERMANENTLY, response.statusCode);
- Expect.equals("Don't come looking here any more", response.reasonPhrase);
- var stream = response.inputStream;
- stream.onData = () => Expect.fail("No data expected");
- stream.onClosed = () {
- httpClient.shutdown();
- testServerMain.shutdown();
- };
- };
+ httpClient.get("127.0.0.1", port, "/reasonformoving")
+ .then((request) {
+ request.followRedirects = false;
+ return request.close();
+ })
+ .then((response) {
+ Expect.equals(HttpStatus.MOVED_PERMANENTLY, response.statusCode);
+ Expect.equals("Don't come looking here any more",
+ response.reasonPhrase);
+ response.listen(
+ (data) => Expect.fail("No data expected"),
+ onDone: () {
+ httpClient.close();
+ testServerMain.close();
+ });
+ });
});
testServerMain.start();
}
diff --git a/tests/standalone/io/http_client_connect_test.dart b/tests/standalone/io/http_client_connect_test.dart
new file mode 100644
index 0000000..dd8a3bf
--- /dev/null
+++ b/tests/standalone/io/http_client_connect_test.dart
@@ -0,0 +1,134 @@
+// 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.
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
+
+import 'dart:async';
+import 'dart:io';
+import 'dart:isolate';
+
+void testGetEmptyRequest() {
+ HttpServer.bind().then((server) {
+ server.listen((request) {
+ request.pipe(request.response);
+ });
+
+ var client = new HttpClient();
+ client.get("127.0.0.1", server.port, "/")
+ .then((request) => request.close())
+ .then((response) {
+ response.listen(
+ (data) {},
+ onDone: server.close);
+ });
+ });
+}
+
+void testGetDataRequest() {
+ HttpServer.bind().then((server) {
+ var data = "lalala".charCodes;
+ server.listen((request) {
+ request.response.add(data);
+ request.pipe(request.response);
+ });
+
+ var client = new HttpClient();
+ client.get("127.0.0.1", server.port, "/")
+ .then((request) => request.close())
+ .then((response) {
+ int count = 0;
+ response.listen(
+ (data) => count += data.length,
+ onDone: () {
+ server.close();
+ Expect.equals(data.length, count);
+ });
+ });
+ });
+}
+
+void testGetInvalidHost() {
+ var port = new ReceivePort();
+ var client = new HttpClient();
+ client.get("__SOMETHING_INVALID__", 8888, "/")
+ .catchError((error) {
+ port.close();
+ client.close();
+ });
+}
+
+void testGetServerClose() {
+ HttpServer.bind().then((server) {
+ server.listen((request) {
+ server.close();
+ });
+
+ var port = new ReceivePort();
+ var client = new HttpClient();
+ client.get("127.0.0.1", server.port, "/")
+ .then((request) => request.close())
+ .then((response) {
+ Expect.fail("Request not expected");
+ })
+ .catchError((error) => port.close(),
+ test: (error) => error is HttpParserException);
+ });
+}
+
+void testGetDataServerClose() {
+ var completer = new Completer();
+ HttpServer.bind().then((server) {
+ server.listen((request) {
+ request.response.contentLength = 100;
+ request.response.addString("data");
+ request.response.addString("more data");
+ completer.future.then((_) => server.close());
+ });
+
+ var port = new ReceivePort();
+ var client = new HttpClient();
+ client.get("127.0.0.1", server.port, "/")
+ .then((request) => request.close())
+ .then((response) {
+ // Close the (incomplete) response, now we have seen the response object.
+ completer.complete(null);
+ int errors = 0;
+ response.listen(
+ (data) {},
+ onError: (error) => errors++,
+ onDone: () {
+ port.close();
+ Expect.equals(1, errors);
+ });
+ });
+ });
+}
+
+void testPostEmptyRequest() {
+ HttpServer.bind().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 main() {
+ testGetEmptyRequest();
+ testGetDataRequest();
+ testGetInvalidHost();
+ testGetServerClose();
+ testGetDataServerClose();
+ testPostEmptyRequest();
+}
diff --git a/tests/standalone/io/http_client_request_test.dart b/tests/standalone/io/http_client_request_test.dart
new file mode 100644
index 0000000..02d0a86
--- /dev/null
+++ b/tests/standalone/io/http_client_request_test.dart
@@ -0,0 +1,107 @@
+// 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 "dart:io";
+import "dart:isolate";
+import "dart:scalarlist";
+
+void testClientRequest(void handler(request)) {
+ HttpServer.bind().then((server) {
+ server.listen((request) {
+ request.listen((_) {}, onDone: () {
+ request.response.close();
+ }, onError: (e) {});
+ });
+
+ var client = new HttpClient();
+ client.get("127.0.0.1", server.port, "/")
+ .then((request) {
+ return handler(request);
+ })
+ .then((response) {
+ response.listen((_) {}, onDone: () {
+ client.close();
+ server.close();
+ });
+ })
+ .catchError((error) {
+ server.close();
+ client.close();
+ }, test: (e) => e is HttpParserException);
+ });
+}
+
+void testResponseDone() {
+ testClientRequest((request) {
+ request.close();
+ request.done.then((req) {
+ Expect.equals(request, req);
+ });
+ return request.response;
+ });
+}
+
+void testBadResponseAdd() {
+ testClientRequest((request) {
+ var port = new ReceivePort();
+ request.contentLength = 0;
+ request.add([0]);
+ request.done.catchError((error) {
+ port.close();
+ }, test: (e) => e is HttpException);
+ return request.response;
+ });
+
+ testClientRequest((request) {
+ var port = new ReceivePort();
+ request.contentLength = 5;
+ request.add([0, 0, 0]);
+ request.add([0, 0, 0]);
+ request.done.catchError((error) {
+ port.close();
+ }, test: (e) => e is HttpException);
+ return request.response;
+ });
+
+ testClientRequest((request) {
+ var port = new ReceivePort();
+ request.contentLength = 0;
+ request.add(new Uint8List(64 * 1024));
+ request.add(new Uint8List(64 * 1024));
+ request.add(new Uint8List(64 * 1024));
+ request.done.catchError((error) {
+ port.close();
+ }, test: (e) => e is HttpException);
+ return request.response;
+ });
+}
+
+void testBadResponseClose() {
+ testClientRequest((request) {
+ var port = new ReceivePort();
+ request.contentLength = 5;
+ request.close();
+ request.done.catchError((error) {
+ port.close();
+ }, test: (e) => e is HttpException);
+ return request.response;
+ });
+
+ testClientRequest((request) {
+ var port = new ReceivePort();
+ request.contentLength = 5;
+ request.add([0]);
+ request.close();
+ request.done.catchError((error) {
+ port.close();
+ }, test: (e) => e is HttpException);
+ return request.response;
+ });
+}
+
+void main() {
+ testResponseDone();
+ testBadResponseAdd();
+ testBadResponseClose();
+}
diff --git a/tests/standalone/io/http_client_socket_reuse_test.dart b/tests/standalone/io/http_client_socket_reuse_test.dart
new file mode 100644
index 0000000..715624a8
--- /dev/null
+++ b/tests/standalone/io/http_client_socket_reuse_test.dart
@@ -0,0 +1,43 @@
+// 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.
+
+import "dart:async";
+import "dart:io";
+import "dart:uri";
+import "dart:isolate";
+
+// By running tests sequentially, we cover the socket reuse code in HttpClient.
+
+void testGoogleUrls() {
+ int testsStarted = 0;
+ int testsFinished = 0;
+ bool allStarted = false;
+ HttpClient client = new HttpClient();
+
+ Future testUrl(String url) {
+ testsStarted++;
+ var requestUri = Uri.parse(url);
+ return client.getUrl(requestUri)
+ .then((HttpClientRequest request) => request.close())
+ .then((HttpClientResponse response) {
+ Expect.isTrue(response.statusCode < 500);
+ if (requestUri.path.length == 0) {
+ Expect.isTrue(response.statusCode != 404);
+ }
+ return response.reduce(null, (previous, element) => null);
+ })
+ .catchError((error) => Expect.fail("Unexpected IO error: $error"));
+ }
+
+ // TODO(3593): Use a Dart HTTP server for this test.
+ testUrl('http://www.google.dk')
+ .then((_) => testUrl('http://www.google.dk'))
+ .then((_) => testUrl('http://www.google.dk/#q=foo'))
+ .then((_) => testUrl('http://www.google.dk/#hl=da&q=foo'))
+ .then((_) { client.close(); });
+}
+
+void main() {
+ testGoogleUrls();
+}
diff --git a/tests/standalone/io/http_client_test.dart b/tests/standalone/io/http_client_test.dart
index 698090a..c3e7db1 100644
--- a/tests/standalone/io/http_client_test.dart
+++ b/tests/standalone/io/http_client_test.dart
@@ -1,6 +1,11 @@
// 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.
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
import "dart:io";
import "dart:uri";
@@ -8,21 +13,13 @@
void testGoogle() {
HttpClient client = new HttpClient();
- var conn = client.get('www.google.com', 80, '/');
-
- conn.onRequest = (HttpClientRequest request) {
- request.outputStream.close();
- };
- conn.onResponse = (HttpClientResponse response) {
- Expect.isTrue(response.statusCode < 500);
- response.inputStream.onData = () {
- response.inputStream.read();
- };
- response.inputStream.onClosed = () {
- client.shutdown();
- };
- };
- conn.onError = (error) => Expect.fail("Unexpected IO error $error");
+ client.get('www.google.com', 80, '/')
+ .then((request) => request.close())
+ .then((response) {
+ Expect.isTrue(response.statusCode < 500);
+ response.listen((data) {}, onDone: client.close);
+ })
+ .catchError((error) => Expect.fail("Unexpected IO error: $error"));
}
int testGoogleUrlCount = 0;
@@ -31,25 +28,19 @@
void testUrl(String url) {
var requestUri = Uri.parse(url);
- var conn = client.getUrl(requestUri);
-
- conn.onRequest = (HttpClientRequest request) {
- request.outputStream.close();
- };
- conn.onResponse = (HttpClientResponse response) {
- testGoogleUrlCount++;
- Expect.isTrue(response.statusCode < 500);
- if (requestUri.path.length == 0) {
- Expect.isTrue(response.statusCode != 404);
- }
- response.inputStream.onData = () {
- response.inputStream.read();
- };
- response.inputStream.onClosed = () {
- if (testGoogleUrlCount == 5) client.shutdown();
- };
- };
- conn.onError = (error) => Expect.fail("Unexpected IO error $error");
+ client.getUrl(requestUri)
+ .then((request) => request.close())
+ .then((response) {
+ testGoogleUrlCount++;
+ Expect.isTrue(response.statusCode < 500);
+ if (requestUri.path.length == 0) {
+ Expect.isTrue(response.statusCode != 404);
+ }
+ response.listen((data) {}, onDone: () {
+ if (testGoogleUrlCount == 5) client.close();
+ });
+ })
+ .catchError((error) => Expect.fail("Unexpected IO error: $error"));
}
testUrl('http://www.google.com');
@@ -67,15 +58,13 @@
void testBadHostName() {
HttpClient client = new HttpClient();
- HttpClientConnection connection =
- client.get("some.bad.host.name.7654321", 0, "/");
- connection.onRequest = (HttpClientRequest request) {
- Expect.fail("Should not open a request on bad hostname");
- };
ReceivePort port = new ReceivePort();
- connection.onError = (Exception error) {
- port.close(); // We expect onError to be called, due to bad host name.
- };
+ client.get("some.bad.host.name.7654321", 0, "/")
+ .then((request) {
+ Expect.fail("Should not open a request on bad hostname");
+ }).catchError((error) {
+ port.close(); // We expect onError to be called, due to bad host name.
+ }, test: (error) => error is! String);
}
void main() {
diff --git a/tests/standalone/io/http_connection_close_test.dart b/tests/standalone/io/http_connection_close_test.dart
index 75f3b98..e8f3609 100644
--- a/tests/standalone/io/http_connection_close_test.dart
+++ b/tests/standalone/io/http_connection_close_test.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
//
@@ -8,74 +8,73 @@
import "dart:uri";
void testHttp10Close(bool closeRequest) {
- HttpServer server = new HttpServer();
- server.listen("127.0.0.1", 0, backlog: 5);
+ HttpServer.bind().then((server) {
+ server.listen((request) {
+ request.response.close();
+ });
- Socket socket = new Socket("127.0.0.1", server.port);
- socket.onConnect = () {
- List<int> buffer = new List<int>.fixedLength(1024);
- socket.outputStream.writeString("GET / HTTP/1.0\r\n\r\n");
- if (closeRequest) socket.outputStream.close();
- socket.onData = () => socket.readList(buffer, 0, buffer.length);
- socket.onClosed = () {
- if (!closeRequest) socket.close(true);
- server.close();
- };
- };
+ Socket.connect("127.0.0.1", server.port)
+ .then((socket) {
+ socket.addString("GET / HTTP/1.0\r\n\r\n");
+ socket.listen(
+ (data) {},
+ onDone: () {
+ if (!closeRequest) socket.destroy();
+ server.close();
+ });
+ if (closeRequest) socket.close();
+ });
+ });
}
void testHttp11Close(bool closeRequest) {
- HttpServer server = new HttpServer();
- server.listen("127.0.0.1", 0, backlog: 5);
+ HttpServer.bind().then((server) {
+ server.listen((request) {
+ request.response.close();
+ });
- Socket socket = new Socket("127.0.0.1", server.port);
- socket.onConnect = () {
- List<int> buffer = new List<int>.fixedLength(1024);
- socket.outputStream.writeString(
- "GET / HTTP/1.1\r\nConnection: close\r\n\r\n");
- if (closeRequest) socket.outputStream.close();
- socket.onData = () => socket.readList(buffer, 0, buffer.length);
- socket.onClosed = () {
- if (!closeRequest) socket.close(true);
- server.close();
- };
- };
+ Socket.connect("127.0.0.1", server.port)
+ .then((socket) {
+ List<int> buffer = new List<int>.fixedLength(1024);
+ socket.addString("GET / HTTP/1.1\r\nConnection: close\r\n\r\n");
+ socket.listen(
+ (data) {},
+ onDone: () {
+ if (!closeRequest) socket.destroy();
+ server.close();
+ });
+ if (closeRequest) socket.close();
+ });
+ });
}
void testStreamResponse() {
- Timer timer;
- var server = new HttpServer();
- server.onError = (e) {
- server.close();
- timer.cancel();
- };
- server.listen("127.0.0.1", 0, backlog: 5);
- server.defaultRequestHandler = (var request, var response) {
- timer = new Timer.repeating(new Duration(milliseconds: 10), (_) {
- DateTime now = new DateTime.now();
- try {
- response.outputStream.writeString(
- 'data:${now.millisecondsSinceEpoch}\n\n');
- } catch (e) {
- timer.cancel();
- server.close();
+ HttpServer.bind().then((server) {
+ server.listen((request) {
+ // TODO(ajohnsen): Use timer (see old version).
+ for (int i = 0; i < 10; i++) {
+ request.response.addString(
+ 'data:${new DateTime.now().millisecondsSinceEpoch}\n\n');
}
});
- };
- var client = new HttpClient();
- var connection =
- client.getUrl(Uri.parse("http://127.0.0.1:${server.port}"));
- connection.onResponse = (resp) {
- int bytes = 0;
- resp.inputStream.onData = () {
- bytes += resp.inputStream.read().length;
- if (bytes > 100) {
- client.shutdown(force: true);
- }
- };
- };
- connection.onError = (e) => Expect.isTrue(e is HttpException);
+ var client = new HttpClient();
+ client.getUrl(Uri.parse("http://127.0.0.1:${server.port}"))
+ .then((request) => request.close())
+ .then((response) {
+ int bytes = 0;
+ response.listen(
+ (data) {
+ bytes += data.length;
+ if (bytes > 100) {
+ client.close(force: true);
+ }
+ },
+ onError: (error) {
+ server.close();
+ });
+ });
+ });
}
main() {
diff --git a/tests/standalone/io/http_connection_header_test.dart b/tests/standalone/io/http_connection_header_test.dart
index 77cf976..b7966a2 100644
--- a/tests/standalone/io/http_connection_header_test.dart
+++ b/tests/standalone/io/http_connection_header_test.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
//
@@ -31,48 +31,47 @@
}
void test(int totalConnections, bool clientPersistentConnection) {
- HttpServer server = new HttpServer();
- server.onError = (e) => Expect.fail("Unexpected error $e");
- server.listen("127.0.0.1", 0, backlog: totalConnections);
- server.defaultRequestHandler = (HttpRequest request, HttpResponse response) {
- // Check expected request.
- Expect.equals(clientPersistentConnection, request.persistentConnection);
- Expect.equals(clientPersistentConnection, response.persistentConnection);
- checkExpectedConnectionHeaders(request.headers,
- request.persistentConnection);
+ HttpServer.bind().then((server) {
+ server.listen((HttpRequest request) {
+ // Check expected request.
+ Expect.equals(clientPersistentConnection, request.persistentConnection);
+ Expect.equals(clientPersistentConnection,
+ request.response.persistentConnection);
+ checkExpectedConnectionHeaders(request.headers,
+ request.persistentConnection);
- // Generate response. If the client signaled non-persistent
- // connection the server should not need to set it.
- if (request.persistentConnection) {
- response.persistentConnection = false;
+ // Generate response. If the client signaled non-persistent
+ // connection the server should not need to set it.
+ if (request.persistentConnection) {
+ request.response.persistentConnection = false;
+ }
+ setConnectionHeaders(request.response.headers);
+ request.response.close();
+ });
+
+ int count = 0;
+ HttpClient client = new HttpClient();
+ for (int i = 0; i < totalConnections; i++) {
+ client.get("127.0.0.1", server.port, "/")
+ .then((HttpClientRequest request) {
+ setConnectionHeaders(request.headers);
+ request.persistentConnection = clientPersistentConnection;
+ return request.close();
+ })
+ .then((HttpClientResponse response) {
+ Expect.isFalse(response.persistentConnection);
+ checkExpectedConnectionHeaders(response.headers,
+ response.persistentConnection);
+ response.listen((_) {}, onDone: () {
+ count++;
+ if (count == totalConnections) {
+ client.close();
+ server.close();
+ }
+ });
+ });
}
- setConnectionHeaders(response.headers);
- response.outputStream.close();
- };
-
- int count = 0;
- HttpClient client = new HttpClient();
- for (int i = 0; i < totalConnections; i++) {
- HttpClientConnection conn = client.get("127.0.0.1", server.port, "/");
- conn.onError = (e) => Expect.fail("Unexpected error $e");
- conn.onRequest = (HttpClientRequest request) {
- setConnectionHeaders(request.headers);
- request.persistentConnection = clientPersistentConnection;
- request.outputStream.close();
- };
- conn.onResponse = (HttpClientResponse response) {
- Expect.isFalse(response.persistentConnection);
- checkExpectedConnectionHeaders(response.headers,
- response.persistentConnection);
- response.inputStream.onClosed = () {
- count++;
- if (count == totalConnections) {
- client.shutdown();
- server.close();
- }
- };
- };
- }
+ });
}
diff --git a/tests/standalone/io/http_connection_info_test.dart b/tests/standalone/io/http_connection_info_test.dart
index 627d0fc..54c0628 100644
--- a/tests/standalone/io/http_connection_info_test.dart
+++ b/tests/standalone/io/http_connection_info_test.dart
@@ -1,44 +1,46 @@
-// Copyright (c) 2012, 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.
import "dart:io";
void testHttpConnectionInfo() {
- HttpServer server = new HttpServer();
- server.listen("0.0.0.0", 0);
- int clientPort;
- server.defaultRequestHandler = (HttpRequest request, HttpResponse response) {
- Expect.isTrue(request.connectionInfo.remoteHost is String);
- Expect.equals(request.connectionInfo.localPort, server.port);
- Expect.isNotNull(clientPort);
- Expect.equals(request.connectionInfo.remotePort, clientPort);
- request.inputStream.onClosed = () {
- response.outputStream.close();
- };
- };
- server.onError = (Exception e) {
- Expect.fail("Unexpected error: $e");
- };
+ HttpServer.bind("0.0.0.0", 0).then((server) {
+ int clientPort;
+ server.listen((request) {
+ var response = request.response;
+ Expect.isTrue(request.connectionInfo.remoteHost is String);
+ Expect.isTrue(response.connectionInfo.remoteHost is String);
+ Expect.equals(request.connectionInfo.localPort, server.port);
+ Expect.equals(response.connectionInfo.localPort, server.port);
+ Expect.isNotNull(clientPort);
+ Expect.equals(request.connectionInfo.remotePort, clientPort);
+ Expect.equals(response.connectionInfo.remotePort, clientPort);
+ request.listen(
+ (_) { },
+ onDone: () { request.response.close(); });
+ });
- HttpClient client = new HttpClient();
- HttpClientConnection conn = client.get("127.0.0.1", server.port, "/");
- conn.onRequest = (HttpClientRequest request) {
- Expect.isTrue(conn.connectionInfo.remoteHost is String);
- Expect.equals(conn.connectionInfo.remotePort, server.port);
- clientPort = conn.connectionInfo.localPort;
- request.outputStream.close();
- };
- conn.onResponse = (HttpClientResponse response) {
- response.inputStream.onClosed = () {
- client.shutdown();
- server.close();
- };
- };
- conn.onError = (Exception e) {
- Expect.fail("Unexpected error: $e");
- };
+ HttpClient client = new HttpClient();
+ client.get("127.0.0.1", server.port, "/")
+ .then((request) {
+ Expect.isTrue(request.connectionInfo.remoteHost is String);
+ Expect.equals(request.connectionInfo.remotePort, server.port);
+ clientPort = request.connectionInfo.localPort;
+ return request.close();
+ })
+ .then((response) {
+ Expect.equals(server.port, response.connectionInfo.remotePort);
+ Expect.equals(clientPort, response.connectionInfo.localPort);
+ response.listen(
+ (_) { },
+ onDone: () {
+ client.close();
+ server.close();
+ });
+ });
+ });
}
void main() {
diff --git a/tests/standalone/io/http_content_length_test.dart b/tests/standalone/io/http_content_length_test.dart
index 6290218..14d4000 100644
--- a/tests/standalone/io/http_content_length_test.dart
+++ b/tests/standalone/io/http_content_length_test.dart
@@ -6,192 +6,229 @@
import "dart:io";
void testNoBody(int totalConnections, bool explicitContentLength) {
- HttpServer server = new HttpServer();
- server.onError = (e) => Expect.fail("Unexpected error $e");
- server.listen("127.0.0.1", 0, backlog: totalConnections);
- server.defaultRequestHandler = (HttpRequest request, HttpResponse response) {
- Expect.equals("0", request.headers.value('content-length'));
- Expect.equals(0, request.contentLength);
- response.contentLength = 0;
- OutputStream stream = response.outputStream;
- Expect.throws(() => stream.writeString("x"), (e) => e is HttpException);
- stream.close();
- Expect.throws(() => stream.writeString("x"), (e) => e is HttpException);
- };
+ var errors = 0;
+ HttpServer.bind("127.0.0.1", 0, totalConnections).then((server) {
+ server.listen(
+ (HttpRequest request) {
+ Expect.equals("0", request.headers.value('content-length'));
+ Expect.equals(0, request.contentLength);
+ var response = request.response;
+ response.contentLength = 0;
+ response.done
+ .then((_) {
+ Expect.fail("Unexpected successful response completion");
+ })
+ .catchError((e) {
+ Expect.isTrue(e.error is HttpException);
+ });
+ // addString with content length 0 closes the connection and
+ // reports an error.
+ response.addString("x");
+ // Subsequent addString are ignored as there is already an
+ // error.
+ response.addString("x");
+ // After an explicit close, addString becomes a state error
+ // because we have said we will not add more.
+ response.close();
+ Expect.throws(() => response.addString("x"),
+ (e) => e is StateError);
+ },
+ onError: (e) {
+ Expect.fail("Unexpected server error $e");
+ });
- int count = 0;
- HttpClient client = new HttpClient();
- for (int i = 0; i < totalConnections; i++) {
- HttpClientConnection conn = client.get("127.0.0.1", server.port, "/");
- conn.onError = (e) => Expect.fail("Unexpected error $e");
- conn.onRequest = (HttpClientRequest request) {
- OutputStream stream = request.outputStream;
- if (explicitContentLength) {
- request.contentLength = 0;
- Expect.throws(() => stream.writeString("x"), (e) => e is HttpException);
- }
- stream.close();
- Expect.throws(() => stream.writeString("x"), (e) => e is HttpException);
- };
- conn.onResponse = (HttpClientResponse response) {
- Expect.equals("0", response.headers.value('content-length'));
- Expect.equals(0, response.contentLength);
- response.inputStream.onData = response.inputStream.read;
- response.inputStream.onClosed = () {
- if (++count == totalConnections) {
- client.shutdown();
- server.close();
- }
- };
- };
- }
+ int count = 0;
+ HttpClient client = new HttpClient();
+ for (int i = 0; i < totalConnections; i++) {
+ client.get("127.0.0.1", server.port, "/")
+ .then((request) {
+ if (explicitContentLength) {
+ request.contentLength = 0;
+ }
+ return request.close();
+ })
+ .then((response) {
+ Expect.equals("0", response.headers.value('content-length'));
+ Expect.equals(0, response.contentLength);
+ response.listen(
+ (d) {},
+ onDone: () {
+ if (++count == totalConnections) {
+ client.close();
+ server.close();
+ }
+ });
+ })
+ .catchError((e) {
+ Expect.fail("Unexpected error $e");
+ });
+ }
+ });
}
void testBody(int totalConnections, bool useHeader) {
- HttpServer server = new HttpServer();
- server.onError = (e) => Expect.fail("Unexpected error $e");
- server.listen("127.0.0.1", 0, backlog: totalConnections);
- server.defaultRequestHandler = (HttpRequest request, HttpResponse response) {
- Expect.equals("2", request.headers.value('content-length'));
- Expect.equals(2, request.contentLength);
- if (useHeader) {
- response.contentLength = 2;
- } else {
- response.headers.set("content-length", 2);
- }
- request.inputStream.onData = request.inputStream.read;
- request.inputStream.onClosed = () {
- OutputStream stream = response.outputStream;
- stream.writeString("x");
- Expect.throws(() => response.contentLength = 3, (e) => e is HttpException);
- stream.writeString("x");
- Expect.throws(() => stream.writeString("x"), (e) => e is HttpException);
- stream.close();
- Expect.throws(() => stream.writeString("x"), (e) => e is HttpException);
- };
- };
+ HttpServer.bind("127.0.0.1", 0, totalConnections).then((server) {
+ server.listen(
+ (HttpRequest request) {
+ Expect.equals("2", request.headers.value('content-length'));
+ Expect.equals(2, request.contentLength);
+ var response = request.response;
+ if (useHeader) {
+ response.contentLength = 2;
+ } else {
+ response.headers.set("content-length", 2);
+ }
+ request.listen(
+ (d) {},
+ onDone: () {
+ response.addString("x");
+ Expect.throws(() => response.contentLength = 3,
+ (e) => e is HttpException);
+ response.addString("x");
+ response.addString("x");
+ response.done
+ .then((_) {
+ Expect.fail("Unexpected successful response completion");
+ })
+ .catchError((e) {
+ Expect.isTrue(e.error is HttpException);
+ });
+ response.close();
+ Expect.throws(() => response.addString("x"),
+ (e) => e is StateError);
+ });
+ },
+ onError: (e) => Expect.fail("Unexpected error $e"));
- int count = 0;
- HttpClient client = new HttpClient();
- for (int i = 0; i < totalConnections; i++) {
- HttpClientConnection conn = client.get("127.0.0.1", server.port, "/");
- conn.onError = (e) => Expect.fail("Unexpected error $e");
- conn.onRequest = (HttpClientRequest request) {
- if (useHeader) {
- request.contentLength = 2;
- } else {
- request.headers.add(HttpHeaders.CONTENT_LENGTH, "7");
- request.headers.add(HttpHeaders.CONTENT_LENGTH, "2");
- }
- OutputStream stream = request.outputStream;
- stream.writeString("x");
- Expect.throws(() => request.contentLength = 3, (e) => e is HttpException);
- stream.writeString("x");
- Expect.throws(() => stream.writeString("x"), (e) => e is HttpException);
- stream.close();
- Expect.throws(() => stream.writeString("x"), (e) => e is HttpException);
- };
- conn.onResponse = (HttpClientResponse response) {
- Expect.equals("2", response.headers.value('content-length'));
- Expect.equals(2, response.contentLength);
- response.inputStream.onData = response.inputStream.read;
- response.inputStream.onClosed = () {
- if (++count == totalConnections) {
- client.shutdown();
- server.close();
- }
- };
- };
- }
+ int count = 0;
+ HttpClient client = new HttpClient();
+ for (int i = 0; i < totalConnections; i++) {
+ client.get("127.0.0.1", server.port, "/")
+ .then((request) {
+ if (useHeader) {
+ request.contentLength = 2;
+ } else {
+ request.headers.add(HttpHeaders.CONTENT_LENGTH, "7");
+ request.headers.add(HttpHeaders.CONTENT_LENGTH, "2");
+ }
+ request.addString("x");
+ Expect.throws(() => request.contentLength = 3,
+ (e) => e is HttpException);
+ request.addString("x");
+ return request.close();
+ })
+ .then((response) {
+ Expect.equals("2", response.headers.value('content-length'));
+ Expect.equals(2, response.contentLength);
+ response.listen(
+ (d) {},
+ onDone: () {
+ if (++count == totalConnections) {
+ client.close();
+ server.close();
+ }
+ });
+ });
+ }
+ });
}
void testBodyChunked(int totalConnections, bool useHeader) {
- HttpServer server = new HttpServer();
- server.onError = (e) => Expect.fail("Unexpected error $e");
- server.listen("127.0.0.1", 0, backlog: totalConnections);
- server.defaultRequestHandler = (HttpRequest request, HttpResponse response) {
- Expect.isNull(request.headers.value('content-length'));
- Expect.equals(-1, request.contentLength);
- if (useHeader) {
- response.contentLength = 2;
- response.headers.chunkedTransferEncoding = true;
- } else {
- response.headers.set("content-length", 2);
- response.headers.set("transfer-encoding", "chunked");
- }
- request.inputStream.onData = request.inputStream.read;
- request.inputStream.onClosed = () {
- OutputStream stream = response.outputStream;
- stream.writeString("x");
- Expect.throws(() => response.headers.chunkedTransferEncoding = false,
+ HttpServer.bind("127.0.0.1", 0, totalConnections).then((server) {
+ server.listen(
+ (HttpRequest request) {
+ Expect.isNull(request.headers.value('content-length'));
+ Expect.equals(-1, request.contentLength);
+ var response = request.response;
+ if (useHeader) {
+ response.contentLength = 2;
+ response.headers.chunkedTransferEncoding = true;
+ } else {
+ response.headers.set("content-length", 2);
+ response.headers.set("transfer-encoding", "chunked");
+ }
+ request.listen(
+ (d) {},
+ onDone: () {
+ response.addString("x");
+ Expect.throws(
+ () => response.headers.chunkedTransferEncoding = false,
(e) => e is HttpException);
- stream.writeString("x");
- stream.writeString("x");
- stream.close();
- Expect.throws(() => stream.writeString("x"), (e) => e is HttpException);
- };
- };
+ response.addString("x");
+ response.addString("x");
+ response.close();
+ Expect.throws(() => response.addString("x"),
+ (e) => e is StateError);
+ });
+ },
+ onError: (e) => Expect.fail("Unexpected error $e"));
- int count = 0;
- HttpClient client = new HttpClient();
- for (int i = 0; i < totalConnections; i++) {
- HttpClientConnection conn = client.get("127.0.0.1", server.port, "/");
- conn.onError = (e) => Expect.fail("Unexpected error $e");
- conn.onRequest = (HttpClientRequest request) {
- if (useHeader) {
- request.contentLength = 2;
- request.headers.chunkedTransferEncoding = true;
- } else {
- request.headers.add(HttpHeaders.CONTENT_LENGTH, "2");
- request.headers.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
- }
- OutputStream stream = request.outputStream;
- stream.writeString("x");
- Expect.throws(() => request.headers.chunkedTransferEncoding = false,
- (e) => e is HttpException);
- stream.writeString("x");
- stream.writeString("x");
- stream.close();
- Expect.throws(() => stream.writeString("x"), (e) => e is HttpException);
- };
- conn.onResponse = (HttpClientResponse response) {
- Expect.isNull(response.headers.value('content-length'));
- Expect.equals(-1, response.contentLength);
- response.inputStream.onData = response.inputStream.read;
- response.inputStream.onClosed = () {
- if (++count == totalConnections) {
- client.shutdown();
- server.close();
- }
- };
- };
- }
+ int count = 0;
+ HttpClient client = new HttpClient();
+ for (int i = 0; i < totalConnections; i++) {
+ client.get("127.0.0.1", server.port, "/")
+ .then((request) {
+ if (useHeader) {
+ request.contentLength = 2;
+ request.headers.chunkedTransferEncoding = true;
+ } else {
+ request.headers.add(HttpHeaders.CONTENT_LENGTH, "2");
+ request.headers.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
+ }
+ request.addString("x");
+ Expect.throws(() => request.headers.chunkedTransferEncoding = false,
+ (e) => e is HttpException);
+ request.addString("x");
+ request.addString("x");
+ return request.close();
+ })
+ .then((response) {
+ Expect.isNull(response.headers.value('content-length'));
+ Expect.equals(-1, response.contentLength);
+ response.listen(
+ (d) {},
+ onDone: () {
+ if (++count == totalConnections) {
+ client.close();
+ server.close();
+ }
+ });
+ })
+ .catchError((e) => Expect.fail("Unexpected error $e"));
+ }
+ });
}
void testHttp10() {
- HttpServer server = new HttpServer();
- server.onError = (e) => Expect.fail("Unexpected error $e");
- server.listen("127.0.0.1", 0, backlog: 5);
- server.defaultRequestHandler = (HttpRequest request, HttpResponse response) {
- Expect.isNull(request.headers.value('content-length'));
- Expect.equals(-1, request.contentLength);
- response.contentLength = 0;
- OutputStream stream = response.outputStream;
- Expect.equals("1.0", request.protocolVersion);
- Expect.throws(() => stream.writeString("x"), (e) => e is HttpException);
- stream.close();
- };
+ HttpServer.bind("127.0.0.1", 0, 5).then((server) {
+ server.listen(
+ (HttpRequest request) {
+ Expect.isNull(request.headers.value('content-length'));
+ Expect.equals(-1, request.contentLength);
+ var response = request.response;
+ response.contentLength = 0;
+ Expect.equals("1.0", request.protocolVersion);
+ response.done
+ .then((_) => Expect.fail("Unexpected response completion"))
+ .catchError((e) => Expect.isTrue(e.error is HttpException));
+ response.addString("x");
+ response.close();
+ Expect.throws(() => response.addString("x"),
+ (e) => e is StateError);
+ },
+ onError: (e) => Expect.fail("Unexpected error $e"));
- Socket socket = new Socket("127.0.0.1", server.port);
- socket.onConnect = () {
- List<int> buffer = new List<int>.fixedLength(1024);
- socket.outputStream.writeString("GET / HTTP/1.0\r\n\r\n");
- socket.onData = () => socket.readList(buffer, 0, buffer.length);
- socket.onClosed = () {
- socket.close(true);
- server.close();
- };
- };
+ Socket.connect("127.0.0.1", server.port).then((socket) {
+ socket.addString("GET / HTTP/1.0\r\n\r\n");
+ socket.close();
+ socket.listen(
+ (d) { },
+ onDone: () {
+ socket.destroy();
+ server.close();
+ });
+ });
+ });
}
void main() {
diff --git a/tests/standalone/io/http_date_test.dart b/tests/standalone/io/http_date_test.dart
index c5b36e2..5821e05 100644
--- a/tests/standalone/io/http_date_test.dart
+++ b/tests/standalone/io/http_date_test.dart
@@ -5,15 +5,12 @@
import "dart:async";
import "dart:math";
-part "../../../sdk/lib/io/input_stream.dart";
-part "../../../sdk/lib/io/output_stream.dart";
-part "../../../sdk/lib/io/chunked_stream.dart";
-part "../../../sdk/lib/io/string_stream.dart";
-part "../../../sdk/lib/io/stream_util.dart";
+part "../../../sdk/lib/io/io_stream_consumer.dart";
part "../../../sdk/lib/io/http.dart";
part "../../../sdk/lib/io/http_impl.dart";
part "../../../sdk/lib/io/http_parser.dart";
part "../../../sdk/lib/io/http_utils.dart";
+part "../../../sdk/lib/io/socket.dart";
void testParseHttpDate() {
DateTime date;
diff --git a/tests/standalone/io/http_detach_socket_test.dart b/tests/standalone/io/http_detach_socket_test.dart
new file mode 100644
index 0000000..e3a5ad5
--- /dev/null
+++ b/tests/standalone/io/http_detach_socket_test.dart
@@ -0,0 +1,113 @@
+// 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.
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
+
+import "dart:io";
+import "dart:isolate";
+
+void testServerDetachSocket() {
+ HttpServer.bind().then((server) {
+ server.listen((request) {
+ var response = request.response;
+ response.contentLength = 0;
+ response.detachSocket().then((socket) {
+ Expect.isNotNull(socket);
+ var body = new StringBuffer();
+ socket.listen(
+ (data) => body.add(new String.fromCharCodes(data)),
+ onDone: () => Expect.equals("Some data", body.toString()));
+ socket.addString("Test!");
+ socket.close();
+ });
+ server.close();
+ });
+
+ Socket.connect("127.0.0.1", server.port).then((socket) {
+ socket.addString("GET / HTTP/1.1\r\n"
+ "content-length: 0\r\n\r\n"
+ "Some data");
+ var body = new StringBuffer();
+ socket.listen(
+ (data) => body.add(new String.fromCharCodes(data)),
+ onDone: () {
+ Expect.equals("HTTP/1.1 200 OK\r\n"
+ "content-length: 0\r\n"
+ "\r\n"
+ "Test!",
+ body.toString());
+ socket.close();
+ });
+ });
+ });
+}
+
+void testBadServerDetachSocket() {
+ HttpServer.bind().then((server) {
+ server.listen((request) {
+ var response = request.response;
+ response.contentLength = 0;
+ response.close();
+ Expect.throws(response.detachSocket);
+ server.close();
+ });
+
+ Socket.connect("127.0.0.1", server.port).then((socket) {
+ socket.addString("GET / HTTP/1.1\r\n"
+ "content-length: 0\r\n\r\n");
+ socket.listen((_) {}, onDone: () {
+ socket.close();
+ });
+ });
+ });
+}
+
+void testClientDetachSocket() {
+ ServerSocket.bind().then((server) {
+ server.listen((socket) {
+ socket.addString("HTTP/1.1 200 OK\r\n"
+ "\r\n"
+ "Test!");
+ var body = new StringBuffer();
+ socket.listen(
+ (data) => body.add(new String.fromCharCodes(data)),
+ onDone: () {
+ Expect.equals("GET / HTTP/1.1\r\n"
+ "content-length: 0\r\n"
+ "host: 127.0.0.1:${server.port}\r\n\r\n"
+ "Some data",
+ body.toString());
+ socket.close();
+ });
+ server.close();
+ });
+
+ var client = new HttpClient();
+ client.get("127.0.0.1", server.port, "/")
+ .then((request) => request.close())
+ .then((response) {
+ response.detachSocket().then((socket) {
+ var body = new StringBuffer();
+ socket.listen(
+ (data) => body.add(new String.fromCharCodes(data)),
+ onDone: () {
+ Expect.equals("Test!",
+ body.toString());
+ client.close();
+ });
+ socket.addString("Some data");
+ socket.close();
+ });
+ });
+ });
+}
+
+void main() {
+ testServerDetachSocket();
+ testBadServerDetachSocket();
+ testClientDetachSocket();
+}
diff --git a/tests/standalone/io/http_head_test.dart b/tests/standalone/io/http_head_test.dart
index 7880e2d..215872c 100644
--- a/tests/standalone/io/http_head_test.dart
+++ b/tests/standalone/io/http_head_test.dart
@@ -1,52 +1,47 @@
-// Copyright (c) 2012, 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.
import "dart:io";
void testHEAD(int totalConnections) {
- HttpServer server = new HttpServer();
- server.onError = (e) => Expect.fail("Unexpected error $e");
- server.listen("127.0.0.1", 0, backlog: totalConnections);
- server.addRequestHandler(
- (request) => request.path == "/test100",
- (HttpRequest request, HttpResponse response) {
- response.contentLength = 100;
- response.outputStream.close();
- });
- server.addRequestHandler(
- (request) => request.path == "/test200",
- (HttpRequest request, HttpResponse response) {
- response.contentLength = 200;
- List<int> data = new List<int>.fixedLength(200);
- response.outputStream.write(data);
- response.outputStream.close();
- });
-
- HttpClient client = new HttpClient();
-
- int count = 0;
- for (int i = 0; i < totalConnections; i++) {
- HttpClientConnection conn;
- int len = (i % 2 == 0) ? 100 : 200;
- if (i % 2 == 0) {
- conn = client.open("HEAD", "127.0.0.1", server.port, "/test$len");
- } else {
- conn = client.open("HEAD", "127.0.0.1", server.port, "/test$len");
- }
- conn.onError = (e) => Expect.fail("Unexpected error $e");
- conn.onResponse = (HttpClientResponse response) {
- Expect.equals(len, response.contentLength);
- response.inputStream.onData = () => Expect.fail("Data from HEAD request");
- response.inputStream.onClosed = () {
- count++;
- if (count == totalConnections) {
- client.shutdown();
- server.close();
+ HttpServer.bind().then((server) {
+ server.listen((request) {
+ var response = request.response;
+ if (request.uri.path == "/test100") {
+ response.contentLength = 100;
+ response.close();
+ } else if (request.uri.path == "/test200") {
+ response.contentLength = 200;
+ List<int> data = new List<int>.fixedLength(200, fill: 0);
+ response.add(data);
+ response.close();
+ } else {
+ assert(false);
}
- };
- };
- }
+ });
+
+ HttpClient client = new HttpClient();
+
+ int count = 0;
+ for (int i = 0; i < totalConnections; i++) {
+ int len = (i % 2 == 0) ? 100 : 200;
+ client.open("HEAD", "127.0.0.1", server.port, "/test$len")
+ .then((request) => request.close())
+ .then((HttpClientResponse response) {
+ Expect.equals(len, response.contentLength);
+ response.listen(
+ (_) => Expect.fail("Data from HEAD request"),
+ onDone: () {
+ count++;
+ if (count == totalConnections) {
+ client.close();
+ server.close();
+ }
+ });
+ });
+ }
+ });
}
void main() {
diff --git a/tests/standalone/io/http_headers_state_test.dart b/tests/standalone/io/http_headers_state_test.dart
index 72e1ddd..73754c7 100644
--- a/tests/standalone/io/http_headers_state_test.dart
+++ b/tests/standalone/io/http_headers_state_test.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
//
@@ -7,71 +7,71 @@
import "dart:io";
void test(int totalConnections, [String body]) {
- HttpServer server = new HttpServer();
- server.onError = (e) => Expect.fail("Unexpected error $e");
- server.listen("127.0.0.1", 0, backlog: totalConnections);
- server.defaultRequestHandler = (HttpRequest request, HttpResponse response) {
- // Cannot mutate request headers.
- Expect.throws(() => request.headers.add("X-Request-Header", "value"),
- (e) => e is HttpException);
- Expect.equals("value", request.headers.value("X-Request-Header"));
- request.inputStream.onData = request.inputStream.read;
- request.inputStream.onClosed = () {
- OutputStream stream = response.outputStream;
- // Can still mutate response headers as long as no data has been sent.
- response.headers.add("X-Response-Header", "value");
- if (body != null) {
- stream.writeString(body);
- // Cannot mutate response headers when data has been sent.
- Expect.throws(() => request.headers.add("X-Request-Header", "value2"),
- (e) => e is HttpException);
- }
- stream.close();
- // Cannot mutate response headers when data has been sent.
- Expect.throws(() => request.headers.add("X-Request-Header", "value3"),
- (e) => e is HttpException);
- };
- };
+ HttpServer.bind().then((server) {
- int count = 0;
- HttpClient client = new HttpClient();
- for (int i = 0; i < totalConnections; i++) {
- HttpClientConnection conn = client.get("127.0.0.1", server.port, "/");
- conn.onError = (e) => Expect.fail("Unexpected error $e");
- conn.onRequest = (HttpClientRequest request) {
- if (body != null) {
- request.contentLength = -1;
- }
- OutputStream stream = request.outputStream;
- // Can still mutate request headers as long as no data has been sent.
- request.headers.add("X-Request-Header", "value");
- if (body != null) {
- stream.writeString(body);
- // Cannot mutate request headers when data has been sent.
- Expect.throws(() => request.headers.add("X-Request-Header", "value2"),
- (e) => e is HttpException);
- }
- stream.close();
- // Cannot mutate request headers when data has been sent.
- Expect.throws(() => request.headers.add("X-Request-Header", "value3"),
+ server.listen((HttpRequest request) {
+ HttpResponse response = request.response;
+ // Cannot mutate request headers.
+ Expect.throws(() => request.headers.add("X-Request-Header", "value"),
(e) => e is HttpException);
- };
- conn.onResponse = (HttpClientResponse response) {
- // Cannot mutate response headers.
- Expect.throws(() => response.headers.add("X-Response-Header", "value"),
- (e) => e is HttpException);
- Expect.equals("value", response.headers.value("X-Response-Header"));
- response.inputStream.onData = response.inputStream.read;
- response.inputStream.onClosed = () {
- // Do not close the connections before we have read the full response
- // bodies for all connections.
- if (++count == totalConnections) {
- client.shutdown();
- server.close();
+ Expect.equals("value", request.headers.value("X-Request-Header"));
+ request.listen((_) {}, onDone: () {
+ // Can still mutate response headers as long as no data has been sent.
+ response.headers.add("X-Response-Header", "value");
+ if (body != null) {
+ response.addString(body);
+ // Cannot mutate response headers when data has been sent.
+ Expect.throws(() => request.headers.add("X-Request-Header", "value2"),
+ (e) => e is HttpException);
}
- };
- };
- }
+ response..close();
+ // Cannot mutate response headers when data has been sent.
+ Expect.throws(() => request.headers.add("X-Request-Header", "value3"),
+ (e) => e is HttpException);
+ });
+ });
+
+ int count = 0;
+ HttpClient client = new HttpClient();
+ for (int i = 0; i < totalConnections; i++) {
+ client.get("127.0.0.1", server.port, "/")
+ .then((HttpClientRequest request) {
+ if (body != null) {
+ request.contentLength = -1;
+ }
+ // Can still mutate request headers as long as no data has been sent.
+ request.headers.add("X-Request-Header", "value");
+ if (body != null) {
+ request.addString(body);
+ // Cannot mutate request headers when data has been sent.
+ Expect.throws(
+ () => request.headers.add("X-Request-Header", "value2"),
+ (e) => e is HttpException);
+ }
+ request.close();
+ // Cannot mutate request headers when data has been sent.
+ Expect.throws(() => request.headers.add("X-Request-Header", "value3"),
+ (e) => e is HttpException);
+ return request.response;
+ })
+ .then((HttpClientResponse response) {
+ // Cannot mutate response headers.
+ Expect.throws(
+ () => response.headers.add("X-Response-Header", "value"),
+ (e) => e is HttpException);
+ Expect.equals("value", response.headers.value("X-Response-Header"));
+ response.listen((_) {}, onDone: () {
+ // Do not close the connections before we have read the
+ // full response bodies for all connections.
+ if (++count == totalConnections) {
+ client.close();
+ server.close();
+ }
+ });
+ });
+ }
+
+ });
}
void main() {
diff --git a/tests/standalone/io/http_headers_test.dart b/tests/standalone/io/http_headers_test.dart
index 826bd81..fca7a49 100644
--- a/tests/standalone/io/http_headers_test.dart
+++ b/tests/standalone/io/http_headers_test.dart
@@ -5,19 +5,16 @@
import 'dart:async';
import 'dart:math';
-part "../../../sdk/lib/io/input_stream.dart";
-part "../../../sdk/lib/io/output_stream.dart";
-part "../../../sdk/lib/io/chunked_stream.dart";
-part "../../../sdk/lib/io/string_stream.dart";
-part "../../../sdk/lib/io/stream_util.dart";
+part "../../../sdk/lib/io/io_stream_consumer.dart";
part "../../../sdk/lib/io/http.dart";
part "../../../sdk/lib/io/http_headers.dart";
part "../../../sdk/lib/io/http_impl.dart";
part "../../../sdk/lib/io/http_parser.dart";
part "../../../sdk/lib/io/http_utils.dart";
+part "../../../sdk/lib/io/socket.dart";
void testMultiValue() {
- _HttpHeaders headers = new _HttpHeaders();
+ _HttpHeaders headers = new _HttpHeaders("1.1");
Expect.isNull(headers[HttpHeaders.PRAGMA]);
headers.add(HttpHeaders.PRAGMA, "pragma1");
Expect.equals(1, headers[HttpHeaders.PRAGMA].length);
@@ -59,7 +56,7 @@
DateTime date2 = new DateTime.utc(2000, DateTime.AUG, 16, 12, 34, 56, 0);
String httpDate2 = "Wed, 16 Aug 2000 12:34:56 GMT";
- _HttpHeaders headers = new _HttpHeaders();
+ _HttpHeaders headers = new _HttpHeaders("1.1");
Expect.isNull(headers.date);
headers.date = date1;
Expect.equals(date1, headers.date);
@@ -85,7 +82,7 @@
DateTime date2 = new DateTime.utc(2000, DateTime.AUG, 16, 12, 34, 56, 0);
String httpDate2 = "Wed, 16 Aug 2000 12:34:56 GMT";
- _HttpHeaders headers = new _HttpHeaders();
+ _HttpHeaders headers = new _HttpHeaders("1.1");
Expect.isNull(headers.expires);
headers.expires = date1;
Expect.equals(date1, headers.expires);
@@ -111,7 +108,7 @@
DateTime date2 = new DateTime.utc(2000, DateTime.AUG, 16, 12, 34, 56, 0);
String httpDate2 = "Wed, 16 Aug 2000 12:34:56 GMT";
- _HttpHeaders headers = new _HttpHeaders();
+ _HttpHeaders headers = new _HttpHeaders("1.1");
Expect.isNull(headers.ifModifiedSince);
headers.ifModifiedSince = date1;
Expect.equals(date1, headers.ifModifiedSince);
@@ -133,7 +130,7 @@
void testHost() {
String host = "www.google.com";
- _HttpHeaders headers = new _HttpHeaders();
+ _HttpHeaders headers = new _HttpHeaders("1.1");
Expect.isNull(headers.host);
Expect.isNull(headers.port);
headers.host = host;
@@ -143,7 +140,7 @@
headers.port = HttpClient.DEFAULT_HTTP_PORT;
Expect.equals(host, headers.value(HttpHeaders.HOST));
- headers = new _HttpHeaders();
+ headers = new _HttpHeaders("1.1");
headers.add(HttpHeaders.HOST, host);
Expect.equals(host, headers.host);
Expect.equals(HttpClient.DEFAULT_HTTP_PORT, headers.port);
@@ -152,13 +149,13 @@
Expect.equals(host, headers.host);
Expect.equals(4567, headers.port);
- headers = new _HttpHeaders();
+ headers = new _HttpHeaders("1.1");
headers.add(HttpHeaders.HOST, "$host:xxx");
Expect.equals("$host:xxx", headers.value(HttpHeaders.HOST));
Expect.equals(host, headers.host);
Expect.isNull(headers.port);
- headers = new _HttpHeaders();
+ headers = new _HttpHeaders("1.1");
headers.add(HttpHeaders.HOST, ":1234");
Expect.equals(":1234", headers.value(HttpHeaders.HOST));
Expect.isNull(headers.host);
@@ -166,7 +163,7 @@
}
void testEnumeration() {
- _HttpHeaders headers = new _HttpHeaders();
+ _HttpHeaders headers = new _HttpHeaders("1.1");
Expect.isNull(headers[HttpHeaders.PRAGMA]);
headers.add("My-Header-1", "value 1");
headers.add("My-Header-2", "value 2");
@@ -287,7 +284,7 @@
}
void testContentTypeCache() {
- _HttpHeaders headers = new _HttpHeaders();
+ _HttpHeaders headers = new _HttpHeaders("1.1");
headers.set(HttpHeaders.CONTENT_TYPE, "text/html");
Expect.equals("text", headers.contentType.primaryType);
Expect.equals("html", headers.contentType.subType);
diff --git a/tests/standalone/io/http_keep_alive_test.dart b/tests/standalone/io/http_keep_alive_test.dart
new file mode 100644
index 0000000..70b9021
--- /dev/null
+++ b/tests/standalone/io/http_keep_alive_test.dart
@@ -0,0 +1,96 @@
+// 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.
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
+
+import "dart:async";
+import "dart:io";
+
+Future getData(HttpClient client, int port, bool chunked, int length) {
+ return client.get("localhost", port, "/?chunked=$chunked&length=$length")
+ .then((request) => request.close())
+ .then((response) {
+ return response.reduce(0, (bytes, data) => bytes + data.length)
+ .then((bytes) {
+ Expect.equals(length, bytes);
+ });
+ });
+}
+
+Future<HttpServer> startServer() {
+ return HttpServer.bind("127.0.0.1", 0).then((server) {
+ server.listen((request) {
+ bool chunked = request.queryParameters["chunked"] == "true";
+ int length = int.parse(request.queryParameters["length"]);
+ var buffer = new List.fixedLength(length, fill: 0);
+ if (!chunked) request.response.contentLength = length;
+ request.response.add(buffer);
+ request.response.close();
+ });
+ return server;
+ });
+}
+
+testKeepAliveNonChunked() {
+ startServer().then((server) {
+ var client = new HttpClient();
+
+ getData(client, server.port, false, 100)
+ .then((_) => getData(client, server.port, false, 100))
+ .then((_) => getData(client, server.port, false, 100))
+ .then((_) => getData(client, server.port, false, 100))
+ .then((_) => getData(client, server.port, false, 100))
+ .then((_) {
+ server.close();
+ client.close();
+ });
+
+ });
+}
+
+testKeepAliveChunked() {
+ startServer().then((server) {
+ var client = new HttpClient();
+
+ getData(client, server.port, true, 100)
+ .then((_) => getData(client, server.port, true, 100))
+ .then((_) => getData(client, server.port, true, 100))
+ .then((_) => getData(client, server.port, true, 100))
+ .then((_) => getData(client, server.port, true, 100))
+ .then((_) {
+ server.close();
+ client.close();
+ });
+
+ });
+}
+
+testKeepAliveMixed() {
+ startServer().then((server) {
+ var client = new HttpClient();
+
+ getData(client, server.port, true, 100)
+ .then((_) => getData(client, server.port, false, 100))
+ .then((_) => getData(client, server.port, true, 100))
+ .then((_) => getData(client, server.port, false, 100))
+ .then((_) => getData(client, server.port, true, 100))
+ .then((_) => getData(client, server.port, false, 100))
+ .then((_) => getData(client, server.port, true, 100))
+ .then((_) => getData(client, server.port, false, 100))
+ .then((_) {
+ server.close();
+ client.close();
+ });
+
+ });
+}
+
+void main() {
+ testKeepAliveNonChunked();
+ testKeepAliveChunked();
+ testKeepAliveMixed();
+}
diff --git a/tests/standalone/io/http_parser_test.dart b/tests/standalone/io/http_parser_test.dart
index fe4018a..173fec4 100644
--- a/tests/standalone/io/http_parser_test.dart
+++ b/tests/standalone/io/http_parser_test.dart
@@ -1,14 +1,19 @@
-// Copyright (c) 2012, 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.
import 'dart:async';
import 'dart:math';
import 'dart:scalarlist';
+import 'dart:isolate';
+import 'dart:uri';
+part '../../../sdk/lib/io/io_stream_consumer.dart';
part '../../../sdk/lib/io/http.dart';
+part '../../../sdk/lib/io/http_impl.dart';
part '../../../sdk/lib/io/http_headers.dart';
part '../../../sdk/lib/io/http_parser.dart';
+part '../../../sdk/lib/io/socket.dart';
class HttpParserTest {
static void runAllTests() {
@@ -21,7 +26,7 @@
static void _testParseRequest(String request,
String expectedMethod,
String expectedUri,
- {int expectedContentLength: 0,
+ {int expectedTransferLength: 0,
int expectedBytesReceived: 0,
Map expectedHeaders: null,
bool chunked: false,
@@ -29,88 +34,93 @@
int unparsedLength: 0,
bool connectionClose: false,
String expectedVersion: "1.1"}) {
- _HttpParser httpParser;
- bool headersCompleteCalled;
- bool dataEndCalled;
- String method;
- String uri;
- String version;
- HttpHeaders headers;
- int contentLength;
- int bytesReceived;
-
+ StreamController controller;
void reset() {
- httpParser = new _HttpParser.requestParser();
- httpParser.requestStart = (m, u, v, h, b) {
- method = m;
- uri = u;
- version = v;
- headers = h;
- headersCompleteCalled = true;
+ _HttpParser httpParser = new _HttpParser.requestParser();
+ controller = new StreamController();
+ var port1 = new ReceivePort();
+ var port2 = new ReceivePort();
+
+ String method;
+ Uri uri;
+ HttpHeaders headers;
+ int contentLength;
+ int bytesReceived;
+ int unparsedBytesReceived;
+ bool upgraded;
+
+ controller.stream.pipe(httpParser);
+ var subscription = httpParser.listen((incoming) {
+ method = incoming.method;
+ uri = incoming.uri;
+ headers = incoming.headers;
+ upgraded = incoming.upgraded;
+ Expect.equals(upgrade, upgraded);
+
if (!chunked) {
- Expect.equals(expectedContentLength, httpParser.contentLength);
+ Expect.equals(expectedTransferLength, incoming.transferLength);
} else {
- Expect.equals(-1, httpParser.contentLength);
+ Expect.equals(-1, incoming.transferLength);
}
if (expectedHeaders != null) {
expectedHeaders.forEach(
(String name, String value) =>
Expect.equals(value, headers[name][0]));
}
- Expect.equals(upgrade, httpParser.upgrade);
- Expect.equals(connectionClose, !httpParser.persistentConnection);
- headersCompleteCalled = true;
- };
- httpParser.responseStart = (s, r, v, h, b) {
- Expect.fail("Expected request");
- };
- httpParser.dataReceived = (List<int> data) {
- Expect.isTrue(headersCompleteCalled);
- bytesReceived += data.length;
- };
- httpParser.dataEnd = (close) {
- Expect.isFalse(close);
- dataEndCalled = true;
- };
- httpParser.closed = () { };
+ incoming.listen(
+ (List<int> data) {
+ Expect.isFalse(upgraded);
+ bytesReceived += data.length;
+ },
+ onDone: () {
+ Expect.isFalse(upgraded);
+ port2.close();
+ });
- headersCompleteCalled = false;
- dataEndCalled = false;
+ if (upgraded) {
+ port1.close();
+ httpParser.detachIncoming().listen(
+ (List<int> data) {
+ unparsedBytesReceived += data.length;
+ },
+ onDone: () {
+ Expect.equals(unparsedLength, unparsedBytesReceived);
+ port2.close();
+ });
+ }
+
+ incoming.dataDone.then((_) {
+ port1.close();
+ Expect.isFalse(upgraded);
+ Expect.equals(expectedMethod, method);
+ Expect.stringEquals(expectedUri, uri.toString());
+ Expect.equals(expectedVersion, headers.protocolVersion);
+ if (upgrade) {
+ Expect.equals(0, bytesReceived);
+ // port1 is closed by the listener on the detached data.
+ } else {
+ Expect.equals(expectedBytesReceived, bytesReceived);
+ }
+ });
+ });
+
method = null;
uri = null;
headers = null;
bytesReceived = 0;
+ unparsedBytesReceived = 0;
+ upgraded = false;
}
void testWrite(List<int> requestData, [int chunkSize = -1]) {
if (chunkSize == -1) chunkSize = requestData.length;
reset();
- int written = 0;
- int unparsed;
for (int pos = 0; pos < requestData.length; pos += chunkSize) {
int remaining = requestData.length - pos;
int writeLength = min(chunkSize, remaining);
- written += writeLength;
- httpParser.streamData(requestData.getRange(pos, writeLength));
- unparsed = httpParser.readUnparsedData().length;
- if (httpParser.upgrade) {
- unparsed += requestData.length - written;
- break;
- } else {
- Expect.equals(0, unparsed);
- }
+ controller.add(requestData.getRange(pos, writeLength));
}
- Expect.equals(expectedMethod, method);
- Expect.equals(expectedUri, uri);
- Expect.equals(expectedVersion, version);
- Expect.isTrue(headersCompleteCalled);
- Expect.equals(expectedBytesReceived, bytesReceived);
- if (!upgrade) Expect.isTrue(dataEndCalled);
- if (unparsedLength == 0) {
- Expect.equals(0, unparsed);
- } else {
- Expect.equals(unparsedLength, unparsed);
- }
+ controller.close();
}
// Test parsing the request three times delivering the data in
@@ -124,17 +134,23 @@
static void _testParseInvalidRequest(String request) {
_HttpParser httpParser;
bool errorCalled;
+ StreamController controller;
void reset() {
httpParser = new _HttpParser.requestParser();
- httpParser.responseStart = (s, r, v, h, b) {
+ controller = new StreamController();
+ var port = new ReceivePort();
+ controller.stream.pipe(httpParser);
+ var subscription = httpParser.listen((incoming) {
Expect.fail("Expected request");
- };
- httpParser.error = (e) {
+ });
+ subscription.onError((e) {
errorCalled = true;
- };
- httpParser.closed = () { };
-
+ });
+ subscription.onDone(() {
+ port.close();
+ Expect.isTrue(errorCalled);
+ });
errorCalled = false;
}
@@ -146,9 +162,9 @@
pos += chunkSize) {
int remaining = requestData.length - pos;
int writeLength = min(chunkSize, remaining);
- httpParser.streamData(requestData.getRange(pos, writeLength));
+ controller.add(requestData.getRange(pos, writeLength));
}
- Expect.isTrue(errorCalled);
+ controller.close();
}
// Test parsing the request three times delivering the data in
@@ -162,7 +178,7 @@
static void _testParseResponse(String response,
int expectedStatusCode,
String expectedReasonPhrase,
- {int expectedContentLength: -1,
+ {int expectedTransferLength: 0,
int expectedBytesReceived: 0,
Map expectedHeaders: null,
bool chunked: false,
@@ -178,29 +194,27 @@
bool dataEndClose;
int statusCode;
String reasonPhrase;
- String version;
HttpHeaders headers;
int contentLength;
int bytesReceived;
+ StreamController controller;
+ bool upgraded;
void reset() {
httpParser = new _HttpParser.responseParser();
- if (responseToMethod != null) {
- httpParser.responseToMethod = responseToMethod;
- }
- httpParser.requestStart = (m, u, v, h, b) {
- Expect.fail("Expected response");
- };
- httpParser.responseStart = (s, r, v, h, b) {
- statusCode = s;
- reasonPhrase = r;
- version = v;
- headers = h;
+ controller = new StreamController();
+ var port = new ReceivePort();
+ controller.stream.pipe(httpParser);
+ var subscription = httpParser.listen((incoming) {
+ port.close();
+ statusCode = incoming.statusCode;
+ reasonPhrase = incoming.reasonPhrase;
+ headers = incoming.headers;
Expect.isFalse(headersCompleteCalled);
if (!chunked && !close) {
- Expect.equals(expectedContentLength, httpParser.contentLength);
+ Expect.equals(expectedTransferLength, incoming.transferLength);
} else {
- Expect.equals(-1, httpParser.contentLength);
+ Expect.equals(-1, incoming.transferLength);
}
if (expectedHeaders != null) {
expectedHeaders.forEach((String name, String value) {
@@ -209,16 +223,29 @@
}
Expect.equals(upgrade, httpParser.upgrade);
headersCompleteCalled = true;
- };
- httpParser.dataReceived = (List<int> data) {
+ incoming.listen(
+ (List<int> data) {
+ Expect.isTrue(headersCompleteCalled);
+ bytesReceived += data.length;
+ },
+ onDone: () {
+ dataEndCalled = true;
+ dataEndClose = close;
+ });
+ });
+
+ subscription.onDone(() {
+ Expect.equals(expectedVersion, headers.protocolVersion);
+ Expect.equals(expectedStatusCode, statusCode);
+ Expect.equals(expectedReasonPhrase, reasonPhrase);
Expect.isTrue(headersCompleteCalled);
- bytesReceived += data.length;
- };
- httpParser.dataEnd = (close) {
- dataEndCalled = true;
- dataEndClose = close;
- };
- httpParser.closed = () { };
+ Expect.equals(expectedBytesReceived, bytesReceived);
+ if (!upgrade) {
+ Expect.isTrue(dataEndCalled);
+ if (close) Expect.isTrue(dataEndClose);
+ Expect.equals(dataEndClose, connectionClose);
+ }
+ });
headersCompleteCalled = false;
dataEndCalled = false;
@@ -232,37 +259,13 @@
void testWrite(List<int> requestData, [int chunkSize = -1]) {
if (chunkSize == -1) chunkSize = requestData.length;
reset();
- int written = 0;
- int unparsed;
for (int pos = 0; pos < requestData.length; pos += chunkSize) {
int remaining = requestData.length - pos;
int writeLength = min(chunkSize, remaining);
- written += writeLength;
- httpParser.streamData(requestData.getRange(pos, writeLength));
- unparsed = httpParser.readUnparsedData().length;
- if (httpParser.upgrade) {
- unparsed += requestData.length - written;
- break;
- } else {
- Expect.equals(0, unparsed);
- }
+ controller.add(requestData.getRange(pos, writeLength));
+
}
- if (close) httpParser.streamDone();
- Expect.equals(expectedVersion, version);
- Expect.equals(expectedStatusCode, statusCode);
- Expect.equals(expectedReasonPhrase, reasonPhrase);
- Expect.isTrue(headersCompleteCalled);
- Expect.equals(expectedBytesReceived, bytesReceived);
- if (!upgrade) {
- Expect.isTrue(dataEndCalled);
- if (close) Expect.isTrue(dataEndClose);
- Expect.equals(dataEndClose, connectionClose);
- }
- if (unparsedLength == 0) {
- Expect.equals(0, unparsed);
- } else {
- Expect.equals(unparsedLength, unparsed);
- }
+ if (close) controller.close();
}
// Test parsing the request three times delivering the data in
@@ -276,15 +279,29 @@
static void _testParseInvalidResponse(String response, [bool close = false]) {
_HttpParser httpParser;
bool errorCalled;
+ StreamController controller;
void reset() {
httpParser = new _HttpParser.responseParser();
- httpParser.requestStart = (m, u, v, h, b) {
- Expect.fail("Expected response");
- };
- httpParser.error = (e) => errorCalled = true;
- httpParser.closed = () { };
-
+ controller = new StreamController();
+ var port = new ReceivePort();
+ controller.stream.pipe(httpParser);
+ var subscription = httpParser.listen((incoming) {
+ incoming.listen(
+ (data) {},
+ onError: (e) {
+ Expect.isFalse(errorCalled);
+ errorCalled = true;
+ });
+ });
+ subscription.onError((e) {
+ Expect.isFalse(errorCalled);
+ errorCalled = true;
+ });
+ subscription.onDone(() {
+ port.close();
+ Expect.isTrue(errorCalled);
+ });
errorCalled = false;
}
@@ -296,10 +313,9 @@
pos += chunkSize) {
int remaining = requestData.length - pos;
int writeLength = min(chunkSize, remaining);
- httpParser.streamData(requestData.getRange(pos, writeLength));
+ controller.add(requestData.getRange(pos, writeLength));
}
- if (close) httpParser.streamDone();
- Expect.isTrue(errorCalled);
+ controller.close();
}
// Test parsing the request three times delivering the data in
@@ -329,6 +345,7 @@
_testParseRequest(request, method, "/index.html");
});
+
request = "GET / HTTP/1.0\r\n\r\n";
_testParseRequest(request, "GET", "/",
expectedVersion: "1.0",
@@ -406,7 +423,7 @@
_testParseRequest(request,
"POST",
"/test",
- expectedContentLength: 10,
+ expectedTransferLength: 10,
expectedBytesReceived: 10);
// Test connection close header.
@@ -426,7 +443,7 @@
_testParseRequest(request,
"POST",
"/test",
- expectedContentLength: -1,
+ expectedTransferLength: -1,
expectedBytesReceived: 10,
chunked: true);
@@ -445,7 +462,7 @@
_testParseRequest(request,
"POST",
"/test",
- expectedContentLength: -1,
+ expectedTransferLength: -1,
expectedBytesReceived: 10,
chunked: true);
@@ -464,7 +481,7 @@
_testParseRequest(request,
"POST",
"/test",
- expectedContentLength: -1,
+ expectedTransferLength: -1,
expectedBytesReceived: 10,
chunked: true);
@@ -481,7 +498,7 @@
_testParseRequest(request,
"POST",
"/test",
- expectedContentLength: -1,
+ expectedTransferLength: -1,
expectedBytesReceived: 60,
chunked: true);
@@ -498,7 +515,7 @@
_testParseRequest(request,
"POST",
"/test",
- expectedContentLength: -1,
+ expectedTransferLength: -1,
expectedBytesReceived: 60,
chunked: true);
@@ -586,13 +603,16 @@
String response;
Map headers;
response = "HTTP/1.1 100 Continue\r\nContent-Length: 0\r\n\r\n";
- _testParseResponse(response, 100, "Continue", expectedContentLength: 0);
+ _testParseResponse(response, 100, "Continue");
+
+ response = "HTTP/1.1 100 Continue\r\nContent-Length: 0\r\n\r\n";
+ _testParseResponse(response, 100, "Continue");
response = "HTTP/1.1 100 Continue\r\nContent-Length: 10\r\n\r\n";
_testParseResponse(response,
100,
"Continue",
- expectedContentLength: 10,
+ expectedTransferLength: 10,
expectedBytesReceived: 0);
response = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n"
@@ -600,14 +620,12 @@
_testParseResponse(response,
200,
"OK",
- expectedContentLength: 0,
connectionClose: true);
response = "HTTP/1.0 200 OK\r\nContent-Length: 0\r\n\r\n";
_testParseResponse(response,
200,
"OK",
- expectedContentLength: 0,
expectedVersion: "1.0",
connectionClose: true);
@@ -616,31 +634,30 @@
_testParseResponse(response,
200,
"OK",
- expectedContentLength: 0,
expectedVersion: "1.0");
response = "HTTP/1.1 204 No Content\r\nContent-Length: 11\r\n\r\n";
_testParseResponse(response,
204,
"No Content",
- expectedContentLength: 11,
+ expectedTransferLength: 11,
expectedBytesReceived: 0);
response = "HTTP/1.1 304 Not Modified\r\nContent-Length: 12\r\n\r\n";
_testParseResponse(response,
304,
"Not Modified",
- expectedContentLength: 12,
+ expectedTransferLength: 12,
expectedBytesReceived: 0);
response = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n";
- _testParseResponse(response, 200, "OK", expectedContentLength: 0);
+ _testParseResponse(response, 200, "OK");
response = "HTTP/1.1 404 Not found\r\nContent-Length: 0\r\n\r\n";
- _testParseResponse(response, 404, "Not found", expectedContentLength: 0);
+ _testParseResponse(response, 404, "Not found");
response = "HTTP/1.1 500 Server error\r\nContent-Length: 0\r\n\r\n";
- _testParseResponse(response, 500, "Server error", expectedContentLength: 0);
+ _testParseResponse(response, 500, "Server error");
// Test response to HEAD request.
response = """
@@ -655,7 +672,7 @@
200,
"OK",
responseToMethod: "HEAD",
- expectedContentLength: 20,
+ expectedTransferLength: 20,
expectedBytesReceived: 0,
expectedHeaders: headers);
@@ -668,7 +685,7 @@
_testParseResponse(response,
200,
"OK",
- expectedContentLength: 20,
+ expectedTransferLength: 20,
expectedBytesReceived: 20);
// Test upper and lower case hex digits in chunked encoding.
@@ -684,7 +701,7 @@
_testParseResponse(response,
200,
"OK",
- expectedContentLength: -1,
+ expectedTransferLength: -1,
expectedBytesReceived: 57,
chunked: true);
@@ -697,7 +714,6 @@
_testParseResponse(response,
200,
"OK",
- expectedContentLength: 0,
connectionClose: true);
// Test HTTP response without any transfer length indications
@@ -711,7 +727,7 @@
_testParseResponse(response,
200,
"OK",
- expectedContentLength: -1,
+ expectedTransferLength: -1,
expectedBytesReceived: 59,
close: true,
connectionClose: true);
diff --git a/tests/standalone/io/http_proxy_test.dart b/tests/standalone/io/http_proxy_test.dart
index 9d3190c..24a2f84 100644
--- a/tests/standalone/io/http_proxy_test.dart
+++ b/tests/standalone/io/http_proxy_test.dart
@@ -1,47 +1,60 @@
-// Copyright (c) 2012, 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.
+import "dart:async";
import "dart:io";
import "dart:uri";
class Server {
HttpServer server;
+ bool secure;
int proxyHops;
List<String> directRequestPaths;
int requestCount = 0;
- Server(this.proxyHops, this.directRequestPaths) : server = new HttpServer();
+ Server(this.proxyHops, this.directRequestPaths, this.secure);
- void start() {
- server.listen("127.0.0.1", 0);
- server.defaultRequestHandler =
- (HttpRequest request, HttpResponse response) {
- requestCount++;
- // Check whether a proxy or direct connection is expected.
- bool direct = directRequestPaths.reduce(
- false,
- (prev, path) => prev ? prev : path == request.path);
- if (!direct && proxyHops > 0) {
- Expect.isNotNull(request.headers[HttpHeaders.VIA]);
- Expect.equals(1, request.headers[HttpHeaders.VIA].length);
- Expect.equals(
- proxyHops,
- request.headers[HttpHeaders.VIA][0].split(",").length);
- } else {
- Expect.isNull(request.headers[HttpHeaders.VIA]);
- }
- StringInputStream stream = new StringInputStream(request.inputStream);
- StringBuffer body = new StringBuffer();
- stream.onData = () => body.add(stream.read());
- stream.onClosed = () {
- String path = request.path.substring(1);
- String content = "$path$path$path";
- Expect.equals(content, body.toString());
- response.outputStream.writeString(request.path);
- response.outputStream.close();
- };
- };
+ Future<Server> start() {
+ var x = new Completer();
+ Future f = secure
+ ? HttpServer.bindSecure(
+ "127.0.0.1", 0, certificateName: 'localhost_cert')
+ : HttpServer.bind();
+ return f.then((s) {
+ server = s;
+ x.complete(this);
+ server.listen((request) {
+ var response = request.response;
+ requestCount++;
+ // Check whether a proxy or direct connection is expected.
+ bool direct = directRequestPaths.reduce(
+ false,
+ (prev, path) => prev ? prev : path == request.uri.path);
+ if (!direct && proxyHops > 0) {
+ Expect.isNotNull(request.headers[HttpHeaders.VIA]);
+ Expect.equals(1, request.headers[HttpHeaders.VIA].length);
+ Expect.equals(
+ proxyHops,
+ request.headers[HttpHeaders.VIA][0].split(",").length);
+ } else {
+ Expect.isNull(request.headers[HttpHeaders.VIA]);
+ }
+ var body = new StringBuffer();
+ request.listen(
+ (data) {
+ body.add(new String.fromCharCodes(data));
+ },
+ onDone: () {
+ String path = request.uri.path.substring(1);
+ String content = "$path$path$path";
+ Expect.equals(content, body.toString());
+ response.addString(request.uri.path);
+ response.close();
+ });
+ });
+ return x.future;
+ });
}
void shutdown() {
@@ -51,11 +64,11 @@
int get port => server.port;
}
-Server setupServer(int proxyHops,
- [List<String> directRequestPaths = const <String>[]]) {
- Server server = new Server(proxyHops, directRequestPaths);
- server.start();
- return server;
+Future<Server> setupServer(int proxyHops,
+ {List<String> directRequestPaths: const <String>[],
+ secure: false}) {
+ Server server = new Server(proxyHops, directRequestPaths, secure);
+ return server.start();
}
class ProxyServer {
@@ -63,17 +76,18 @@
HttpClient client;
int requestCount = 0;
- ProxyServer() : server = new HttpServer(), client = new HttpClient();
+ ProxyServer() : client = new HttpClient();
- void start() {
- server.listen("127.0.0.1", 0);
- server.defaultRequestHandler =
- (HttpRequest request, HttpResponse response) {
- requestCount++;
- // Open the connection from the proxy.
- HttpClientConnection conn =
- client.openUrl(request.method, Uri.parse(request.path));
- conn.onRequest = (HttpClientRequest clientRequest) {
+ Future<ProxyServer> start() {
+ var x = new Completer();
+ HttpServer.bind().then((s) {
+ server = s;
+ x.complete(this);
+ server.listen((HttpRequest request) {
+ requestCount++;
+ // Open the connection from the proxy.
+ client.openUrl(request.method, request.uri)
+ .then((HttpClientRequest clientRequest) {
// Forward all headers.
request.headers.forEach((String name, List<String> values) {
values.forEach((String value) {
@@ -89,224 +103,244 @@
clientRequest.headers.add(
HttpHeaders.VIA, "${viaPrefix}1.1 localhost:$port");
// Copy all content.
- request.inputStream.pipe(clientRequest.outputStream);
- };
- conn.onResponse = (HttpClientResponse clientResponse) {
- clientResponse.inputStream.pipe(response.outputStream);
- };
- };
+ request.pipe(clientRequest);
+ return clientRequest.response;
+ })
+ .then((HttpClientResponse clientResponse) {
+ clientResponse.pipe(request.response);
+ });
+ });
+ });
+ return x.future;
}
void shutdown() {
server.close();
- client.shutdown();
+ client.close();
}
int get port => server.port;
}
-ProxyServer setupProxyServer() {
+Future<ProxyServer> setupProxyServer() {
ProxyServer proxyServer = new ProxyServer();
- proxyServer.start();
- return proxyServer;
+ return proxyServer.start();
}
testInvalidProxy() {
HttpClient client = new HttpClient();
- // TODO(sgjesse): This should not throw errors, but call
- // HttpClientConnection onError.
+ client.findProxy = (Uri uri) => "";
+ client.getUrl(Uri.parse("http://www.google.com/test"))
+ .catchError((error) {}, test: (e) => e is HttpException);
+
client.findProxy = (Uri uri) => "XXX";
- Expect.throws(
- () => client.getUrl(Uri.parse("http://www.google.com/test")),
- (e) => e is HttpException);
+ client.getUrl(Uri.parse("http://www.google.com/test"))
+ .catchError((error) {}, test: (e) => e is HttpException);
client.findProxy = (Uri uri) => "PROXY www.google.com";
- Expect.throws(
- () => client.getUrl(Uri.parse("http://www.google.com/test")),
- (e) => e is HttpException);
+ client.getUrl(Uri.parse("http://www.google.com/test"))
+ .catchError((error) {}, test: (e) => e is HttpException);
client.findProxy = (Uri uri) => "PROXY www.google.com:http";
- Expect.throws(
- () => client.getUrl(Uri.parse("http://www.google.com/test")),
- (e) => e is HttpException);
+ client.getUrl(Uri.parse("http://www.google.com/test"))
+ .catchError((error) {}, test: (e) => e is HttpException);
}
int testDirectDoneCount = 0;
void testDirectProxy() {
- Server server = setupServer(0);
- HttpClient client = new HttpClient();
- List<String> proxy =
- ["DIRECT", " DIRECT ", "DIRECT ;", " DIRECT ; ",
- ";DIRECT", " ; DIRECT ", ";;DIRECT;;"];
+ setupServer(0).then((server) {
+ HttpClient client = new HttpClient();
+ List<String> proxy =
+ ["DIRECT", " DIRECT ", "DIRECT ;", " DIRECT ; ",
+ ";DIRECT", " ; DIRECT ", ";;DIRECT;;"];
- client.findProxy = (Uri uri) {
- int index = int.parse(uri.path.substring(1));
- return proxy[index];
- };
+ client.findProxy = (Uri uri) {
+ int index = int.parse(uri.path.substring(1));
+ return proxy[index];
+ };
- for (int i = 0; i < proxy.length; i++) {
- HttpClientConnection conn =
- client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/$i"));
- conn.onRequest = (HttpClientRequest clientRequest) {
- String content = "$i$i$i";
- clientRequest.contentLength = content.length;
- clientRequest.outputStream.writeString(content);
- clientRequest.outputStream.close();
- };
- conn.onResponse = (HttpClientResponse response) {
- response.inputStream.onData = () => response.inputStream.read();
- response.inputStream.onClosed = () {
- testDirectDoneCount++;
- if (testDirectDoneCount == proxy.length) {
- Expect.equals(proxy.length, server.requestCount);
- server.shutdown();
- client.shutdown();
- }
- };
- };
- }
+ for (int i = 0; i < proxy.length; i++) {
+ client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/$i"))
+ .then((HttpClientRequest clientRequest) {
+ String content = "$i$i$i";
+ clientRequest.contentLength = content.length;
+ clientRequest.addString(content);
+ return clientRequest.close();
+ })
+ .then((HttpClientResponse response) {
+ response.listen((_) {}, onDone: () {
+ testDirectDoneCount++;
+ if (testDirectDoneCount == proxy.length) {
+ Expect.equals(proxy.length, server.requestCount);
+ server.shutdown();
+ client.close();
+ }
+ });
+ });
+ }
+ });
}
int testProxyDoneCount = 0;
void testProxy() {
- ProxyServer proxyServer = setupProxyServer();
- Server server = setupServer(1, ["/4"]);
- HttpClient client = new HttpClient();
+ setupProxyServer().then((proxyServer) {
+ setupServer(1, directRequestPaths: ["/4"]).then((server) {
+ setupServer(1, directRequestPaths: ["/4"], secure: true).then((secureServer) {
+ HttpClient client = new HttpClient();
- List<String> proxy =
- ["PROXY localhost:${proxyServer.port}",
- "PROXY localhost:${proxyServer.port}; PROXY hede.hule.hest:8080",
- "PROXY hede.hule.hest:8080; PROXY localhost:${proxyServer.port}",
- "PROXY hede.hule.hest:8080; PROXY hede.hule.hest:8181; PROXY localhost:${proxyServer.port}",
- "PROXY hede.hule.hest:8080; PROXY hede.hule.hest:8181; DIRECT",
- "PROXY localhost:${proxyServer.port}; DIRECT"];
+ List<String> proxy =
+ ["PROXY localhost:${proxyServer.port}",
+ "PROXY localhost:${proxyServer.port}; PROXY hede.hule.hest:8080",
+ "PROXY hede.hule.hest:8080; PROXY localhost:${proxyServer.port}",
+ "PROXY hede.hule.hest:8080; PROXY hede.hule.hest:8181;"
+ " PROXY localhost:${proxyServer.port}",
+ "PROXY hede.hule.hest:8080; PROXY hede.hule.hest:8181; DIRECT",
+ "PROXY localhost:${proxyServer.port}; DIRECT"];
- client.findProxy = (Uri uri) {
- // Pick the proxy configuration based on the request path.
- int index = int.parse(uri.path.substring(1));
- return proxy[index];
- };
-
- for (int i = 0; i < proxy.length; i++) {
- HttpClientConnection conn =
- client.postUrl(
- Uri.parse("http://127.0.0.1:${server.port}/$i"));
- conn.onRequest = (HttpClientRequest clientRequest) {
- String content = "$i$i$i";
- clientRequest.outputStream.writeString(content);
- clientRequest.outputStream.close();
+ client.findProxy = (Uri uri) {
+ // Pick the proxy configuration based on the request path.
+ int index = int.parse(uri.path.substring(1));
+ return proxy[index];
};
- conn.onResponse = (HttpClientResponse response) {
- response.inputStream.onData = () => response.inputStream.read();
- response.inputStream.onClosed = () {
- testProxyDoneCount++;
- if (testProxyDoneCount == proxy.length) {
- Expect.equals(proxy.length, server.requestCount);
- proxyServer.shutdown();
- server.shutdown();
- client.shutdown();
- }
- };
- };
- }
+
+ for (int i = 0; i < proxy.length; i++) {
+ test(bool secure) {
+ String url = secure
+ ? "https://localhost:${secureServer.port}/$i"
+ : "http://127.0.0.1:${server.port}/$i";
+
+ client.postUrl(Uri.parse(url))
+ .then((HttpClientRequest clientRequest) {
+ String content = "$i$i$i";
+ clientRequest.addString(content);
+ return clientRequest.close();
+ })
+ .then((HttpClientResponse response) {
+ response.listen((_) {}, onDone: () {
+ testProxyDoneCount++;
+ if (testProxyDoneCount == proxy.length * 2) {
+ Expect.equals(proxy.length, server.requestCount);
+ proxyServer.shutdown();
+ server.shutdown();
+ secureServer.shutdown();
+ client.close();
+ }
+ });
+ });
+ }
+
+ test(false);
+ test(true);
+ }
+ });
+ });
+ });
}
int testProxyChainDoneCount = 0;
void testProxyChain() {
// Setup two proxy servers having the first using the second as its proxy.
- ProxyServer proxyServer1 = setupProxyServer();
- ProxyServer proxyServer2 = setupProxyServer();
+ setupProxyServer().then((proxyServer1) {
+ setupProxyServer().then((proxyServer2) {
proxyServer1.client.findProxy = (_) => "PROXY 127.0.0.1:${proxyServer2.port}";
- Server server = setupServer(2, ["/4"]);
- HttpClient client = new HttpClient();
+ setupServer(2, directRequestPaths: ["/4"]).then((server) {
+ HttpClient client = new HttpClient();
- List<String> proxy =
- ["PROXY localhost:${proxyServer1.port}",
- "PROXY localhost:${proxyServer1.port}; PROXY hede.hule.hest:8080",
- "PROXY hede.hule.hest:8080; PROXY localhost:${proxyServer1.port}",
- "PROXY hede.hule.hest:8080; PROXY hede.hule.hest:8181; PROXY localhost:${proxyServer1.port}",
- "PROXY hede.hule.hest:8080; PROXY hede.hule.hest:8181; DIRECT",
- "PROXY localhost:${proxyServer1.port}; DIRECT"];
+ List<String> proxy =
+ ["PROXY localhost:${proxyServer1.port}",
+ "PROXY localhost:${proxyServer1.port}; PROXY hede.hule.hest:8080",
+ "PROXY hede.hule.hest:8080; PROXY localhost:${proxyServer1.port}",
+ "PROXY hede.hule.hest:8080; PROXY hede.hule.hest:8181; PROXY localhost:${proxyServer1.port}",
+ "PROXY hede.hule.hest:8080; PROXY hede.hule.hest:8181; DIRECT",
+ "PROXY localhost:${proxyServer1.port}; DIRECT"];
- client.findProxy = (Uri uri) {
- // Pick the proxy configuration based on the request path.
- int index = int.parse(uri.path.substring(1));
- return proxy[index];
- };
-
- for (int i = 0; i < proxy.length; i++) {
- HttpClientConnection conn =
- client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/$i"));
- conn.onRequest = (HttpClientRequest clientRequest) {
- String content = "$i$i$i";
- clientRequest.contentLength = content.length;
- clientRequest.outputStream.writeString(content);
- clientRequest.outputStream.close();
+ client.findProxy = (Uri uri) {
+ // Pick the proxy configuration based on the request path.
+ int index = int.parse(uri.path.substring(1));
+ return proxy[index];
};
- conn.onResponse = (HttpClientResponse response) {
- response.inputStream.onData = () => response.inputStream.read();
- response.inputStream.onClosed = () {
- testProxyChainDoneCount++;
- if (testProxyChainDoneCount == proxy.length) {
- Expect.equals(proxy.length, server.requestCount);
- proxyServer1.shutdown();
- proxyServer2.shutdown();
- server.shutdown();
- client.shutdown();
- }
- };
- };
- }
+
+ for (int i = 0; i < proxy.length; i++) {
+ client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/$i"))
+ .then((HttpClientRequest clientRequest) {
+ String content = "$i$i$i";
+ clientRequest.contentLength = content.length;
+ clientRequest.addString(content);
+ return clientRequest.close();
+ })
+ .then((HttpClientResponse response) {
+ response.listen((_) {}, onDone: () {
+ testProxyChainDoneCount++;
+ if (testProxyChainDoneCount == proxy.length) {
+ Expect.equals(proxy.length, server.requestCount);
+ proxyServer1.shutdown();
+ proxyServer2.shutdown();
+ server.shutdown();
+ client.close();
+ }
+ });
+ });
+ }
+ });
+ });
+ });
}
int testRealProxyDoneCount = 0;
void testRealProxy() {
- Server server = setupServer(1);
- HttpClient client = new HttpClient();
+ setupServer(1).then((server) {
+ HttpClient client = new HttpClient();
- List<String> proxy =
- ["PROXY localhost:8080",
- "PROXY localhost:8080; PROXY hede.hule.hest:8080",
- "PROXY hede.hule.hest:8080; PROXY localhost:8080",
- "PROXY localhost:8080; DIRECT"];
+ List<String> proxy =
+ ["PROXY localhost:8080",
+ "PROXY localhost:8080; PROXY hede.hule.hest:8080",
+ "PROXY hede.hule.hest:8080; PROXY localhost:8080",
+ "PROXY localhost:8080; DIRECT"];
- client.findProxy = (Uri uri) {
- // Pick the proxy configuration based on the request path.
- int index = int.parse(uri.path.substring(1));
- return proxy[index];
- };
-
- for (int i = 0; i < proxy.length; i++) {
- HttpClientConnection conn =
- client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/$i"));
- conn.onRequest = (HttpClientRequest clientRequest) {
- String content = "$i$i$i";
- clientRequest.contentLength = content.length;
- clientRequest.outputStream.writeString(content);
- clientRequest.outputStream.close();
+ client.findProxy = (Uri uri) {
+ // Pick the proxy configuration based on the request path.
+ int index = int.parse(uri.path.substring(1));
+ return proxy[index];
};
- conn.onResponse = (HttpClientResponse response) {
- response.inputStream.onData = () => response.inputStream.read();
- response.inputStream.onClosed = () {
- testRealProxyDoneCount++;
- if (testRealProxyDoneCount == proxy.length) {
- Expect.equals(proxy.length, server.requestCount);
- server.shutdown();
- client.shutdown();
- }
- };
- };
- }
+
+ for (int i = 0; i < proxy.length; i++) {
+ client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/$i"))
+ .then((HttpClientRequest clientRequest) {
+ String content = "$i$i$i";
+ clientRequest.contentLength = content.length;
+ clientRequest.addString(content);
+ return clientRequest.close();
+ })
+ .then((HttpClientResponse response) {
+ response.listen((_) {}, onDone: () {
+ testRealProxyDoneCount++;
+ if (testRealProxyDoneCount == proxy.length) {
+ Expect.equals(proxy.length, server.requestCount);
+ server.shutdown();
+ client.close();
+ }
+ });
+ });
+ }
+ });
+}
+
+void InitializeSSL() {
+ var testPkcertDatabase =
+ new Path(new Options().script).directoryPath.append('pkcert/');
+ SecureSocket.initialize(database: testPkcertDatabase.toNativePath(),
+ password: 'dartdart');
}
main() {
+ InitializeSSL();
testInvalidProxy();
testDirectProxy();
testProxy();
testProxyChain();
// This test is not normally run. It can be used for locally testing
// with a real proxy server (e.g. Apache).
- // testRealProxy();
+ //testRealProxy();
}
diff --git a/tests/standalone/io/http_read_test.dart b/tests/standalone/io/http_read_test.dart
index bb8a87a..dfef4e4 100644
--- a/tests/standalone/io/http_read_test.dart
+++ b/tests/standalone/io/http_read_test.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
//
@@ -10,11 +10,11 @@
import "dart:isolate";
import "dart:io";
-class TestServerMain {
- TestServerMain()
+class IsolatedHttpServer {
+ IsolatedHttpServer()
: _statusPort = new ReceivePort(),
_serverPort = null {
- _serverPort = spawnFunction(startTestServer);
+ _serverPort = spawnFunction(startIsolatedHttpServer);
}
void setServerStartedHandler(void startedCallback(int port)) {
@@ -30,20 +30,22 @@
});
// Send server start message to the server.
- var command = new TestServerCommand.start();
+ var command = new IsolatedHttpServerCommand.start();
_serverPort.send(command, _statusPort.toSendPort());
}
void shutdown() {
// Send server stop message to the server.
- _serverPort.send(new TestServerCommand.stop(), _statusPort.toSendPort());
+ _serverPort.send(new IsolatedHttpServerCommand.stop(),
+ _statusPort.toSendPort());
_statusPort.close();
}
void chunkedEncoding() {
// Send chunked encoding message to the server.
_serverPort.send(
- new TestServerCommand.chunkedEncoding(), _statusPort.toSendPort());
+ new IsolatedHttpServerCommand.chunkedEncoding(),
+ _statusPort.toSendPort());
}
ReceivePort _statusPort; // Port for receiving messages from the server.
@@ -52,14 +54,14 @@
}
-class TestServerCommand {
+class IsolatedHttpServerCommand {
static const START = 0;
static const STOP = 1;
static const CHUNKED_ENCODING = 2;
- TestServerCommand.start() : _command = START;
- TestServerCommand.stop() : _command = STOP;
- TestServerCommand.chunkedEncoding() : _command = CHUNKED_ENCODING;
+ IsolatedHttpServerCommand.start() : _command = START;
+ IsolatedHttpServerCommand.stop() : _command = STOP;
+ IsolatedHttpServerCommand.chunkedEncoding() : _command = CHUNKED_ENCODING;
bool get isStart => _command == START;
bool get isStop => _command == STOP;
@@ -69,14 +71,14 @@
}
-class TestServerStatus {
+class IsolatedHttpServerStatus {
static const STARTED = 0;
static const STOPPED = 1;
static const ERROR = 2;
- TestServerStatus.started(this._port) : _state = STARTED;
- TestServerStatus.stopped() : _state = STOPPED;
- TestServerStatus.error() : _state = ERROR;
+ IsolatedHttpServerStatus.started(this._port) : _state = STARTED;
+ IsolatedHttpServerStatus.stopped() : _state = STOPPED;
+ IsolatedHttpServerStatus.error() : _state = ERROR;
bool get isStarted => _state == STARTED;
bool get isStopped => _state == STOPPED;
@@ -89,7 +91,7 @@
}
-void startTestServer() {
+void startIsolatedHttpServer() {
var server = new TestServer();
server.init();
port.receive(server.dispatch);
@@ -97,56 +99,56 @@
class TestServer {
// Echo the request content back to the response.
- void _echoHandler(HttpRequest request, HttpResponse response) {
+ void _echoHandler(HttpRequest request) {
+ var response = request.response;
Expect.equals("POST", request.method);
response.contentLength = request.contentLength;
- request.inputStream.pipe(response.outputStream);
+ request.pipe(response);
}
// Return a 404.
- void _notFoundHandler(HttpRequest request, HttpResponse response) {
+ void _notFoundHandler(HttpRequest request) {
+ var response = request.response;
response.statusCode = HttpStatus.NOT_FOUND;
response.headers.set("Content-Type", "text/html; charset=UTF-8");
- response.outputStream.writeString("Page not found");
- response.outputStream.close();
+ response.addString("Page not found");
+ response.close();
}
void init() {
// Setup request handlers.
_requestHandlers = new Map();
- _requestHandlers["/echo"] = (HttpRequest request, HttpResponse response) {
- _echoHandler(request, response);
- };
+ _requestHandlers["/echo"] = _echoHandler;
}
void dispatch(message, SendPort replyTo) {
if (message.isStart) {
- _server = new HttpServer();
try {
- _server.listen("127.0.0.1", 0);
- _server.defaultRequestHandler = (HttpRequest req, HttpResponse rsp) {
- _requestReceivedHandler(req, rsp);
- };
- replyTo.send(new TestServerStatus.started(_server.port), null);
+ HttpServer.bind().then((server) {
+ _server = server;
+ _server.listen(_requestReceivedHandler);
+ replyTo.send(
+ new IsolatedHttpServerStatus.started(_server.port), null);
+ });
} catch (e) {
- replyTo.send(new TestServerStatus.error(), null);
+ replyTo.send(new IsolatedHttpServerStatus.error(), null);
}
} else if (message.isStop) {
_server.close();
port.close();
- replyTo.send(new TestServerStatus.stopped(), null);
+ replyTo.send(new IsolatedHttpServerStatus.stopped(), null);
} else if (message.isChunkedEncoding) {
_chunkedEncoding = true;
}
}
- void _requestReceivedHandler(HttpRequest request, HttpResponse response) {
- var requestHandler =_requestHandlers[request.path];
+ void _requestReceivedHandler(HttpRequest request) {
+ var requestHandler =_requestHandlers[request.uri.path];
if (requestHandler != null) {
- requestHandler(request, response);
+ requestHandler(request);
} else {
- _notFoundHandler(request, response);
+ _notFoundHandler(request);
}
}
@@ -155,116 +157,55 @@
bool _chunkedEncoding = false;
}
-void testReadInto(bool chunkedEncoding) {
+void testRead(bool chunkedEncoding) {
String data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
final int kMessageCount = 10;
- TestServerMain testServerMain = new TestServerMain();
+ IsolatedHttpServer server = new IsolatedHttpServer();
void runTest(int port) {
int count = 0;
HttpClient httpClient = new HttpClient();
void sendRequest() {
- HttpClientConnection conn =
- httpClient.post("127.0.0.1", port, "/echo");
- conn.onRequest = (HttpClientRequest request) {
- if (chunkedEncoding) {
- request.outputStream.writeString(data.substring(0, 10));
- request.outputStream.writeString(data.substring(10, data.length));
- } else {
- request.contentLength = data.length;
- request.outputStream.write(data.charCodes);
- }
- request.outputStream.close();
- };
- conn.onResponse = (HttpClientResponse response) {
- Expect.equals(HttpStatus.OK, response.statusCode);
- InputStream stream = response.inputStream;
- List<int> body = new List<int>();
- stream.onData = () {
- List tmp = new List.fixedLength(3);
- int bytes = stream.readInto(tmp);
- body.addAll(tmp.getRange(0, bytes));
- };
- stream.onClosed = () {
- Expect.equals(data, new String.fromCharCodes(body));
- count++;
- if (count < kMessageCount) {
- sendRequest();
- } else {
- httpClient.shutdown();
- testServerMain.shutdown();
- }
- };
- };
+ httpClient.post("127.0.0.1", port, "/echo")
+ .then((request) {
+ if (chunkedEncoding) {
+ request.addString(data.substring(0, 10));
+ request.addString(data.substring(10, data.length));
+ } else {
+ request.contentLength = data.length;
+ request.add(data.charCodes);
+ }
+ return request.close();
+ })
+ .then((response) {
+ Expect.equals(HttpStatus.OK, response.statusCode);
+ List<int> body = new List<int>();
+ response.listen(
+ body.addAll,
+ onDone: () {
+ Expect.equals(data, new String.fromCharCodes(body));
+ count++;
+ if (count < kMessageCount) {
+ sendRequest();
+ } else {
+ httpClient.close();
+ server.shutdown();
+ }
+ });
+ });
}
-
sendRequest();
}
- testServerMain.setServerStartedHandler(runTest);
+ server.setServerStartedHandler(runTest);
if (chunkedEncoding) {
- testServerMain.chunkedEncoding();
+ server.chunkedEncoding();
}
- testServerMain.start();
-}
-
-void testReadShort(bool chunkedEncoding) {
- String data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
- final int kMessageCount = 10;
-
- TestServerMain testServerMain = new TestServerMain();
-
- void runTest(int port) {
- int count = 0;
- HttpClient httpClient = new HttpClient();
- void sendRequest() {
- HttpClientConnection conn =
- httpClient.post("127.0.0.1", port, "/echo");
- conn.onRequest = (HttpClientRequest request) {
- if (chunkedEncoding) {
- request.outputStream.writeString(data.substring(0, 10));
- request.outputStream.writeString(data.substring(10, data.length));
- } else {
- request.contentLength = data.length;
- request.outputStream.write(data.charCodes);
- }
- request.outputStream.close();
- };
- conn.onResponse = (HttpClientResponse response) {
- Expect.equals(HttpStatus.OK, response.statusCode);
- InputStream stream = response.inputStream;
- List<int> body = new List<int>();
- stream.onData = () {
- List tmp = stream.read(2);
- body.addAll(tmp);
- };
- stream.onClosed = () {
- Expect.equals(data, new String.fromCharCodes(body));
- count++;
- if (count < kMessageCount) {
- sendRequest();
- } else {
- httpClient.shutdown();
- testServerMain.shutdown();
- }
- };
- };
- }
-
- sendRequest();
- }
-
- testServerMain.setServerStartedHandler(runTest);
- if (chunkedEncoding) {
- testServerMain.chunkedEncoding();
- }
- testServerMain.start();
+ server.start();
}
void main() {
- testReadInto(true);
- testReadInto(false);
- testReadShort(true);
- testReadShort(false);
+ testRead(true);
+ testRead(false);
}
diff --git a/tests/standalone/io/http_redirect_test.dart b/tests/standalone/io/http_redirect_test.dart
index 4df9782..3209496 100644
--- a/tests/standalone/io/http_redirect_test.dart
+++ b/tests/standalone/io/http_redirect_test.dart
@@ -3,386 +3,370 @@
// BSD-style license that can be found in the LICENSE file.
//
+import "dart:async";
import "dart:io";
import "dart:uri";
-HttpServer setupServer() {
- HttpServer server = new HttpServer();
- server.listen("127.0.0.1", 0, backlog: 5);
+Future<HttpServer> setupServer() {
+ Completer completer = new Completer();
+ HttpServer.bind().then((server) {
- void addRedirectHandler(int number, int statusCode) {
- server.addRequestHandler(
- (HttpRequest request) => request.path == "/$number",
- (HttpRequest request, HttpResponse response) {
- response.headers.set(HttpHeaders.LOCATION,
- "http://127.0.0.1:${server.port}/${number + 1}");
- response.statusCode = statusCode;
- response.outputStream.close();
- });
- }
+ var handlers = new Map<String, Function>();
+ addRequestHandler(String path, void handler(HttpRequest request,
+ HttpResponse response)) {
+ handlers[path] = handler;
+ }
- // Setup simple redirect.
- server.addRequestHandler(
- (HttpRequest request) => request.path == "/redirect",
- (HttpRequest request, HttpResponse response) {
- response.headers.set(HttpHeaders.LOCATION,
- "http://127.0.0.1:${server.port}/location");
- response.statusCode = HttpStatus.MOVED_PERMANENTLY;
- response.outputStream.close();
- }
- );
- server.addRequestHandler(
- (HttpRequest request) => request.path == "/location",
- (HttpRequest request, HttpResponse response) {
- response.outputStream.close();
- }
- );
+ server.listen((HttpRequest request) {
+ if (handlers.containsKey(request.uri.path)) {
+ handlers[request.uri.path](request, request.response);
+ } else {
+ request.listen((_) {}, onDone: () {
+ request.response.statusCode = 404;
+ request.response.close();
+ });
+ }
+ });
- // Setup redirect chain.
- int n = 1;
- addRedirectHandler(n++, HttpStatus.MOVED_PERMANENTLY);
- addRedirectHandler(n++, HttpStatus.MOVED_TEMPORARILY);
- addRedirectHandler(n++, HttpStatus.SEE_OTHER);
- addRedirectHandler(n++, HttpStatus.TEMPORARY_REDIRECT);
- for (int i = n; i < 10; i++) {
- addRedirectHandler(i, HttpStatus.MOVED_PERMANENTLY);
- }
-
- // Setup redirect loop.
- server.addRequestHandler(
- (HttpRequest request) => request.path == "/A",
- (HttpRequest request, HttpResponse response) {
- response.headers.set(HttpHeaders.LOCATION,
- "http://127.0.0.1:${server.port}/B");
- response.statusCode = HttpStatus.MOVED_PERMANENTLY;
- response.outputStream.close();
- }
- );
- server.addRequestHandler(
- (HttpRequest request) => request.path == "/B",
- (HttpRequest request, HttpResponse response) {
- response.headers.set(HttpHeaders.LOCATION,
- "http://127.0.0.1:${server.port}/A");
- response.statusCode = HttpStatus.MOVED_TEMPORARILY;
- response.outputStream.close();
- }
- );
-
- // Setup redirect checking headers.
- server.addRequestHandler(
- (HttpRequest request) => request.path == "/src",
- (HttpRequest request, HttpResponse response) {
- Expect.equals("value", request.headers.value("X-Request-Header"));
- response.headers.set(HttpHeaders.LOCATION,
- "http://127.0.0.1:${server.port}/target");
- response.statusCode = HttpStatus.MOVED_PERMANENTLY;
- response.outputStream.close();
- }
- );
- server.addRequestHandler(
- (HttpRequest request) => request.path == "/target",
- (HttpRequest request, HttpResponse response) {
- Expect.equals("value", request.headers.value("X-Request-Header"));
- response.outputStream.close();
- }
- );
-
- // Setup redirect for 301 where POST should not redirect.
- server.addRequestHandler(
- (HttpRequest request) => request.path == "/301src",
- (HttpRequest request, HttpResponse response) {
- Expect.equals("POST", request.method);
- response.headers.set(HttpHeaders.LOCATION,
- "http://127.0.0.1:${server.port}/301target");
- response.statusCode = HttpStatus.MOVED_PERMANENTLY;
- response.outputStream.close();
- }
- );
- server.addRequestHandler(
- (HttpRequest request) => request.path == "/301target",
- (HttpRequest request, HttpResponse response) {
- Expect.fail("Redirect of POST should not happen");
- }
- );
-
- // Setup redirect for 303 where POST should turn into GET.
- server.addRequestHandler(
- (HttpRequest request) => request.path == "/303src",
- (HttpRequest request, HttpResponse response) {
- Expect.equals("POST", request.method);
- Expect.equals(10, request.contentLength);
- request.inputStream.onData = request.inputStream.read;
- request.inputStream.onClosed = () {
+ void addRedirectHandler(int number, int statusCode) {
+ addRequestHandler(
+ "/$number",
+ (HttpRequest request, HttpResponse response) {
response.headers.set(HttpHeaders.LOCATION,
- "http://127.0.0.1:${server.port}/303target");
- response.statusCode = HttpStatus.SEE_OTHER;
- response.outputStream.close();
- };
- }
- );
- server.addRequestHandler(
- (HttpRequest request) => request.path == "/303target",
- (HttpRequest request, HttpResponse response) {
- Expect.equals("GET", request.method);
- response.outputStream.close();
- }
- );
+ "http://127.0.0.1:${server.port}/${number + 1}");
+ response.statusCode = statusCode;
+ response.close();
+ });
+ }
- // Setup redirect where we close the connection.
- server.addRequestHandler(
- (HttpRequest request) => request.path == "/closing",
- (HttpRequest request, HttpResponse response) {
- response.headers.set(HttpHeaders.LOCATION,
- "http://127.0.0.1:${server.port}/");
- response.statusCode = HttpStatus.FOUND;
- response.persistentConnection = false;
- response.outputStream.close();
- }
- );
+ // Setup simple redirect.
+ addRequestHandler(
+ "/redirect",
+ (HttpRequest request, HttpResponse response) {
+ response.headers.set(HttpHeaders.LOCATION,
+ "http://127.0.0.1:${server.port}/location");
+ response.statusCode = HttpStatus.MOVED_PERMANENTLY;
+ response.close();
+ }
+ );
+ addRequestHandler(
+ "/location",
+ (HttpRequest request, HttpResponse response) {
+ response.close();
+ }
+ );
- return server;
+ // Setup redirect chain.
+ int n = 1;
+ addRedirectHandler(n++, HttpStatus.MOVED_PERMANENTLY);
+ addRedirectHandler(n++, HttpStatus.MOVED_TEMPORARILY);
+ addRedirectHandler(n++, HttpStatus.SEE_OTHER);
+ addRedirectHandler(n++, HttpStatus.TEMPORARY_REDIRECT);
+ for (int i = n; i < 10; i++) {
+ addRedirectHandler(i, HttpStatus.MOVED_PERMANENTLY);
+ }
+
+ // Setup redirect loop.
+ addRequestHandler(
+ "/A",
+ (HttpRequest request, HttpResponse response) {
+ response.headers.set(HttpHeaders.LOCATION,
+ "http://127.0.0.1:${server.port}/B");
+ response.statusCode = HttpStatus.MOVED_PERMANENTLY;
+ response.close();
+ }
+ );
+ addRequestHandler(
+ "/B",
+ (HttpRequest request, HttpResponse response) {
+ response.headers.set(HttpHeaders.LOCATION,
+ "http://127.0.0.1:${server.port}/A");
+ response.statusCode = HttpStatus.MOVED_TEMPORARILY;
+ response.close();
+ }
+ );
+
+ // Setup redirect checking headers.
+ addRequestHandler(
+ "/src",
+ (HttpRequest request, HttpResponse response) {
+ Expect.equals("value", request.headers.value("X-Request-Header"));
+ response.headers.set(HttpHeaders.LOCATION,
+ "http://127.0.0.1:${server.port}/target");
+ response.statusCode = HttpStatus.MOVED_PERMANENTLY;
+ response.close();
+ }
+ );
+ addRequestHandler(
+ "/target",
+ (HttpRequest request, HttpResponse response) {
+ Expect.equals("value", request.headers.value("X-Request-Header"));
+ response.close();
+ }
+ );
+
+ // Setup redirect for 301 where POST should not redirect.
+ addRequestHandler(
+ "/301src",
+ (HttpRequest request, HttpResponse response) {
+ Expect.equals("POST", request.method);
+ request.listen(
+ (_) {},
+ onDone: () {
+ response.headers.set(
+ HttpHeaders.LOCATION,
+ "http://127.0.0.1:${server.port}/301target");
+ response.statusCode = HttpStatus.MOVED_PERMANENTLY;
+ response.close();
+ });
+ });
+ addRequestHandler(
+ "/301target",
+ (HttpRequest request, HttpResponse response) {
+ Expect.fail("Redirect of POST should not happen");
+ }
+ );
+
+ // Setup redirect for 303 where POST should turn into GET.
+ addRequestHandler(
+ "/303src",
+ (HttpRequest request, HttpResponse response) {
+ request.listen((_) {}, onDone: () {
+ Expect.equals("POST", request.method);
+ request.listen(
+ (_) {},
+ onDone: () {
+ response.headers.set(
+ HttpHeaders.LOCATION,
+ "http://127.0.0.1:${server.port}/303target");
+ response.statusCode = HttpStatus.SEE_OTHER;
+ response.close();
+ });
+ });
+ });
+ addRequestHandler(
+ "/303target",
+ (HttpRequest request, HttpResponse response) {
+ Expect.equals("GET", request.method);
+ response.close();
+ });
+
+ // Setup redirect where we close the connection.
+ addRequestHandler(
+ "/closing",
+ (HttpRequest request, HttpResponse response) {
+ response.headers.set(HttpHeaders.LOCATION,
+ "http://127.0.0.1:${server.port}/");
+ response.statusCode = HttpStatus.FOUND;
+ response.persistentConnection = false;
+ response.close();
+ });
+
+ completer.complete(server);
+ });
+ return completer.future;
}
-void checkRedirects(int redirectCount, HttpClientConnection conn) {
+void checkRedirects(int redirectCount, HttpClientResponse response) {
if (redirectCount < 2) {
- Expect.isNull(conn.redirects);
+ Expect.isTrue(response.redirects.isEmpty);
} else {
- Expect.equals(redirectCount - 1, conn.redirects.length);
+ Expect.equals(redirectCount - 1, response.redirects.length);
for (int i = 0; i < redirectCount - 2; i++) {
- Expect.equals(conn.redirects[i].location.path, "/${i + 2}");
+ Expect.equals(response.redirects[i].location.path, "/${i + 2}");
}
}
}
void testManualRedirect() {
- HttpServer server = setupServer();
- HttpClient client = new HttpClient();
+ setupServer().then((server) {
+ HttpClient client = new HttpClient();
- int redirectCount = 0;
- HttpClientConnection conn =
- client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/1"));
- conn.followRedirects = false;
- conn.onResponse = (HttpClientResponse response) {
- response.inputStream.onData = response.inputStream.read;
- response.inputStream.onClosed = () {
- redirectCount++;
- if (redirectCount < 10) {
- Expect.isTrue(response.isRedirect);
- checkRedirects(redirectCount, conn);
- conn.redirect();
- } else {
- Expect.equals(HttpStatus.NOT_FOUND, response.statusCode);
- server.close();
- client.shutdown();
- }
- };
- };
+ int redirectCount = 0;
+ handleResponse(HttpClientResponse response) {
+ response.listen(
+ (_) => Expect.fail("Response data not expected"),
+ onDone: () {
+ redirectCount++;
+ if (redirectCount < 10) {
+ Expect.isTrue(response.isRedirect);
+ checkRedirects(redirectCount, response);
+ response.redirect().then(handleResponse);
+ } else {
+ Expect.equals(HttpStatus.NOT_FOUND, response.statusCode);
+ server.close();
+ client.close();
+ }
+ });
+ }
+ client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/1"))
+ .then((HttpClientRequest request) {
+ request.followRedirects = false;
+ return request.close();
+ })
+ .then(handleResponse);
+ });
}
void testManualRedirectWithHeaders() {
- HttpServer server = setupServer();
- HttpClient client = new HttpClient();
+ setupServer().then((server) {
+ HttpClient client = new HttpClient();
- int redirectCount = 0;
- HttpClientConnection conn =
- client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/src"));
- conn.followRedirects = false;
- conn.onRequest = (HttpClientRequest request) {
- request.headers.add("X-Request-Header", "value");
- request.outputStream.close();
- };
- conn.onResponse = (HttpClientResponse response) {
- response.inputStream.onData = response.inputStream.read;
- response.inputStream.onClosed = () {
- redirectCount++;
- if (redirectCount < 2) {
- Expect.isTrue(response.isRedirect);
- conn.redirect();
- } else {
- Expect.equals(HttpStatus.OK, response.statusCode);
- server.close();
- client.shutdown();
- }
- };
- };
+ int redirectCount = 0;
+
+ handleResponse(HttpClientResponse response) {
+ response.listen(
+ (_) => Expect.fail("Response data not expected"),
+ onDone: () {
+ redirectCount++;
+ if (redirectCount < 2) {
+ Expect.isTrue(response.isRedirect);
+ response.redirect().then(handleResponse);
+ } else {
+ Expect.equals(HttpStatus.OK, response.statusCode);
+ server.close();
+ client.close();
+ }
+ });
+ }
+
+ client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/src"))
+ .then((HttpClientRequest request) {
+ request.followRedirects = false;
+ request.headers.add("X-Request-Header", "value");
+ return request.close();
+ }).then(handleResponse);
+ });
}
void testAutoRedirect() {
- HttpServer server = setupServer();
- HttpClient client = new HttpClient();
+ setupServer().then((server) {
+ HttpClient client = new HttpClient();
- var requestCount = 0;
-
- void onRequest(HttpClientRequest request) {
- requestCount++;
- request.outputStream.close();
- }
-
- void onResponse(HttpClientResponse response) {
- response.inputStream.onData =
- () => Expect.fail("Response data not expected");
- response.inputStream.onClosed = () {
- Expect.equals(1, requestCount);
- server.close();
- client.shutdown();
- };
- };
-
- HttpClientConnection conn =
- client.getUrl(
- Uri.parse("http://127.0.0.1:${server.port}/redirect"));
- conn.onRequest = onRequest;
- conn.onResponse = onResponse;
- conn.onError = (e) => Expect.fail("Error not expected ($e)");
+ client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/redirect"))
+ .then((HttpClientRequest request) {
+ return request.close();
+ })
+ .then((HttpClientResponse response) {
+ response.listen(
+ (_) => Expect.fail("Response data not expected"),
+ onDone: () {
+ Expect.equals(1, response.redirects.length);
+ server.close();
+ client.close();
+ });
+ });
+ });
}
void testAutoRedirectWithHeaders() {
- HttpServer server = setupServer();
- HttpClient client = new HttpClient();
+ setupServer().then((server) {
+ HttpClient client = new HttpClient();
- var requestCount = 0;
-
- void onRequest(HttpClientRequest request) {
- requestCount++;
- request.headers.add("X-Request-Header", "value");
- request.outputStream.close();
- };
-
- void onResponse(HttpClientResponse response) {
- response.inputStream.onData =
- () => Expect.fail("Response data not expected");
- response.inputStream.onClosed = () {
- Expect.equals(1, requestCount);
- server.close();
- client.shutdown();
- };
- };
-
- HttpClientConnection conn =
- client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/src"));
- conn.onRequest = onRequest;
- conn.onResponse = onResponse;
- conn.onError = (e) => Expect.fail("Error not expected ($e)");
+ client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/src"))
+ .then((HttpClientRequest request) {
+ request.headers.add("X-Request-Header", "value");
+ return request.close();
+ })
+ .then((HttpClientResponse response) {
+ response.listen(
+ (_) => Expect.fail("Response data not expected"),
+ onDone: () {
+ Expect.equals(1, response.redirects.length);
+ server.close();
+ client.close();
+ });
+ });
+ });
}
void testAutoRedirect301POST() {
- HttpServer server = setupServer();
- HttpClient client = new HttpClient();
+ setupServer().then((server) {
+ HttpClient client = new HttpClient();
- var requestCount = 0;
-
- void onRequest(HttpClientRequest request) {
- requestCount++;
- request.outputStream.close();
- };
-
- void onResponse(HttpClientResponse response) {
- Expect.equals(HttpStatus.MOVED_PERMANENTLY, response.statusCode);
- response.inputStream.onData =
- () => Expect.fail("Response data not expected");
- response.inputStream.onClosed = () {
- Expect.equals(1, requestCount);
- server.close();
- client.shutdown();
- };
- };
-
- HttpClientConnection conn =
- client.postUrl(
- Uri.parse("http://127.0.0.1:${server.port}/301src"));
- conn.onRequest = onRequest;
- conn.onResponse = onResponse;
- conn.onError = (e) => Expect.fail("Error not expected ($e)");
+ client.postUrl(Uri.parse("http://127.0.0.1:${server.port}/301src"))
+ .then((HttpClientRequest request) {
+ return request.close();
+ })
+ .then((HttpClientResponse response) {
+ Expect.equals(HttpStatus.MOVED_PERMANENTLY, response.statusCode);
+ response.listen(
+ (_) => Expect.fail("Response data not expected"),
+ onDone: () {
+ Expect.equals(0, response.redirects.length);
+ server.close();
+ client.close();
+ });
+ });
+ });
}
void testAutoRedirect303POST() {
- HttpServer server = setupServer();
- HttpClient client = new HttpClient();
+ setupServer().then((server) {
+ HttpClient client = new HttpClient();
- var requestCount = 0;
-
- void onRequest(HttpClientRequest request) {
- requestCount++;
- request.contentLength = 10;
- request.outputStream.write(new List<int>.fixedLength(10, fill: 0));
- request.outputStream.close();
- };
-
- void onResponse(HttpClientResponse response) {
- Expect.equals(HttpStatus.OK, response.statusCode);
- response.inputStream.onData =
- () => Expect.fail("Response data not expected");
- response.inputStream.onClosed = () {
- Expect.equals(1, requestCount);
- server.close();
- client.shutdown();
- };
- };
-
- HttpClientConnection conn =
- client.postUrl(
- Uri.parse("http://127.0.0.1:${server.port}/303src"));
- conn.onRequest = onRequest;
- conn.onResponse = onResponse;
- conn.onError = (e) => Expect.fail("Error not expected ($e)");
+ client.postUrl(Uri.parse("http://127.0.0.1:${server.port}/303src"))
+ .then((HttpClientRequest request) {
+ return request.close();
+ })
+ .then((HttpClientResponse response) {
+ Expect.equals(HttpStatus.OK, response.statusCode);
+ response.listen(
+ (_) => Expect.fail("Response data not expected"),
+ onDone: () {
+ Expect.equals(1, response.redirects.length);
+ server.close();
+ client.close();
+ });
+ });
+ });
}
void testAutoRedirectLimit() {
- HttpServer server = setupServer();
- HttpClient client = new HttpClient();
+ setupServer().then((server) {
+ HttpClient client = new HttpClient();
- HttpClientConnection conn =
- client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/1"));
- conn.onResponse = (HttpClientResponse response) {
- response.inputStream.onData = () => Expect.fail("Response not expected");
- response.inputStream.onClosed = () => Expect.fail("Response not expected");
- };
- conn.onError = (e) {
- Expect.isTrue(e is RedirectLimitExceededException);
- Expect.equals(5, e.redirects.length);
- server.close();
- client.shutdown();
- };
+ client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/1"))
+ .then((HttpClientRequest request) => request.close())
+ .catchError((e) {
+ Expect.equals(5, e.error.redirects.length);
+ server.close();
+ client.close();
+ }, test: (e) => e is RedirectLimitExceededException);
+ });
}
void testRedirectLoop() {
- HttpServer server = setupServer();
- HttpClient client = new HttpClient();
+ setupServer().then((server) {
+ HttpClient client = new HttpClient();
- int redirectCount = 0;
- HttpClientConnection conn =
- client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/A"));
- conn.onResponse = (HttpClientResponse response) {
- response.inputStream.onData = () => Expect.fail("Response not expected");
- response.inputStream.onClosed = () => Expect.fail("Response not expected");
- };
- conn.onError = (e) {
- Expect.isTrue(e is RedirectLoopException);
- Expect.equals(2, e.redirects.length);
- server.close();
- client.shutdown();
- };
+ int redirectCount = 0;
+ client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/A"))
+ .then((HttpClientRequest request) => request.close())
+ .catchError((e) {
+ Expect.equals(2, e.error.redirects.length);
+ server.close();
+ client.close();
+ }, test: (e) => e is RedirectLoopException);
+ });
}
void testRedirectClosingConnection() {
- HttpServer server = setupServer();
- HttpClient client = new HttpClient();
+ setupServer().then((server) {
+ HttpClient client = new HttpClient();
- int redirectCount = 0;
- HttpClientConnection conn =
- client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/closing"));
-
- conn.followRedirects = true;
- conn.onResponse = (HttpClientResponse response) {
- response.inputStream.onData = () => Expect.fail("Response not expected");
- response.inputStream.onClosed = () => Expect.fail("Response not expected");
- };
- conn.onError = (e) {
- Expect.isTrue(e is RedirectException);
- Expect.isNull(e.redirects);
- server.close();
- client.shutdown();
- };
+ client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/closing"))
+ .then((request) => request.close())
+ .then((response) {
+ response.listen(
+ (_) {},
+ onDone: () {
+ Expect.equals(1, response.redirects.length);
+ server.close();
+ client.close();
+ });
+ });
+ });
}
main() {
diff --git a/tests/standalone/io/http_server_early_client_close_test.dart b/tests/standalone/io/http_server_early_client_close_test.dart
index 624442f..b59857e 100644
--- a/tests/standalone/io/http_server_early_client_close_test.dart
+++ b/tests/standalone/io/http_server_early_client_close_test.dart
@@ -7,16 +7,16 @@
import "dart:isolate";
void sendData(List<int> data, int port) {
- Socket socket = new Socket("127.0.0.1", port);
- socket.onConnect = () {
- socket.onData = () {
- Expect.fail("No data response was expected");
- };
- socket.outputStream.onNoPendingWrites = () {
- socket.close(true);
- };
- socket.outputStream.write(data);
- };
+ Socket.connect("127.0.0.1", port).then((socket) {
+ socket.listen((data) {
+ Expect.fail("No data response was expected");
+ });
+ socket.add(data);
+ socket.close();
+ socket.done.then((_) {
+ socket.destroy();
+ });
+ });
}
class EarlyCloseTest {
@@ -29,22 +29,31 @@
bool calledOnRequest = false;
bool calledOnError = false;
- server.defaultRequestHandler =
- (HttpRequest request, HttpResponse response) {
+ ReceivePort port = new ReceivePort();
+ server.listen(
+ (request) {
Expect.isTrue(expectRequest);
Expect.isFalse(calledOnError);
Expect.isFalse(calledOnRequest, "onRequest called multiple times");
calledOnRequest = true;
- };
- ReceivePort port = new ReceivePort();
- server.onError = (error) {
- Expect.isFalse(calledOnError);
- Expect.equals(exception, error.message);
- Expect.equals(expectRequest, calledOnRequest);
- calledOnError = true;
- port.close();
- c.complete(null);
- };
+ request.listen(
+ (_) {},
+ onError: (e) {
+ Expect.isFalse(calledOnError);
+ Expect.equals(exception, e.error.message);
+ calledOnError = true;
+ port.close();
+ c.complete(null);
+ });
+ },
+ onError: (e) {
+ Expect.isFalse(calledOnError);
+ Expect.equals(exception, e.error.message);
+ Expect.equals(expectRequest, calledOnRequest);
+ calledOnError = true;
+ port.close();
+ c.complete(null);
+ });
List<int> d;
if (data is List<int>) d = data;
@@ -68,7 +77,7 @@
// The empty packet is valid.
// Close while sending header
- String message = "Connection closed before full request header was received";
+ String message = "Connection closed before full header was received";
add("G", message);
add("GET /", message);
add("GET / HTTP/1.1", message);
@@ -76,54 +85,55 @@
// Close while sending content
add("GET / HTTP/1.1\r\nContent-Length: 100\r\n\r\n",
- "Connection closed before full request body was received",
+ "Connection closed while receiving data",
expectRequest: true);
add("GET / HTTP/1.1\r\nContent-Length: 100\r\n\r\n1",
- "Connection closed before full request body was received",
+ "Connection closed while receiving data",
expectRequest: true);
-
- HttpServer server = new HttpServer();
- server.listen("127.0.0.1", 0);
void runTest(Iterator it) {
if (it.moveNext()) {
- it.current.execute(server).then((_) => runTest(it));
- } else {
- server.close();
+ HttpServer.bind("127.0.0.1", 0).then((server) {
+ it.current.execute(server).then((_) {
+ runTest(it);
+ server.close();
+ });
+ });
}
}
runTest(tests.iterator);
}
testEarlyClose2() {
- var server = new HttpServer();
- server.listen("127.0.0.1", 0);
- server.onError = (e) { /* ignore */ };
- server.defaultRequestHandler = (request, response) {
- String name = new Options().script;
- new File(name).openInputStream().pipe(response.outputStream);
- };
+ HttpServer.bind("127.0.0.1", 0).then((server) {
+ server.listen(
+ (request) {
+ String name = new Options().script;
+ new File(name).openRead().pipe(request.response);
+ },
+ onError: (e) { /* ignore */ });
- var count = 0;
- var makeRequest;
- makeRequest = () {
- Socket socket = new Socket("127.0.0.1", server.port);
- socket.onConnect = () {
- var data = "GET / HTTP/1.1\r\nContent-Length: 0\r\n\r\n".charCodes;
- socket.writeList(data, 0, data.length);
- socket.close();
- if (++count < 10) {
- makeRequest();
- } else {
- server.close();
- }
- };
- };
-
- makeRequest();
+ var count = 0;
+ makeRequest() {
+ Socket.connect("127.0.0.1", server.port).then((socket) {
+ var data = "GET / HTTP/1.1\r\nContent-Length: 0\r\n\r\n".charCodes;
+ socket.add(data);
+ socket.close();
+ socket.done.then((_) {
+ socket.destroy();
+ if (++count < 10) {
+ makeRequest();
+ } else {
+ server.close();
+ }
+ });
+ });
+ }
+ makeRequest();
+ });
}
void main() {
testEarlyClose1();
- testEarlyClose2();
+// testEarlyClose2();
}
diff --git a/tests/standalone/io/http_server_early_server_close_test.dart b/tests/standalone/io/http_server_early_server_close_test.dart
index 7609257..79f1254 100644
--- a/tests/standalone/io/http_server_early_server_close_test.dart
+++ b/tests/standalone/io/http_server_early_server_close_test.dart
@@ -7,39 +7,38 @@
import "dart:isolate";
class Server {
- Server() {
- HttpServer server = new HttpServer();
- server.listen("127.0.0.1", 0);
- port = server.port;
- server.defaultRequestHandler =
- (HttpRequest request, HttpResponse response) {
- Timer.run(server.close);
- };
- server.onError = (e) {
- Expect.fail("No server errors expected: $e");
- };
+ static Future<int> start() {
+ return HttpServer.bind("127.0.0.1", 0).then((server) {
+ server.listen((HttpRequest request) {
+ Timer.run(server.close);
+ }, onError: (e) {
+ Expect.fail("No server errors expected: $e");
+ });
+ return server.port;
+ });
}
- int port;
}
class Client {
Client(int port) {
ReceivePort r = new ReceivePort();
HttpClient client = new HttpClient();
- HttpClientConnection c = client.get("127.0.0.1", port, "/");
- c.onRequest = (HttpClientRequest request) {
- request.outputStream.close();
- };
- c.onResponse = (HttpClientResponse response) {
- Expect.fail("Response should not be given, as not data was returned.");
- };
- c.onError = (e) {
- r.close();
- };
+ client.get("127.0.0.1", port, "/")
+ .then((HttpClientRequest request) {
+ return request.close();
+ })
+ .then((HttpClientResponse response) {
+ Expect.fail(
+ "Response should not be given, as not data was returned.");
+ })
+ .catchError((e) {
+ r.close();
+ });
}
}
main() {
- Server server = new Server();
- new Client(server.port);
+ Server.start().then((port) {
+ new Client(port);
+ });
}
diff --git a/tests/standalone/io/http_server_handler_test.dart b/tests/standalone/io/http_server_handler_test.dart
deleted file mode 100644
index 62d77ed..0000000
--- a/tests/standalone/io/http_server_handler_test.dart
+++ /dev/null
@@ -1,187 +0,0 @@
-// 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.
-//
-
-import 'dart:io';
-import 'dart:isolate';
-
-class Handler1 {
- void onRequest(HttpRequest request, HttpResponse response) {
- response.outputStream.writeString("Handler 1");
- response.outputStream.close();
- }
-}
-
-class Handler2 {
- void onRequest(HttpRequest request, HttpResponse response) {
- response.outputStream.writeString("Handler 2");
- response.outputStream.close();
- }
-}
-
-class French404Handler {
- void onRequest(HttpRequest request, HttpResponse response) {
- response.statusCode = HttpStatus.NOT_FOUND;
- response.reasonPhrase = "Non Trouvé";
- response.outputStream.close();
- }
-}
-
-class Server {
- Server() {
- server = new HttpServer();
- server.listen("127.0.0.1", 0);
- port = server.port;
- server.onError = (e) {
- Expect.fail("No server errors expected: $e");
- };
- }
-
- void addHandler(Function matcher, handler) {
- if (handler is Function) {
- server.addRequestHandler(matcher, handler);
- } else {
- server.addRequestHandler(matcher, handler.onRequest);
- }
- }
-
- void set defaultHandler(handler) {
- if (handler is Function) {
- server.defaultRequestHandler = handler;
- } else {
- server.defaultRequestHandler = handler.onRequest;
- }
- }
-
- void close() {
- server.close();
- }
-
- int port;
- HttpServer server;
-}
-
-void testDefaultHandler() {
- Server server = new Server();
- HttpClient client = new HttpClient();
-
- void done() {
- server.close();
- client.shutdown();
- }
-
- void error(e) {
- Expect.fail("No client error expected $e");
- done();
- };
-
- void german404(HttpRequest request, HttpResponse response) {
- response.statusCode = HttpStatus.NOT_FOUND;
- response.reasonPhrase = "Nicht Gefunden";
- response.outputStream.close();
- }
-
- // Test the standard default handler.
- HttpClientConnection conn = client.get("127.0.0.1", server.port, "/");
- conn.onResponse = (HttpClientResponse response) {
- Expect.equals(HttpStatus.NOT_FOUND, response.statusCode);
- Expect.equals("Not Found", response.reasonPhrase);
-
- // Install a default handler.
- server.defaultHandler = german404;
- HttpClientConnection conn = client.get("127.0.0.1", server.port, "/");
- conn.onResponse = (HttpClientResponse response) {
- Expect.equals(HttpStatus.NOT_FOUND, response.statusCode);
- Expect.equals("Nicht Gefunden", response.reasonPhrase);
-
- // Install another default handler.
- server.defaultHandler = new French404Handler();
- HttpClientConnection conn = client.get("127.0.0.1", server.port, "/");
- conn.onResponse = (HttpClientResponse response) {
- Expect.equals(HttpStatus.NOT_FOUND, response.statusCode);
- Expect.equals("Non Trouvé", response.reasonPhrase);
- response.inputStream.onClosed = done;
- };
- conn.onError = error;
- };
- conn.onError = error;
- };
- conn.onError = error;
-}
-
-void testHandlers() {
- Server server = new Server();
- HttpClient client = new HttpClient();
- int requests = 0;
- int doneCount = 0;
-
- void done() {
- doneCount++;
- if (doneCount == requests) {
- server.close();
- client.shutdown();
- }
- }
-
- void error(e) {
- Expect.fail("No client error expected $e");
- done();
- };
-
- void handler3(HttpRequest request, HttpResponse response) {
- response.statusCode = HttpStatus.OK;
- response.outputStream.writeString("Handler 3");
- response.outputStream.close();
- }
-
- void handler4(HttpRequest request, HttpResponse response) {
- response.statusCode = HttpStatus.OK;
- response.outputStream.writeString("Handler 4");
- response.outputStream.close();
- }
-
- void checkBody(HttpClientResponse response, String expected) {
- StringBuffer sb = new StringBuffer();
- StringInputStream stream = new StringInputStream(response.inputStream);
- stream.onData = () {
- sb.add(stream.read());
- };
- stream.onClosed = () {
- Expect.equals(expected, sb.toString());
- done();
- };
- }
-
- server.addHandler(
- (request) => request.path.startsWith("/xxx/yyy/"), new Handler1());
- server.addHandler(
- (request) => new RegExp("^/xxx").hasMatch(request.path), new Handler2());
- server.addHandler(
- (request) => request.path == "/yyy.dat", handler3);
- server.addHandler(
- (request) => request.path.endsWith(".dat"), handler4);
-
- void testRequest(String path, int expectedStatus, String expectedBody) {
- HttpClientConnection conn =
- client.get("127.0.0.1", server.port, path);
- requests++;
- conn.onResponse = (HttpClientResponse response) {
- Expect.equals(expectedStatus, response.statusCode);
- checkBody(response, expectedBody);
- };
- conn.onError = error;
- }
-
- testRequest("/xxx/yyy/zzz", HttpStatus.OK, "Handler 1");
- testRequest("/xxx/zzz", HttpStatus.OK, "Handler 2");
- testRequest("/yyy.dat", HttpStatus.OK, "Handler 3");
- testRequest("/abc.dat", HttpStatus.OK, "Handler 4");
- testRequest("/abcdat", HttpStatus.NOT_FOUND, "");
- testRequest("/xxx.dat", HttpStatus.OK, "Handler 2");
-}
-
-main() {
- testDefaultHandler();
- testHandlers();
-}
diff --git a/tests/standalone/io/http_server_response_test.dart b/tests/standalone/io/http_server_response_test.dart
new file mode 100644
index 0000000..674c901
--- /dev/null
+++ b/tests/standalone/io/http_server_response_test.dart
@@ -0,0 +1,100 @@
+// 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.
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
+
+import "dart:io";
+import "dart:scalarlist";
+
+void testServerRequest(void handler(server, request)) {
+ HttpServer.bind().then((server) {
+ server.listen((request) {
+ handler(server, request);
+ });
+
+ var client = new HttpClient();
+ // We only close the client on either
+ // - Bad response headers
+ // - Response done (with optional errors in between).
+ client.get("127.0.0.1", server.port, "/")
+ .then((request) => request.close())
+ .then((response) {
+ response.listen((_) {}, onDone: () {
+ client.close();
+ }, onError: (error) {
+ Expect.isTrue(error.error is HttpParserException);
+ });
+ })
+ .catchError((error) {
+ client.close();
+ }, test: (e) => e is HttpParserException);
+ });
+}
+
+void testResponseDone() {
+ testServerRequest((server, request) {
+ request.response.close();
+ request.response.done.then((response) {
+ Expect.equals(request.response, response);
+ server.close();
+ });
+ });
+}
+
+void testBadResponseAdd() {
+ testServerRequest((server, request) {
+ request.response.contentLength = 0;
+ request.response.add([0]);
+ request.response.done.catchError((error) {
+ server.close();
+ }, test: (e) => e is HttpException);
+ });
+
+ testServerRequest((server, request) {
+ request.response.contentLength = 5;
+ request.response.add([0, 0, 0]);
+ request.response.add([0, 0, 0]);
+ request.response.done.catchError((error) {
+ server.close();
+ }, test: (e) => e is HttpException);
+ });
+
+ testServerRequest((server, request) {
+ request.response.contentLength = 0;
+ request.response.add(new Uint8List(64 * 1024));
+ request.response.add(new Uint8List(64 * 1024));
+ request.response.add(new Uint8List(64 * 1024));
+ request.response.done.catchError((error) {
+ server.close();
+ }, test: (e) => e is HttpException);
+ });
+}
+
+void testBadResponseClose() {
+ testServerRequest((server, request) {
+ request.response.contentLength = 5;
+ request.response.close();
+ request.response.done.catchError((error) {
+ server.close();
+ }, test: (e) => e is HttpException);
+ });
+
+ testServerRequest((server, request) {
+ request.response.contentLength = 5;
+ request.response.add([0]);
+ request.response.close();
+ request.response.done.catchError((error) {
+ server.close();
+ }, test: (e) => e is HttpException);
+ });
+}
+
+void main() {
+ testResponseDone();
+ testBadResponseAdd();
+ testBadResponseClose();
+}
diff --git a/tests/standalone/io/http_server_socket_test.dart b/tests/standalone/io/http_server_socket_test.dart
deleted file mode 100644
index 13b92d8..0000000
--- a/tests/standalone/io/http_server_socket_test.dart
+++ /dev/null
@@ -1,237 +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.
-
-import "dart:io";
-import "dart:isolate";
-import "dart:math";
-
-class ExpectedDataOutputStream implements OutputStream {
- ExpectedDataOutputStream(List<int> this._data,
- int this._cutoff,
- bool this._closeAsError,
- SocketMock this._socket);
-
- void set onNoPendingWrites(void callback()) {
- _onNoPendingWrites = callback;
- }
-
- void set onClosed(void callback()) {
- // Not used in test.
- }
-
- void set onError(void callback(e)) {
- _onError = callback;
- }
-
- bool write(List data, [bool copyBuffer = true]) {
- _onData(data);
- return true;
- }
-
- bool writeFrom(List data, [int offset = 0, int len]) {
- if (len == null) len = data.length - offset;
- _onData(data.getRange(offset, len));
- return true;
- }
-
- void close() {
- _socket.close(true);
- }
-
- void _onData(List<int> data) {
- // TODO(ajohnsen): To be removed, since the socket should not be written to
- // after close.
- if (_socket._closed) return;
- Expect.isFalse(_written > _cutoff);
- Expect.listEquals(data, _data.getRange(0, data.length));
- _data = _data.getRange(data.length, _data.length - data.length);
- _written += data.length;
- if (_written >= _cutoff) {
- // Tell HttpServer that the socket have closed.
- _socket._closeInternal(_closeAsError);
- }
- }
-
- Function _onNoPendingWrites;
- Function _onError;
- List<int> _data;
- int _written = 0;
- int _cutoff;
- bool _closeAsError;
- SocketMock _socket;
-}
-
-class SocketMock implements Socket {
- SocketMock(List<int> this._data,
- List<int> expected,
- int cutoff,
- bool closeAsError) :
- _hashCode = new Random().nextInt(1<< 32),
- _read = [] {
- _outputStream =
- new ExpectedDataOutputStream(expected, cutoff, closeAsError, this);
- }
-
- int available() {
- return _data.length;
- }
-
- void _closeInternal([bool asError = false]) {
- Expect.isFalse(_closed);
- _closed = true;
- _onClosedInternal();
- if (asError) {
- _onError(new Exception("Socket closed unexpected"));
- } else {
- _onClosed();
- }
- }
-
- List<int> read([int len]) {
- var result;
- if (len == null) {
- result = _data;
- _data = [];
- } else {
- result = new Uint8List(len);
- readList(result, 0, len);
- }
- return result;
- }
-
- int readList(List<int> buffer, int offset, int count) {
- int max = min(count, _data.length);
- buffer.setRange(offset, max, _data);
- _data = _data.getRange(max, _data.length - max);
- return max;
- }
-
- void close([bool halfClose = false]) {
- if (!halfClose && !_closed) _closeInternal();
- }
-
- void set onData(void callback()) {
- _onData = callback;
- }
-
- void set onClosed(void callback()) {
- _onClosed = callback;
- }
-
- void set onError(void callback(e)) {
- _onError = callback;
- }
-
- OutputStream get outputStream => _outputStream;
-
- int get hashCode => _hashCode;
-
- List<int> _read;
- bool _closed = false;
- int _hashCode;
- Function _onData;
- Function _onClosed;
- Function _onError;
- Function _onClosedInternal;
- List<int> _data;
- ExpectedDataOutputStream _outputStream;
-}
-
-class ServerSocketMock implements ServerSocket {
- ServerSocketMock(String addr, int this._port, int backlog) :
- _sockets = new Set<Socket>();
-
- void spawnSocket(var data, String response, int cutOff, bool closeAsError) {
- if (data is String) data = data.charCodes;
- SocketMock socket = new SocketMock(data,
- response.charCodes,
- cutOff,
- closeAsError);
- _sockets.add(socket);
- ReceivePort port = new ReceivePort();
- socket._onClosedInternal = () {
- // The server should always close the connection.
- _sockets.remove(socket);
- port.close();
- };
- // Tell HttpServer that a connection have come to life.
- _onConnection(socket);
- // Start 'sending' data.
- socket._onData();
- }
-
- void close() {
- Expect.fail("Don't close the connection, we attach to this socket");
- }
-
- void set onConnection(void callback(Socket connection)) {
- _onConnection = callback;
- }
-
- void set onError(void callback(e)) {
- _onError = callback;
- }
-
- int get port => _port;
-
- int _port;
- Function _onConnection;
- Function _onError;
- Set<Socket> _sockets;
-}
-
-void testSocketClose() {
- ServerSocketMock serverSocket = new ServerSocketMock("0.0.0.0", 5432, 5);
-
- HttpServer server = new HttpServer();
- server.listenOn(serverSocket);
- void testContent(String requestString,
- String responseString,
- [int okayFrom = 0,
- bool expectError = true]) {
- // Inner callback to actually run a given setting.
- void runSettings(int cutoff,
- bool closeAsError,
- bool expectError) {
- server.defaultRequestHandler =
- (HttpRequest request, HttpResponse response) {
- request.inputStream.onData = () {
- };
- request.inputStream.onClosed = () {
- response.outputStream.close();
- };
- };
-
- if (expectError) {
- ReceivePort port = new ReceivePort();
- server.onError = (var error) {
- port.close();
- };
- } else {
- server.onError = (var error) {
- Expect.fail("An error was not expected: $error");
- };
- }
-
- serverSocket.spawnSocket(requestString, responseString,
- cutoff, closeAsError);
- // TODO(ajohnsen): Validate HttpServers number of connections.
- }
- for (int i = 1; i < responseString.length; i++) {
- bool _expectError = expectError && i < responseString.length - okayFrom;
- runSettings(i, false, _expectError);
- runSettings(i, true, _expectError);
- }
- }
- testContent(
- "GET / HTTP/1.1\r\nKeep-Alive: False\r\n\r\n",
- "HTTP/1.1 200 OK\r\ntransfer-encoding: chunked\r\nconnection: close"
- "\r\n\r\n0\r\n\r\n");
-
- server.close();
-}
-
-void main() {
- testSocketClose();
-}
diff --git a/tests/standalone/io/http_server_test.dart b/tests/standalone/io/http_server_test.dart
index 8e7424e..dd6eeac 100644
--- a/tests/standalone/io/http_server_test.dart
+++ b/tests/standalone/io/http_server_test.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
@@ -6,56 +6,53 @@
import "dart:isolate";
void testListenOn() {
- ServerSocket socket = new ServerSocket("127.0.0.1", 0, 5);
-
- socket.onError = (Exception e) {
- Expect.fail("ServerSocket closed unexpected");
- };
+ ServerSocket socket;
+ HttpServer server;
void test(void onDone()) {
- HttpServer server = new HttpServer();
- Expect.throws(() => server.port);
- ReceivePort serverPort = new ReceivePort();
- server.defaultRequestHandler =
- (HttpRequest request, HttpResponse response) {
- request.inputStream.onClosed = () {
- response.outputStream.close();
- serverPort.close();
- };
- };
-
- server.onError = (Exception e) {
- Expect.fail("Unexpected error in Http Server: $e");
- };
-
- server.listenOn(socket);
Expect.equals(socket.port, server.port);
- HttpClient client = new HttpClient();
- HttpClientConnection conn = client.get("127.0.0.1", socket.port, "/");
- conn.onRequest = (HttpClientRequest request) {
- request.outputStream.close();
- };
ReceivePort clientPort = new ReceivePort();
- conn.onResponse = (HttpClientResponse response) {
- response.inputStream.onClosed = () {
- client.shutdown();
- clientPort.close();
- server.close();
- Expect.throws(() => server.port);
- onDone();
- };
- };
- conn.onError = (Exception e) {
- Expect.fail("Unexpected error in Http Client: $e");
- };
- };
+ HttpClient client = new HttpClient();
+ client.get("127.0.0.1", socket.port, "/")
+ .then((request) {
+ return request.close();
+ })
+ .then((response) {
+ response.listen(
+ (_) {},
+ onDone: () {
+ client.close();
+ clientPort.close();
+ onDone();
+ });
+ })
+ .catchError((error) {
+ Expect.fail("Unexpected error in Http Client: $error");
+ });
+ }
// Test two connection after each other.
- test(() {
+ ServerSocket.bind().then((s) {
+ socket = s;
+ server = new HttpServer.listenOn(socket);
+ ReceivePort serverPort = new ReceivePort();
+ server.listen((HttpRequest request) {
+ request.listen(
+ (_) {},
+ onDone: () {
+ request.response.close();
+ serverPort.close();
+ });
+ });
+
test(() {
- socket.close();
+ test(() {
+ server.close();
+ Expect.throws(() => server.port);
+ socket.close();
+ });
});
});
}
diff --git a/tests/standalone/io/http_session_test.dart b/tests/standalone/io/http_session_test.dart
index 796408f..69f6a6b 100644
--- a/tests/standalone/io/http_session_test.dart
+++ b/tests/standalone/io/http_session_test.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
@@ -20,89 +20,137 @@
return id;
}
-Future<String> connectGetSession(int port, [String session]) {
- var c = new Completer();
- var client = new HttpClient();
- var conn = client.get("127.0.0.1", port, "/");
- conn.onRequest = (request) {
- if (session != null) {
- request.cookies.add(new Cookie(SESSION_ID, session));
- }
- request.outputStream.close();
- };
- conn.onResponse = (response) {
- response.inputStream.onData = response.inputStream.read;
- response.inputStream.onClosed = () {
- client.shutdown();
- c.complete(getSessionId(response.cookies));
- };
- };
- return c.future;
+Future<String> connectGetSession(
+ HttpClient client, int port, [String session]) {
+ return client.get("127.0.0.1", port, "/")
+ .then((request) {
+ if (session != null) {
+ request.cookies.add(new Cookie(SESSION_ID, session));
+ }
+ return request.close();
+ })
+ .then((response) {
+ return response.reduce(getSessionId(response.cookies), (v, _) => v);
+ });
}
void testSessions(int sessionCount) {
- HttpServer server = new HttpServer();
- server.listen("127.0.0.1", 0);
- var sessions = new Set();
- server.defaultRequestHandler = (request, response) {
- sessions.add(request.session().id);
- response.outputStream.close();
- };
+ var client = new HttpClient();
+ HttpServer.bind().then((server) {
+ var sessions = new Set();
+ server.listen((request) {
+ sessions.add(request.session.id);
+ request.response.close();
+ });
- var futures = [];
- for (int i = 0; i < sessionCount; i++) {
- futures.add(connectGetSession(server.port).then((session) {
- Expect.isNotNull(session);
- Expect.isTrue(sessions.contains(session));
- return connectGetSession(server.port, session).then((session2) {
- Expect.equals(session2, session);
- Expect.isTrue(sessions.contains(session2));
- return session2;
+ var futures = [];
+ for (int i = 0; i < sessionCount; i++) {
+ futures.add(connectGetSession(client, server.port).then((session) {
+ Expect.isNotNull(session);
+ Expect.isTrue(sessions.contains(session));
+ return connectGetSession(client, server.port, session).then((session2) {
+ Expect.equals(session2, session);
+ Expect.isTrue(sessions.contains(session2));
+ return session2;
});
- }));
- }
- Future.wait(futures).then((clientSessions) {
- Expect.equals(sessions.length, sessionCount);
- Expect.setEquals(new Set.from(clientSessions), sessions);
- server.close();
+ }));
+ }
+ Future.wait(futures).then((clientSessions) {
+ Expect.equals(sessions.length, sessionCount);
+ Expect.setEquals(new Set.from(clientSessions), sessions);
+ server.close();
+ client.close();
+ });
});
}
void testTimeout(int sessionCount) {
- HttpServer server = new HttpServer();
- server.sessionTimeout = 0;
- server.listen("127.0.0.1", 0);
- var timeouts = [];
- server.defaultRequestHandler = (request, response) {
- var c = new Completer();
- timeouts.add(c.future);
- request.session().onTimeout = () {
- c.complete(null);
- };
- response.outputStream.close();
- };
+ var client = new HttpClient();
+ HttpServer.bind().then((server) {
+ server.sessionTimeout = 0;
+ var timeouts = [];
+ server.listen((request) {
+ var c = new Completer();
+ timeouts.add(c.future);
+ request.session.onTimeout = () {
+ c.complete(null);
+ };
+ request.response.close();
+ });
- var futures = [];
- for (int i = 0; i < sessionCount; i++) {
- futures.add(connectGetSession(server.port));
- }
- Future.wait(futures).then((clientSessions) {
- Future.wait(timeouts).then((_) {
- futures = [];
- for (var id in clientSessions) {
- futures.add(connectGetSession(server.port, id).then((session) {
- Expect.isNotNull(session);
- Expect.notEquals(id, session);
- }));
- }
- Future.wait(futures).then((_) {
- server.close();
+ var futures = [];
+ for (int i = 0; i < sessionCount; i++) {
+ futures.add(connectGetSession(client, server.port));
+ }
+ Future.wait(futures).then((clientSessions) {
+ Future.wait(timeouts).then((_) {
+ futures = [];
+ for (var id in clientSessions) {
+ futures.add(connectGetSession(
+ client, server.port, id).then((session) {
+ Expect.isNotNull(session);
+ Expect.notEquals(id, session);
+ }));
+ }
+ Future.wait(futures).then((_) {
+ server.close();
+ client.close();
+ });
});
});
});
}
+void testSessionsData() {
+ HttpServer.bind().then((server) {
+ bool firstHit = false;
+ bool secondHit = false;
+ server.listen((request) {
+ var c = new Completer();
+ var session = request.session;
+ if (session.isNew) {
+ Expect.isFalse(firstHit);
+ Expect.isFalse(secondHit);
+ firstHit = true;
+ session["data"] = "some data";
+ } else {
+ Expect.isTrue(firstHit);
+ Expect.isFalse(secondHit);
+ secondHit = true;
+ Expect.isTrue(session.containsKey("data"));
+ Expect.equals("some data", session["data"]);
+ };
+ request.response.close();
+ });
+
+ var client = new HttpClient();
+ client.get("127.0.0.1", server.port, "/")
+ .then((request) => request.close())
+ .then((response) {
+ response.listen((_) {}, onDone: () {
+ var id = getSessionId(response.cookies);
+ Expect.isNotNull(id);
+ client.get("127.0.0.1", server.port, "/")
+ .then((request) {
+ request.cookies.add(new Cookie(SESSION_ID, id));
+ return request.close();
+ })
+ .then((response) {
+ response.listen((_) {}, onDone: () {
+ Expect.isTrue(firstHit);
+ Expect.isTrue(secondHit);
+ Expect.equals(id, getSessionId(response.cookies));
+ server.close();
+ client.close();
+ });
+ });
+ });
+ });
+ });
+}
+
void main() {
- testSessions(5);
+ testSessions(1);
testTimeout(5);
+ testSessionsData();
}
diff --git a/tests/standalone/io/http_shutdown_test.dart b/tests/standalone/io/http_shutdown_test.dart
index c296be5..c7bcc16 100644
--- a/tests/standalone/io/http_shutdown_test.dart
+++ b/tests/standalone/io/http_shutdown_test.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
//
@@ -8,167 +8,179 @@
void test1(int totalConnections) {
// Server which just closes immediately.
- HttpServer server = new HttpServer();
- server.listen("127.0.0.1", 0, backlog: totalConnections);
- server.defaultRequestHandler = (HttpRequest request, HttpResponse response) {
- response.outputStream.close();
- };
+ HttpServer.bind().then((server) {
+ server.listen((HttpRequest request) {
+ request.response.close();
+ });
- int count = 0;
- HttpClient client = new HttpClient();
- for (int i = 0; i < totalConnections; i++) {
- HttpClientConnection conn = client.get("127.0.0.1", server.port, "/");
- conn.onRequest = (HttpClientRequest request) {
- request.outputStream.close();
- };
- conn.onResponse = (HttpClientResponse response) {
- response.inputStream.onClosed = () {
- count++;
- if (count == totalConnections) {
- client.shutdown();
- server.close();
- }
- };
- };
- }
+ int count = 0;
+ HttpClient client = new HttpClient();
+ for (int i = 0; i < totalConnections; i++) {
+ client.get("127.0.0.1", server.port, "/")
+ .then((HttpClientRequest request) => request.close())
+ .then((HttpClientResponse response) {
+ response.listen((_) {}, onDone: () {
+ count++;
+ if (count == totalConnections) {
+ client.close();
+ server.close();
+ }
+ });
+ });
+ }
+ });
}
void test2(int totalConnections, int outputStreamWrites) {
// Server which responds without waiting for request body.
- HttpServer server = new HttpServer();
- server.listen("127.0.0.1", 0, backlog: totalConnections);
- server.defaultRequestHandler = (HttpRequest request, HttpResponse response) {
- response.outputStream.writeString("!dlrow ,olleH");
- response.outputStream.close();
- };
+ HttpServer.bind().then((server) {
+ server.listen((HttpRequest request) {
+ request.response.addString("!dlrow ,olleH");
+ request.response.close();
+ });
- int count = 0;
- HttpClient client = new HttpClient();
- for (int i = 0; i < totalConnections; i++) {
- HttpClientConnection conn = client.get("127.0.0.1", server.port, "/");
- conn.onRequest = (HttpClientRequest request) {
- request.contentLength = -1;
- for (int i = 0; i < outputStreamWrites; i++)
- request.outputStream.writeString("Hello, world!");
- request.outputStream.close();
- };
- conn.onResponse = (HttpClientResponse response) {
- response.inputStream.onData = response.inputStream.read;
- response.inputStream.onClosed = () {
- count++;
- if (count == totalConnections) {
- client.shutdown();
- server.close();
- }
- };
- };
- conn.onError = (e) {
- count++;
- if (count == totalConnections) {
- client.shutdown();
- server.close();
- }
- };
- }
+ int count = 0;
+ HttpClient client = new HttpClient();
+ for (int i = 0; i < totalConnections; i++) {
+ client.get("127.0.0.1", server.port, "/")
+ .then((HttpClientRequest request) {
+ request.contentLength = -1;
+ for (int i = 0; i < outputStreamWrites; i++) {
+ request.addString("Hello, world!");
+ }
+ return request.close();
+ })
+ .then((HttpClientResponse response) {
+ response.listen(
+ (_) {},
+ onDone: () {
+ count++;
+ if (count == totalConnections) {
+ client.close(force: true);
+ server.close();
+ }
+ },
+ onError: (e) {} /* ignore */);
+ })
+ .catchError((error) {
+ count++;
+ if (count == totalConnections) {
+ client.close();
+ server.close();
+ }
+ });
+ }
+ });
}
void test3(int totalConnections) {
// Server which responds when request body has been received.
- HttpServer server = new HttpServer();
- server.listen("127.0.0.1", 0, backlog: totalConnections);
- server.defaultRequestHandler = (HttpRequest request, HttpResponse response) {
- request.inputStream.onData = () {
- request.inputStream.read();
- };
- request.inputStream.onClosed = () {
- response.outputStream.writeString("!dlrow ,olleH");
- response.outputStream.close();
- };
- };
+ HttpServer.bind().then((server) {
+
+ server.listen((HttpRequest request) {
+ request.listen((_) {}, onDone: () {
+ request.response.addString("!dlrow ,olleH");
+ request.response.close();
+ });
+ });
int count = 0;
HttpClient client = new HttpClient();
for (int i = 0; i < totalConnections; i++) {
- HttpClientConnection conn = client.get("127.0.0.1", server.port, "/");
- conn.onRequest = (HttpClientRequest request) {
- request.contentLength = -1;
- request.outputStream.writeString("Hello, world!");
- request.outputStream.close();
- };
- conn.onResponse = (HttpClientResponse response) {
- response.inputStream.onData = response.inputStream.read;
- response.inputStream.onClosed = () {
- count++;
- if (count == totalConnections) {
- client.shutdown();
- server.close();
- }
- };
- };
+ client.get("127.0.0.1", server.port, "/")
+ .then((HttpClientRequest request) {
+ request.contentLength = -1;
+ request.addString("Hello, world!");
+ return request.close();
+ })
+ .then((HttpClientResponse response) {
+ response.listen((_) {}, onDone: () {
+ count++;
+ if (count == totalConnections) {
+ client.close();
+ server.close();
+ }
+ });
+ });
}
+
+ });
}
void test4() {
- var server = new HttpServer();
- server.listen("127.0.0.1", 0);
- server.defaultRequestHandler = (var request, var response) {
- request.inputStream.onClosed = () {
+ HttpServer.bind().then((server) {
+
+ server.listen((var request) {
+ request.listen((_) {}, onDone: () {
new Timer.repeating(new Duration(milliseconds: 100), (timer) {
if (server.connectionsInfo().total == 0) {
server.close();
timer.cancel();
}
});
- response.outputStream.close();
- };
- };
+ request.response.close();
+ });
+ });
var client= new HttpClient();
- var conn = client.get("127.0.0.1", server.port, "/");
- conn.onResponse = (var response) {
- response.inputStream.onData = response.inputStream.read;
- response.inputStream.onClosed = () {
- client.shutdown();
- };
- };
+ client.get("127.0.0.1", server.port, "/")
+ .then((request) => request.close())
+ .then((response) {
+ response.listen((_) {}, onDone: () {
+ client.close();
+ });
+ });
+
+ });
}
void test5(int totalConnections) {
- var server = new HttpServer();
- server.listen("127.0.0.1", 0, backlog: totalConnections);
- server.defaultRequestHandler = (var request, var response) {
- request.inputStream.onClosed = () {
- response.outputStream.close();
- };
- };
- server.onError = (e) => { };
+ HttpServer.bind().then((server) {
+ server.listen(
+ (request) {
+ request.listen(
+ (_) { },
+ onDone: () {
+ request.response.close();
+ },
+ onError: (error) { });
+ },
+ onError: (error) { });
- // Create a number of client requests and keep then active. Then
- // close the client and wait for the server to lose all active
- // connections.
- var client= new HttpClient();
- for (int i = 0; i < totalConnections; i++) {
- var conn = client.post("127.0.0.1", server.port, "/");
- conn.onRequest = (req) { req.outputStream.write([0]); };
- conn.onError = (e) => Expect.isTrue(e is HttpException);
- }
- bool clientClosed = false;
- new Timer.repeating(new Duration(milliseconds: 100), (timer) {
- if (!clientClosed) {
- if (server.connectionsInfo().total == totalConnections) {
- clientClosed = true;
- client.shutdown(force: true);
- }
- } else {
- if (server.connectionsInfo().total == 0) {
- server.close();
- timer.cancel();
- }
+ // Create a number of client requests and keep then active. Then
+ // close the client and wait for the server to lose all active
+ // connections.
+ var client= new HttpClient();
+ for (int i = 0; i < totalConnections; i++) {
+ client.post("127.0.0.1", server.port, "/")
+ .then((request) {
+ request.add([0]);
+ // TODO(sgjesse): Make this test work with
+ //request.response instead of request.close() return
+ //return request.response;
+ return request.close();
+ })
+ .then((response) { })
+ .catchError((e) { }, test: (e) => e is HttpParserException);
}
+ bool clientClosed = false;
+ new Timer.repeating(new Duration(milliseconds: 100), (timer) {
+ if (!clientClosed) {
+ if (server.connectionsInfo().total == totalConnections) {
+ clientClosed = true;
+ client.close(force: true);
+ }
+ } else {
+ if (server.connectionsInfo().total == 0) {
+ server.close();
+ timer.cancel();
+ }
+ }
+ });
});
}
diff --git a/tests/standalone/io/http_stream_close_test.dart b/tests/standalone/io/http_stream_close_test.dart
index 9f444a6..6cf73d6 100644
--- a/tests/standalone/io/http_stream_close_test.dart
+++ b/tests/standalone/io/http_stream_close_test.dart
@@ -11,47 +11,46 @@
bool clientOnClosed = false;
bool requestOnClosed = false;
- var server = new HttpServer();
- var client = new HttpClient();
+ HttpServer.bind("127.0.0.1", 0).then((server) {
+ var client = new HttpClient();
- checkDone() {
- if (serverOnClosed && clientOnClosed && requestOnClosed) {
- server.close();
- client.shutdown();
+ checkDone() {
+ if (serverOnClosed && clientOnClosed && requestOnClosed) {
+ server.close();
+ client.close();
+ }
}
- }
- server.listen("127.0.0.1", 0);
- server.defaultRequestHandler = (request, response) {
- request.inputStream.onData = request.inputStream.read;
- request.inputStream.onClosed = () {
- response.outputStream.onClosed = () {
- serverOnClosed = true;
- checkDone();
- };
- response.outputStream.writeString("hello!");
- response.outputStream.close();
- };
- };
+ server.listen((request) {
+ request.listen(
+ (_) {},
+ onDone: () {
+ request.response.done.then((_) {
+ serverOnClosed = true;
+ checkDone();
+ });
+ request.response.addString("hello!");
+ request.response.close();
+ });
+ });
- var connection = client.postUrl(
- Uri.parse("http://127.0.0.1:${server.port}"));
- connection.onError = (e) { throw e; };
- connection.onRequest = (request) {
- request.contentLength = "hello!".length;
- request.outputStream.onError = (e) { throw e; };
- request.outputStream.onClosed = () {
- clientOnClosed = true;
- checkDone();
- };
- request.outputStream.writeString("hello!");
- request.outputStream.close();
- };
- connection.onResponse = (response) {
- response.inputStream.onData = response.inputStream.read;
- response.inputStream.onClosed = () {
- requestOnClosed = true;
- checkDone();
- };
- };
+ client.postUrl(Uri.parse("http://127.0.0.1:${server.port}"))
+ .then((request) {
+ request.contentLength = "hello!".length;
+ request.done.then((_) {
+ clientOnClosed = true;
+ checkDone();
+ });
+ request.addString("hello!");
+ return request.close();
+ })
+ .then((response) {
+ response.listen(
+ (_) {},
+ onDone: () {
+ requestOnClosed = true;
+ checkDone();
+ });
+ });
+ });
}
diff --git a/tests/standalone/io/https_client_certificate_test.dart b/tests/standalone/io/https_client_certificate_test.dart
index 67162a5..1d9b0e4 100644
--- a/tests/standalone/io/https_client_certificate_test.dart
+++ b/tests/standalone/io/https_client_certificate_test.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
@@ -10,71 +10,34 @@
const SERVER_ADDRESS = "127.0.0.1";
const HOST_NAME = "localhost";
-int numClientCertificatesReceived = 0;
-Function test(Map options) {
- Future runTest([var unused]) {
- var completer = new Completer();
- HttpsServer server = new HttpsServer();
- Expect.throws(() => server.port);
-
- server.defaultRequestHandler =
- (HttpRequest request, HttpResponse response) {
- if (request.path == '/true') {
- // Client certificate sent
- numClientCertificatesReceived++;
- Expect.isNotNull(request.certificate);
- Expect.equals('CN=localhost', request.certificate.subject);
- } else {
- Expect.equals('/false', request.path);
- Expect.isNull(request.certificate);
- }
-
- request.inputStream.onClosed = () {
- response.outputStream.close();
- };
- };
-
- server.listen(SERVER_ADDRESS,
- 0,
- backlog: 5,
- certificate_name: 'CN=$HOST_NAME',
- requestClientCertificate: true);
+Function test() {
+ var keepAlive = new ReceivePort();
+ HttpServer.bindSecure(SERVER_ADDRESS,
+ 0,
+ backlog: 5,
+ certificateName: 'localhost_cert',
+ requestClientCertificate: true).then((server) {
+ server.listen((HttpRequest request) {
+ Expect.isNotNull(request.certificate);
+ Expect.equals('CN=localhost', request.certificate.subject);
+ request.response.addString("Hello");
+ request.response.close();
+ });
HttpClient client = new HttpClient();
- Future testConnect(bool sendCertificate) {
- client.sendClientCertificate = sendCertificate;
- client.clientCertificate = options['certificateName'];
- var completer = new Completer();
- HttpClientConnection conn =
- client.getUrl(Uri.parse(
- "https://$HOST_NAME:${server.port}/$sendCertificate"));
- conn.onRequest = (HttpClientRequest request) {
- request.outputStream.close();
- };
- conn.onResponse = (HttpClientResponse response) {
- Expect.isNotNull(response.certificate);
- Expect.equals('CN=myauthority', response.certificate.issuer);
- response.inputStream.onClosed = () {
- completer.complete(false); // Chained call will not send cert.
- };
- };
- conn.onError = (Exception e) {
- Expect.fail("Unexpected error in Https Client: $e");
- };
- return completer.future;
- }
-
- testConnect(true).then(testConnect).then((_) {
- client.shutdown();
- server.close();
- Expect.throws(() => server.port);
- // Run second test with a certificate name.
- completer.complete(null);
- });
- return completer.future;
- }
- return runTest;
+ client.getUrl(Uri.parse("https://$HOST_NAME:${server.port}/"))
+ .then((request) => request.close())
+ .then((response) =>
+ response.reduce(<int>[], (message, data) => message..addAll(data)))
+ .then((message) {
+ String received = new String.fromCharCodes(message);
+ Expect.equals(received, "Hello");
+ client.close();
+ server.close();
+ keepAlive.close();
+ });
+ });
}
void InitializeSSL() {
@@ -85,13 +48,6 @@
}
void main() {
- var keepAlive = new ReceivePort();
InitializeSSL();
- // Test two connections in sequence.
- test({'certificateName': null})()
- .then((_) => test({'certificateName': 'localhost_cert'})())
- .then((_) {
- Expect.equals(2, numClientCertificatesReceived);
- keepAlive.close();
- });
+ test();
}
diff --git a/tests/standalone/io/https_client_socket_reuse_test.dart b/tests/standalone/io/https_client_socket_reuse_test.dart
new file mode 100644
index 0000000..5b4244b
--- /dev/null
+++ b/tests/standalone/io/https_client_socket_reuse_test.dart
@@ -0,0 +1,43 @@
+// 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.
+
+import "dart:async";
+import "dart:io";
+import "dart:uri";
+import "dart:isolate";
+
+// By running tests sequentially, we cover the socket reuse code in HttpClient.
+
+void testGoogleUrls() {
+ int testsStarted = 0;
+ int testsFinished = 0;
+ bool allStarted = false;
+ HttpClient client = new HttpClient();
+
+ Future testUrl(String url) {
+ testsStarted++;
+ var requestUri = Uri.parse(url);
+ return client.getUrl(requestUri)
+ .then((HttpClientRequest request) => request.close())
+ .then((HttpClientResponse response) {
+ Expect.isTrue(response.statusCode < 500);
+ if (requestUri.path.length == 0) {
+ Expect.isTrue(response.statusCode != 404);
+ }
+ return response.reduce(null, (previous, element) => null);
+ })
+ .catchError((error) => Expect.fail("Unexpected IO error: $error"));
+ }
+
+ // TODO(3593): Use a Dart HTTPS server for this test.
+ testUrl('https://www.google.dk')
+ .then((_) => testUrl('https://www.google.dk'))
+ .then((_) => testUrl('https://www.google.dk/#q=foo'))
+ .then((_) => testUrl('https://www.google.dk/#hl=da&q=foo'))
+ .then((_) { client.close(); });
+}
+
+void main() {
+ testGoogleUrls();
+}
diff --git a/tests/standalone/io/https_client_test.dart b/tests/standalone/io/https_client_test.dart
index 847249f..37b3d4f 100644
--- a/tests/standalone/io/https_client_test.dart
+++ b/tests/standalone/io/https_client_test.dart
@@ -7,50 +7,46 @@
import "dart:isolate";
-int testGoogleUrlCount = 0;
void testGoogleUrl() {
+ int testsStarted = 0;
+ int testsFinished = 0;
+ bool allStarted = false;
HttpClient client = new HttpClient();
void testUrl(String url) {
+ testsStarted++;
var requestUri = Uri.parse(url);
- var conn = client.getUrl(requestUri);
-
- conn.onRequest = (HttpClientRequest request) {
- request.outputStream.close();
- };
- conn.onResponse = (HttpClientResponse response) {
- testGoogleUrlCount++;
- Expect.isTrue(response.statusCode < 500);
- if (requestUri.path.length == 0) {
- Expect.isTrue(response.statusCode != 404);
- }
- response.inputStream.onData = () {
- response.inputStream.read();
- };
- response.inputStream.onClosed = () {
- if (testGoogleUrlCount == 4) client.shutdown();
- };
- };
- conn.onError = (error) => Expect.fail("Unexpected IO error $error");
+ client.getUrl(requestUri)
+ .then((HttpClientRequest request) => request.close())
+ .then((HttpClientResponse response) {
+ Expect.isTrue(response.statusCode < 500);
+ if (requestUri.path.length == 0) {
+ Expect.isTrue(response.statusCode != 404);
+ }
+ response.listen((data) { }, onDone: () {
+ if (++testsFinished == testsStarted && allStarted) client.close();
+ });
+ })
+ .catchError((error) => Expect.fail("Unexpected IO error: $error"));
}
testUrl('https://www.google.dk');
testUrl('https://www.google.dk');
testUrl('https://www.google.dk/#q=foo');
testUrl('https://www.google.dk/#hl=da&q=foo');
+ allStarted = true;
}
void testBadHostName() {
HttpClient client = new HttpClient();
- HttpClientConnection connection = client.getUrl(
- Uri.parse("https://some.bad.host.name.7654321/"));
- connection.onRequest = (HttpClientRequest request) {
- Expect.fail("Should not open a request on bad hostname");
- };
ReceivePort port = new ReceivePort();
- connection.onError = (Exception error) {
- port.close(); // We expect onError to be called, due to bad host name.
- };
+ client.getUrl(Uri.parse("https://some.bad.host.name.7654321/"))
+ .then((HttpClientRequest request) {
+ Expect.fail("Should not open a request on bad hostname");
+ })
+ .catchError((error) {
+ port.close(); // Should throw an error on bad hostname.
+ });
}
void InitializeSSL() {
diff --git a/tests/standalone/io/https_server_test.dart b/tests/standalone/io/https_server_test.dart
index 1da8854..e4e86e8 100644
--- a/tests/standalone/io/https_server_test.dart
+++ b/tests/standalone/io/https_server_test.dart
@@ -11,52 +11,46 @@
void testListenOn() {
void test(void onDone()) {
- HttpsServer server = new HttpsServer();
- Expect.throws(() => server.port);
-
- ReceivePort serverPort = new ReceivePort();
- server.defaultRequestHandler =
- (HttpRequest request, HttpResponse response) {
- request.inputStream.onClosed = () {
- response.outputStream.close();
+ HttpServer.bindSecure(SERVER_ADDRESS,
+ 0,
+ backlog: 5,
+ certificateName: 'localhost_cert').then((server) {
+ ReceivePort serverPort = new ReceivePort();
+ server.listen((HttpRequest request) {
+ request.listen(
+ (_) { },
+ onDone: () {
+ request.response.close();
serverPort.close();
- };
- };
+ });
+ });
- server.onError = (Exception e) {
- Expect.fail("Unexpected error in Https Server: $e");
- };
-
- server.listen(SERVER_ADDRESS,
- 0,
- backlog: 5,
- certificate_name: 'CN=$HOST_NAME');
-
- HttpClient client = new HttpClient();
- HttpClientConnection conn =
- client.getUrl(Uri.parse("https://$HOST_NAME:${server.port}/"));
- conn.onRequest = (HttpClientRequest request) {
- request.outputStream.close();
- };
- ReceivePort clientPort = new ReceivePort();
- conn.onResponse = (HttpClientResponse response) {
- response.inputStream.onClosed = () {
- client.shutdown();
- clientPort.close();
- server.close();
- Expect.throws(() => server.port);
- onDone();
- };
- };
- conn.onError = (Exception e) {
- Expect.fail("Unexpected error in Https Client: $e");
- };
- };
-
- // Test two connection after each other.
- test(() {
- test(() {
+ HttpClient client = new HttpClient();
+ ReceivePort clientPort = new ReceivePort();
+ client.getUrl(Uri.parse("https://$HOST_NAME:${server.port}/"))
+ .then((HttpClientRequest request) {
+ return request.close();
+ })
+ .then((HttpClientResponse response) {
+ response.listen(
+ (_) { },
+ onDone: () {
+ client.close();
+ clientPort.close();
+ server.close();
+ Expect.throws(() => server.port);
+ onDone();
+ });
+ })
+ .catchError((error) {
+ Expect.fail("Unexpected error in Https client: $error");
+ });
});
+ }
+
+ // Test two servers in succession.
+ test(() {
+ test(() { });
});
}
diff --git a/tests/standalone/io/list_input_stream_test.dart b/tests/standalone/io/list_input_stream_test.dart
deleted file mode 100644
index cebf50d..0000000
--- a/tests/standalone/io/list_input_stream_test.dart
+++ /dev/null
@@ -1,291 +0,0 @@
-// 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.
-
-import "dart:io";
-import "dart:isolate";
-
-void testEmptyListInputStream() {
- ListInputStream stream = new ListInputStream();
- stream.write([]);
- stream.markEndOfStream();
- ReceivePort donePort = new ReceivePort();
-
- void onData() {
- throw "No data expected";
- }
-
- void onClosed() {
- donePort.toSendPort().send(null);
- }
-
- stream.onData = onData;
- stream.onClosed = onClosed;
-
- donePort.receive((x,y) => donePort.close());
-}
-
-void testEmptyDynamicListInputStream() {
- ListInputStream stream = new ListInputStream();
- ReceivePort donePort = new ReceivePort();
-
- void onData() {
- throw "No data expected";
- }
-
- void onClosed() {
- donePort.toSendPort().send(null);
- }
-
- stream.onData = onData;
- stream.onClosed = onClosed;
- stream.markEndOfStream();
-
- donePort.receive((x,y) => donePort.close());
-}
-
-void testListInputStream1() {
- List<int> data = [0x00, 0x01, 0x10, 0x11, 0x7e, 0x7f, 0x80, 0x81, 0xfe, 0xff];
- ListInputStream stream = new ListInputStream();
- stream.write(data);
- stream.markEndOfStream();
- int count = 0;
- ReceivePort donePort = new ReceivePort();
-
- void onData() {
- List<int> x = stream.read(1);
- Expect.equals(1, x.length);
- Expect.equals(data[count++], x[0]);
- }
-
- void onClosed() {
- Expect.equals(data.length, count);
- donePort.toSendPort().send(count);
- }
-
- stream.onData = onData;
- stream.onClosed = onClosed;
-
- donePort.receive((x,y) => donePort.close());
-}
-
-void testListInputStream2() {
- List<int> data = [0x00, 0x01, 0x10, 0x11, 0x7e, 0x7f, 0x80, 0x81, 0xfe, 0xff];
- ListInputStream stream = new ListInputStream();
- stream.write(data);
- stream.markEndOfStream();
- int count = 0;
- ReceivePort donePort = new ReceivePort();
-
- void onData() {
- List<int> x = new List<int>.fixedLength(2);
- var bytesRead = stream.readInto(x);
- Expect.equals(2, bytesRead);
- Expect.equals(data[count++], x[0]);
- Expect.equals(data[count++], x[1]);
- }
-
- void onClosed() {
- Expect.equals(data.length, count);
- donePort.toSendPort().send(count);
- }
-
- stream.onData = onData;
- stream.onClosed = onClosed;
-
- donePort.receive((x,y) => donePort.close());
-}
-
-void testListInputStreamPipe1() {
- List<int> data = [0x00, 0x01, 0x10, 0x11, 0x7e, 0x7f, 0x80, 0x81, 0xfe, 0xff];
- ListInputStream input = new ListInputStream();
- input.write(data);
- input.markEndOfStream();
- ListOutputStream output = new ListOutputStream();
- ReceivePort donePort = new ReceivePort();
-
- void onClosed() {
- var contents = output.read();
- Expect.equals(data.length, contents.length);
- donePort.toSendPort().send(null);
- }
-
- input.onClosed = onClosed;
- input.pipe(output);
-
- donePort.receive((x,y) => donePort.close());
-}
-
-void testListInputStreamPipe2() {
- List<int> data = [0x00, 0x01, 0x10, 0x11, 0x7e, 0x7f, 0x80, 0x81, 0xfe, 0xff];
- ListOutputStream output = new ListOutputStream();
- ReceivePort donePort = new ReceivePort();
- int count = 0;
-
- void onClosed() {
- if (count < 10) {
- ListInputStream input = new ListInputStream();
- input.write(data);
- input.markEndOfStream();
- input.onClosed = onClosed;
- if (count < 9) {
- input.pipe(output, close: false);
- } else {
- input.pipe(output);
- }
- count++;
- } else {
- var contents = output.read();
- Expect.equals(data.length * 10, contents.length);
- donePort.toSendPort().send(null);
- }
- }
-
- ListInputStream input = new ListInputStream();
- input.write(data);
- input.markEndOfStream();
- input.onClosed = onClosed;
- input.pipe(output, close: false);
- count++;
-
- donePort.receive((x,y) => donePort.close());
-}
-
-void testListInputClose1() {
- List<int> data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
- ListInputStream stream = new ListInputStream();
- stream.write(data);
- stream.markEndOfStream();
- ReceivePort donePort = new ReceivePort();
-
- void onData() {
- throw "No data expected";
- }
-
- void onClosed() {
- donePort.toSendPort().send(null);
- }
-
- stream.onData = onData;
- stream.onClosed = onClosed;
- stream.close();
-
- donePort.receive((x,y) => donePort.close());
-}
-
-void testListInputClose2() {
- List<int> data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
- ListInputStream stream = new ListInputStream();
- stream.write(data);
- stream.markEndOfStream();
- ReceivePort donePort = new ReceivePort();
- int count = 0;
-
- void onData() {
- count += stream.read(2).length;
- stream.close();
- }
-
- void onClosed() {
- Expect.equals(2, count);
- donePort.toSendPort().send(count);
- }
-
- stream.onData = onData;
- stream.onClosed = onClosed;
-
- donePort.receive((x,y) => donePort.close());
-}
-
-void testDynamicListInputStream() {
- List<int> data = [0x00, 0x01, 0x10, 0x11, 0x7e, 0x7f, 0x80, 0x81, 0xfe, 0xff];
- ListInputStream stream = new ListInputStream();
- int count = 0;
- ReceivePort donePort = new ReceivePort();
-
- void onData() {
- List<int> x = stream.read(1);
- Expect.equals(1, x.length);
- x = stream.read();
- Expect.equals(9, x.length);
- count++;
- if (count < 10) {
- stream.write(data);
- } else {
- stream.markEndOfStream();
- }
- }
-
- void onClosed() {
- Expect.equals(data.length, count);
- donePort.toSendPort().send(count);
- }
-
- stream.write(data);
- stream.onData = onData;
- stream.onClosed = onClosed;
-
- donePort.receive((x,y) => donePort.close());
-}
-
-void testDynamicListInputClose1() {
- List<int> data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
- ListInputStream stream = new ListInputStream();
- ReceivePort donePort = new ReceivePort();
-
- void onData() {
- throw "No data expected";
- }
-
- void onClosed() {
- donePort.toSendPort().send(null);
- }
-
- stream.write(data);
- stream.onData = onData;
- stream.onClosed = onClosed;
- stream.close();
- Expect.throws(() => stream.write(data), (e) => e is StreamException);
-
- donePort.receive((x,y) => donePort.close());
-}
-
-void testDynamicListInputClose2() {
- List<int> data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
- ListInputStream stream = new ListInputStream();
- ReceivePort donePort = new ReceivePort();
- int count = 0;
-
- void onData() {
- count += stream.read(15).length;
- stream.close();
- Expect.throws(() => stream.write(data), (e) => e is StreamException);
- }
-
- void onClosed() {
- Expect.equals(15, count);
- donePort.toSendPort().send(null);
- }
-
- stream.write(data);
- stream.write(data);
- stream.write(data);
- stream.onData = onData;
- stream.onClosed = onClosed;
-
- donePort.receive((x,y) => donePort.close());
-}
-
-main() {
- testEmptyListInputStream();
- testEmptyDynamicListInputStream();
- testListInputStream1();
- testListInputStream2();
- testListInputStreamPipe1();
- testListInputStreamPipe2();
- testListInputClose1();
- testListInputClose2();
- testDynamicListInputStream();
- testDynamicListInputClose1();
- testDynamicListInputClose2();
-}
diff --git a/tests/standalone/io/list_output_stream_test.dart b/tests/standalone/io/list_output_stream_test.dart
deleted file mode 100644
index ad3b029..0000000
--- a/tests/standalone/io/list_output_stream_test.dart
+++ /dev/null
@@ -1,173 +0,0 @@
-// 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.
-
-import 'dart:async';
-import 'dart:io';
-import 'dart:isolate';
-
-void testEmptyListOutputStream1() {
- ListOutputStream stream = new ListOutputStream();
- Expect.equals(null, stream.read());
- stream.close();
- Expect.equals(null, stream.read());
- Expect.throws(() { stream.write([0]); });
-}
-
-
-void testEmptyListOutputStream2() {
- ListOutputStream stream = new ListOutputStream();
- ReceivePort donePort = new ReceivePort();
-
- void onNoPendingWrites() {
- stream.close();
- }
-
- void onClosed() {
- Expect.equals(null, stream.read());
- donePort.toSendPort().send(null);
- }
-
- stream.onNoPendingWrites = onNoPendingWrites;
- stream.onClosed = onClosed;
-
- donePort.receive((x,y) => donePort.close());
-}
-
-
-void testListOutputStream1() {
- ListOutputStream stream = new ListOutputStream();
- Expect.equals(null, stream.read());
- stream.write([1, 2]);
- stream.writeFrom([1, 2, 3, 4, 5], 2, 2);
- stream.write([5]);
- stream.close();
- var contents = stream.read();
- Expect.equals(5, contents.length);
- for (var i = 0; i < contents.length; i++) Expect.equals(i + 1, contents[i]);
- Expect.equals(null, stream.read());
-}
-
-
-void testListOutputStream2() {
- ListOutputStream stream = new ListOutputStream();
- ReceivePort donePort = new ReceivePort();
- int stage = 0;
- void onNoPendingWrites() {
- switch (stage) {
- case 0:
- stream.write([1, 2]);
- break;
- case 1:
- stream.writeFrom([1, 2, 3, 4, 5], 2, 2);
- break;
- case 2:
- stream.write([5]);
- break;
- case 3:
- stream.close();
- break;
- }
- stage++;
- }
-
- void onClosed() {
- Expect.equals(4, stage);
- var contents = stream.read();
- Expect.equals(5, contents.length);
- for (var i = 0; i < contents.length; i++) Expect.equals(i + 1, contents[i]);
- Expect.equals(null, stream.read());
- donePort.toSendPort().send(null);
- }
-
- stream.onNoPendingWrites = onNoPendingWrites;
- stream.onClosed = onClosed;
-
- donePort.receive((x,y) => donePort.close());
-}
-
-void testListOutputStream3() {
- ListOutputStream stream = new ListOutputStream();
- ReceivePort donePort = new ReceivePort();
- void onNoPendingWrites() {
- stream.writeString("abcdABCD");
- stream.writeString("abcdABCD", Encoding.UTF_8);
- stream.writeString("abcdABCD", Encoding.ISO_8859_1);
- stream.writeString("abcdABCD", Encoding.ASCII);
- stream.writeString("æøå", Encoding.UTF_8);
- stream.close();
- }
-
- void onClosed() {
- var contents = stream.read();
- Expect.equals(38, contents.length);
- donePort.toSendPort().send(null);
- }
-
- stream.onNoPendingWrites = onNoPendingWrites;
- stream.onClosed = onClosed;
-
- donePort.receive((x,y) => donePort.close());
-}
-
-void testListOutputStream4() {
- ListOutputStream stream = new ListOutputStream();
- ReceivePort donePort = new ReceivePort();
- List result = <int>[];
-
- void onData() => result.addAll(stream.read());
-
- void onClosed() {
- Expect.equals(4, result.length);
- for (var i = 0; i < result.length; i++) Expect.equals(i + 1, result[i]);
- donePort.toSendPort().send(null);
- }
-
- stream.onData = onData;
- stream.onClosed = onClosed;
-
- Timer.run(() {
- result.add(1);
- stream.write([2]);
-
- Timer.run(() {
- result.add(3);
- stream.write([4]);
- stream.close();
- });
- });
-
- donePort.receive((x,y) => donePort.close());
-}
-
-void testListOutputStream5() {
- ListOutputStream stream = new ListOutputStream();
- ReceivePort donePort = new ReceivePort();
-
- stream.onClosed = () {
- Expect.isTrue(stream.closed);
- var contents = stream.read();
- Expect.equals(3, contents.length);
- for (var i = 0; i < contents.length; i++) Expect.equals(i + 1, contents[i]);
- donePort.toSendPort().send(null);
- };
-
- Expect.isFalse(stream.closed);
- stream.write([1, 2, 3]);
- Expect.isFalse(stream.closed);
- stream.close();
- Expect.isTrue(stream.closed);
- Expect.throws(() => stream.write([4, 5, 6]));
-
- donePort.receive((x,y) => donePort.close());
-}
-
-main() {
- testEmptyListOutputStream1();
- testEmptyListOutputStream2();
- testListOutputStream1();
- testListOutputStream2();
- testListOutputStream3();
- testListOutputStream4();
- testListOutputStream5();
-}
diff --git a/tests/standalone/io/mime_multipart_parser_test.dart b/tests/standalone/io/mime_multipart_parser_test.dart
index 122e075..adccecb 100644
--- a/tests/standalone/io/mime_multipart_parser_test.dart
+++ b/tests/standalone/io/mime_multipart_parser_test.dart
@@ -1,11 +1,16 @@
-// Copyright (c) 2012, 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.
+import 'dart:async';
import 'dart:math';
+part '../../../sdk/lib/io/io_stream_consumer.dart';
+part "../../../sdk/lib/io/http.dart";
+part "../../../sdk/lib/io/http_impl.dart";
part "../../../sdk/lib/io/http_parser.dart";
part "../../../sdk/lib/io/mime_multipart_parser.dart";
+part '../../../sdk/lib/io/socket.dart';
void testParse(String message,
String boundary,
diff --git a/tests/standalone/io/pipe_server_test.dart b/tests/standalone/io/pipe_server_test.dart
new file mode 100644
index 0000000..2e7c0ea
--- /dev/null
+++ b/tests/standalone/io/pipe_server_test.dart
@@ -0,0 +1,123 @@
+// 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.
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
+
+library ServerTest;
+
+import "dart:async";
+import "dart:io";
+import "dart:isolate";
+part "testing_server.dart";
+
+
+String getDataFilename(String path) =>
+ new File(path).existsSync() ? path : '../$path';
+
+
+bool compareFileContent(String fileName1, String fileName2) {
+ var contents1 = new File(fileName1).readAsStringSync();
+ var contents2 = new File(fileName2).readAsStringSync();
+ return contents1 == contents2;
+}
+
+
+// This test does:
+// 1. Opens a socket to the testing server.
+// 2. Pipes the content of a file to that sockets input stream.
+// 3. Creates a temp file.
+// 4. Pipes the socket output stream to the temp file.
+// 5. Expects the original file and the temp file to be equal.
+class PipeServerGame {
+
+ int count = 0;
+
+ PipeServerGame.start()
+ : _receivePort = new ReceivePort(),
+ _sendPort = null,
+ _messages = 0 {
+ _sendPort = spawnFunction(startPipeServer);
+ initialize();
+ }
+
+ void runTest() {
+
+ void connectHandler() {
+ String srcFileName =
+ getDataFilename("tests/standalone/io/readline_test1.dat");
+ Stream fileInput = new File(srcFileName).openRead();
+
+ fileInput.pipe(_socket).then((_) {
+ var tempDir = new Directory('').createTempSync();
+ var dstFileName = tempDir.path.concat("/readline_test1.dat");
+ var dstFile = new File(dstFileName);
+ dstFile.createSync();
+ var fileOutput = dstFile.openWrite();
+ _socket.pipe(fileOutput).then((_) {
+ // Check that the resulting file is equal to the initial
+ // file.
+ bool result = compareFileContent(srcFileName, dstFileName);
+ new File(dstFileName).deleteSync();
+ tempDir.deleteSync();
+ Expect.isTrue(result);
+
+ // Run this twice.
+ if (count++ < 2) {
+ runTest();
+ } else {
+ shutdown();
+ }
+ });
+ });
+ }
+
+ // Connect to the server.
+ Socket.connect(TestingServer.HOST, _port).then((s) {
+ _socket = s;
+ connectHandler();
+ });
+ }
+
+ void initialize() {
+ _receivePort.receive((var message, SendPort replyTo) {
+ _port = message;
+ runTest();
+ });
+ _sendPort.send(TestingServer.INIT, _receivePort.toSendPort());
+ }
+
+ void shutdown() {
+ _sendPort.send(TestingServer.SHUTDOWN, _receivePort.toSendPort());
+ _receivePort.close();
+ }
+
+ int _port;
+ ReceivePort _receivePort;
+ SendPort _sendPort;
+ Socket _socket;
+ int _messages;
+}
+
+
+void startPipeServer() {
+ var server = new PipeServer();
+ port.receive(server.dispatch);
+}
+
+
+// The testing server will simply pipe each connecting sockets input
+// stream to its output stream.
+class PipeServer extends TestingServer {
+ void onConnection(Socket connection) {
+ connection.pipe(connection);
+ }
+}
+
+
+main() {
+ PipeServerGame echoServerGame = new PipeServerGame.start();
+}
diff --git a/tests/standalone/io/process_broken_pipe_test.dart b/tests/standalone/io/process_broken_pipe_test.dart
index 1f42789..3833158 100644
--- a/tests/standalone/io/process_broken_pipe_test.dart
+++ b/tests/standalone/io/process_broken_pipe_test.dart
@@ -5,6 +5,7 @@
// Process test program to test closed stdin from child process.
import "dart:io";
+import "dart:isolate";
import "process_test_util.dart";
@@ -12,17 +13,18 @@
// Running dart without arguments makes it close right away.
var future = Process.start(new Options().executable, []);
future.then((process) {
- // Ignore error on stdin.
- process.stdin.onError = (e) => null;
+ process.stdin.done.catchError((e) {
+ // Accept errors on stdin.
+ });
// Drain stdout and stderr.
- process.stdout.onData = () => process.stdout.read();
- process.stderr.onData = () => process.stderr.read();
+ process.stdout.listen((_) {});
+ process.stderr.listen((_) {});
// Write to the stdin after the process is terminated to test
// writing to a broken pipe.
- process.onExit = (code) {
- Expect.isFalse(process.stdin.write([0]));
- };
+ process.exitCode.then((code) {
+ process.stdin.add([0]);
+ });
});
}
diff --git a/tests/standalone/io/process_check_arguments_test.dart b/tests/standalone/io/process_check_arguments_test.dart
index 23ee444..46b1cb4 100644
--- a/tests/standalone/io/process_check_arguments_test.dart
+++ b/tests/standalone/io/process_check_arguments_test.dart
@@ -8,12 +8,12 @@
test(args) {
var future = Process.start(new Options().executable, args);
future.then((process) {
- process.onExit = (exitCode) {
+ process.exitCode.then((exitCode) {
Expect.equals(0, exitCode);
- };
+ });
// Drain stdout and stderr.
- process.stdout.onData = process.stdout.read;
- process.stderr.onData = process.stderr.read;
+ process.stdout.listen((_) {});
+ process.stderr.listen((_) {});
});
}
diff --git a/tests/standalone/io/process_exit_test.dart b/tests/standalone/io/process_exit_test.dart
index f4f9100..060bb14 100644
--- a/tests/standalone/io/process_exit_test.dart
+++ b/tests/standalone/io/process_exit_test.dart
@@ -12,11 +12,11 @@
var future = Process.start(getProcessTestFileName(),
const ["0", "0", "99", "0"]);
future.then((process) {
- process.onExit = (int exitCode) {
+ process.exitCode.then((int exitCode) {
Expect.equals(exitCode, 99);
- };
- process.stdout.onData = process.stdout.read;
- process.stderr.onData = process.stderr.read;
+ });
+ process.stdout.listen((_) {});
+ process.stderr.listen((_) {});
});
}
diff --git a/tests/standalone/io/process_kill_test.dart b/tests/standalone/io/process_kill_test.dart
index 8571952..733724f 100644
--- a/tests/standalone/io/process_kill_test.dart
+++ b/tests/standalone/io/process_kill_test.dart
@@ -11,12 +11,12 @@
testKill() {
// Start a process that will hang waiting for input until killed.
Process.start(getProcessTestFileName(), const ["0", "1", "0", "0"]).then((p) {
- p.onExit = (exitCode) {
+ p.exitCode.then((exitCode) {
// Process killed from the side so exit code is not 0.
Expect.isTrue(exitCode != 0);
// Killing a process that is already dead returns false.
Expect.isFalse(p.kill());
- };
+ });
Expect.isTrue(p.kill());
});
}
diff --git a/tests/standalone/io/process_segfault_test.dart b/tests/standalone/io/process_segfault_test.dart
index 10d7572..5c278d2 100644
--- a/tests/standalone/io/process_segfault_test.dart
+++ b/tests/standalone/io/process_segfault_test.dart
@@ -12,11 +12,11 @@
var future = Process.start(getProcessTestFileName(),
const ["0", "0", "1", "1"]);
future.then((process) {
- process.onExit = (int exitCode) {
+ process.exitCode.then((int exitCode) {
Expect.isTrue(exitCode != 0);
- };
- process.stdout.onData = process.stdout.read;
- process.stderr.onData = process.stderr.read;
+ });
+ process.stdout.listen((_) {});
+ process.stderr.listen((_) {});
});
}
diff --git a/tests/standalone/io/process_set_exit_code_script.dart b/tests/standalone/io/process_set_exit_code_script.dart
index 6a78429..f247963 100644
--- a/tests/standalone/io/process_set_exit_code_script.dart
+++ b/tests/standalone/io/process_set_exit_code_script.dart
@@ -5,7 +5,7 @@
import "dart:io";
main() {
- stdout.writeString("standard out");
- stderr.writeString("standard error");
+ stdout.addString("standard out");
+ stderr.addString("standard error");
exitCode = 25;
-}
\ No newline at end of file
+}
diff --git a/tests/standalone/io/process_std_io_script.dart b/tests/standalone/io/process_std_io_script.dart
index bc8b240..06ee29a 100644
--- a/tests/standalone/io/process_std_io_script.dart
+++ b/tests/standalone/io/process_std_io_script.dart
@@ -10,15 +10,14 @@
var options = new Options();
if (options.arguments.length > 0) {
if (options.arguments[0] == "0") {
- stdin.onData = () => stdout.write(stdin.read());
+ stdin.pipe(stdout);
} else if (options.arguments[0] == "1") {
- stdin.onData = () => stderr.write(stdin.read());
+ stdin.pipe(stderr);
} else if (options.arguments[0] == "2") {
- stdin.onData = () {
- var data = stdin.read();
- stdout.write(data);
- stderr.write(data);
- };
+ stdin.listen((data) {
+ stdout.add(data);
+ stderr.add(data);
+ });
}
}
}
diff --git a/tests/standalone/io/process_std_io_script2.dart b/tests/standalone/io/process_std_io_script2.dart
index 96d9739..5c0501f 100644
--- a/tests/standalone/io/process_std_io_script2.dart
+++ b/tests/standalone/io/process_std_io_script2.dart
@@ -9,9 +9,9 @@
writeData(data, encoding, stream) {
if (stream == "stdout") {
- stdout.writeString(data, encoding);
+ stdout.addString(data, encoding);
} else if (stream == "stderr") {
- stderr.writeString(data, encoding);
+ stderr.addString(data, encoding);
}
}
diff --git a/tests/standalone/io/process_stderr_test.dart b/tests/standalone/io/process_stderr_test.dart
index b84d81c..858bb9a 100644
--- a/tests/standalone/io/process_stderr_test.dart
+++ b/tests/standalone/io/process_stderr_test.dart
@@ -17,20 +17,18 @@
void test(Future<Process> future, int expectedExitCode) {
future.then((process) {
- process.onExit = (exitCode) {
+ process.exitCode.then((exitCode) {
Expect.equals(expectedExitCode, exitCode);
- };
+ });
List<int> data = "ABCDEFGHI\n".charCodes;
final int dataSize = data.length;
- InputStream err = process.stderr;
-
int received = 0;
List<int> buffer = [];
- void readData() {
- buffer.addAll(err.read());
+ void readData(data) {
+ buffer.addAll(data);
for (int i = received;
i < min(data.length, buffer.length) - 1;
i++) {
@@ -47,10 +45,10 @@
}
}
- process.stdout.onData = process.stdout.read;
- process.stdin.write(data);
+ process.stdout.listen((_) {});
+ process.stdin.add(data);
process.stdin.close();
- err.onData = readData;
+ process.stderr.listen(readData);
});
}
diff --git a/tests/standalone/io/process_stdout_test.dart b/tests/standalone/io/process_stdout_test.dart
index 200faf0..698ab57 100644
--- a/tests/standalone/io/process_stdout_test.dart
+++ b/tests/standalone/io/process_stdout_test.dart
@@ -17,20 +17,18 @@
void test(Future<Process> future, int expectedExitCode) {
future.then((process) {
- process.onExit = (exitCode) {
+ process.exitCode.then((exitCode) {
Expect.equals(expectedExitCode, exitCode);
- };
+ });
List<int> data = "ABCDEFGHI\n".charCodes;
final int dataSize = data.length;
- InputStream out = process.stdout;
-
int received = 0;
List<int> buffer = [];
- void readData() {
- buffer.addAll(out.read());
+ void readData(data) {
+ buffer.addAll(data);
for (int i = received;
i < min(data.length, buffer.length) - 1;
i++) {
@@ -47,10 +45,10 @@
}
}
- process.stdin.write(data);
+ process.stderr.listen((_) {});
+ process.stdin.add(data);
process.stdin.close();
- out.onData = readData;
- process.stderr.onData = process.stderr.read;
+ process.stdout.listen(readData);
});
}
diff --git a/tests/standalone/io/process_working_directory_test.dart b/tests/standalone/io/process_working_directory_test.dart
index 3240cb9..1529a13 100644
--- a/tests/standalone/io/process_working_directory_test.dart
+++ b/tests/standalone/io/process_working_directory_test.dart
@@ -25,12 +25,12 @@
var processFuture =
Process.start(fullTestFilePath, const ["0", "0", "99", "0"], options);
processFuture.then((process) {
- process.onExit = (int exitCode) {
+ process.exitCode.then((int exitCode) {
Expect.equals(exitCode, 99);
directory.deleteSync();
- };
- process.stdout.onData = process.stdout.read;
- process.stderr.onData = process.stderr.read;
+ });
+ process.stdout.listen((_) {});
+ process.stderr.listen((_) {});
}).catchError((error) {
directory.deleteSync();
Expect.fail("Couldn't start process");
diff --git a/tests/standalone/io/raw_secure_server_closing_test.dart b/tests/standalone/io/raw_secure_server_closing_test.dart
new file mode 100644
index 0000000..1f8116d4
--- /dev/null
+++ b/tests/standalone/io/raw_secure_server_closing_test.dart
@@ -0,0 +1,159 @@
+// 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.
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
+
+import "dart:async";
+import "dart:io";
+import "dart:isolate";
+
+const SERVER_ADDRESS = "127.0.0.1";
+const HOST_NAME = "localhost";
+const CERTIFICATE = "localhost_cert";
+
+void testCloseOneEnd(String toClose) {
+ ReceivePort port = new ReceivePort();
+ Completer serverDone = new Completer();
+ Completer serverEndDone = new Completer();
+ Completer clientEndDone = new Completer();
+ Future.wait([serverDone.future, serverEndDone.future, clientEndDone.future])
+ .then((_) {
+ port.close();
+ });
+ RawSecureServerSocket.bind(SERVER_ADDRESS, 0, 5, CERTIFICATE).then((server) {
+ server.listen((serverConnection) {
+ serverConnection.listen((event) {
+ if (toClose == "server" || event == RawSocketEvent.READ_CLOSED) {
+ serverConnection.shutdown(SocketDirection.SEND);
+ }
+ },
+ onDone: () {
+ serverEndDone.complete(null);
+ });
+ },
+ onDone: () {
+ serverDone.complete(null);
+ });
+ RawSecureSocket.connect(HOST_NAME, server.port).then((clientConnection) {
+ clientConnection.listen((event){
+ if (toClose == "client" || event == RawSocketEvent.READ_CLOSED) {
+ clientConnection.shutdown(SocketDirection.SEND);
+ }
+ },
+ onDone: () {
+ clientEndDone.complete(null);
+ server.close();
+ });
+ });
+ });
+}
+
+void testCloseBothEnds() {
+ ReceivePort port = new ReceivePort();
+ RawSecureServerSocket.bind(SERVER_ADDRESS, 0, 5, CERTIFICATE).then((server) {
+ var clientEndFuture = RawSecureSocket.connect(HOST_NAME, server.port);
+ server.listen((serverEnd) {
+ clientEndFuture.then((clientEnd) {
+ clientEnd.close();
+ serverEnd.close();
+ server.close();
+ port.close();
+ });
+ });
+ });
+}
+
+testPauseServerSocket() {
+ const int socketCount = 10;
+ var acceptCount = 0;
+ var resumed = false;
+
+ ReceivePort port = new ReceivePort();
+
+ RawSecureServerSocket.bind(SERVER_ADDRESS,
+ 0,
+ 2 * socketCount,
+ CERTIFICATE).then((server) {
+ Expect.isTrue(server.port > 0);
+ var subscription;
+ subscription = server.listen((connection) {
+ Expect.isTrue(resumed);
+ connection.shutdown(SocketDirection.SEND);
+ if (++acceptCount == 2 * socketCount) {
+ server.close();
+ port.close();
+ }
+ });
+
+ // Pause the server socket subscription and resume it after having
+ // connected a number client sockets. Then connect more client
+ // sockets.
+ subscription.pause();
+ var connectCount = 0;
+ for (int i = 0; i < socketCount; i++) {
+ RawSecureSocket.connect(HOST_NAME, server.port).then((connection) {
+ connection.shutdown(SocketDirection.SEND);
+ });
+ }
+ new Timer(500, (_) {
+ subscription.resume();
+ resumed = true;
+ for (int i = 0; i < socketCount; i++) {
+ RawSecureSocket.connect(HOST_NAME, server.port).then((connection) {
+ connection.shutdown(SocketDirection.SEND);
+ });
+ }
+ });
+ });
+}
+
+testCloseServer() {
+ const int socketCount = 3;
+ ReceivePort port = new ReceivePort();
+ List ends = [];
+
+ RawSecureServerSocket.bind(SERVER_ADDRESS, 0, 15, CERTIFICATE).then((server) {
+ Expect.isTrue(server.port > 0);
+ void checkDone() {
+ if (ends.length < 2 * socketCount) return;
+ for (var end in ends) {
+ end.close();
+ }
+ server.close();
+ port.close();
+ }
+
+ server.listen((connection) {
+ ends.add(connection);
+ checkDone();
+ });
+
+ for (int i = 0; i < socketCount; i++) {
+ RawSecureSocket.connect(HOST_NAME, server.port).then((connection) {
+ ends.add(connection);
+ checkDone();
+ });
+ }
+ });
+}
+
+
+main() {
+ Path scriptDir = new Path(new Options().script).directoryPath;
+ Path certificateDatabase = scriptDir.append('pkcert');
+ SecureSocket.initialize(database: certificateDatabase.toNativePath(),
+ password: 'dartdart',
+ useBuiltinRoots: false);
+
+ testCloseOneEnd("client");
+ testCloseOneEnd("server");
+ testCloseBothEnds();
+ testCloseServer();
+ testPauseServerSocket();
+ // TODO(whesse): Add testPauseSocket from raw_socket_test.dart.
+ // TODO(whesse): Add testCancelResubscribeSocket from raw_socket_test.dart.
+}
diff --git a/tests/standalone/io/raw_secure_server_socket_test.dart b/tests/standalone/io/raw_secure_server_socket_test.dart
new file mode 100644
index 0000000..73609ae
--- /dev/null
+++ b/tests/standalone/io/raw_secure_server_socket_test.dart
@@ -0,0 +1,262 @@
+// 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.
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
+
+import "dart:async";
+import "dart:io";
+import "dart:isolate";
+
+const SERVER_ADDRESS = "127.0.0.1";
+const HOST_NAME = "localhost";
+const CERTIFICATE = "localhost_cert";
+
+void testArguments() {
+ Expect.throws(() =>
+ RawSecureServerSocket.bind(SERVER_ADDRESS, 65536, 5, CERTIFICATE));
+ Expect.throws(() =>
+ RawSecureServerSocket.bind(SERVER_ADDRESS, -1, CERTIFICATE));
+ Expect.throws(() =>
+ RawSecureServerSocket.bind(SERVER_ADDRESS, 0, -1, CERTIFICATE));
+}
+
+void testSimpleBind() {
+ ReceivePort port = new ReceivePort();
+ RawSecureServerSocket.bind(SERVER_ADDRESS, 0, 5, CERTIFICATE).then((s) {
+ Expect.isTrue(s.port > 0);
+ s.close();
+ port.close();
+ });
+}
+
+void testInvalidBind() {
+ int count = 0;
+ ReceivePort port = new ReceivePort();
+ port.receive((_, __) { count++; if (count == 3) port.close(); });
+
+ // Bind to a unknown DNS name.
+ RawSecureServerSocket.bind("ko.faar.__hest__", 0, 5, CERTIFICATE).then((_) {
+ Expect.fail("Failure expected");
+ }).catchError((e) {
+ Expect.isTrue(e.error is SocketIOException);
+ port.toSendPort().send(1);
+ });
+
+ // Bind to an unavaliable IP-address.
+ RawSecureServerSocket.bind("8.8.8.8", 0, 5, CERTIFICATE).then((_) {
+ Expect.fail("Failure expected");
+ }).catchError((e) {
+ Expect.isTrue(e.error is SocketIOException);
+ port.toSendPort().send(1);
+ });
+
+ // Bind to a port already in use.
+ // Either an error or a successful bind is allowed.
+ // Windows platforms allow multiple binding to the same socket, with
+ // unpredictable results.
+ RawSecureServerSocket.bind(SERVER_ADDRESS, 0, 5, CERTIFICATE).then((s) {
+ RawSecureServerSocket.bind(SERVER_ADDRESS,
+ s.port,
+ 5,
+ CERTIFICATE).then((t) {
+ Expect.equals('windows', Platform.operatingSystem);
+ Expect.equals(s.port, t.port);
+ s.close();
+ t.close();
+ port.toSendPort().send(1);
+ })
+ .catchError((e) {
+ Expect.notEquals('windows', Platform.operatingSystem);
+ Expect.isTrue(e.error is SocketIOException);
+ s.close();
+ port.toSendPort().send(1);
+ });
+ });
+}
+
+void testSimpleConnect(String certificate) {
+ ReceivePort port = new ReceivePort();
+ RawSecureServerSocket.bind(SERVER_ADDRESS, 0, 5, certificate).then((server) {
+ var clientEndFuture = RawSecureSocket.connect(HOST_NAME, server.port);
+ server.listen((serverEnd) {
+ clientEndFuture.then((clientEnd) {
+ clientEnd.shutdown(SocketDirection.SEND);
+ serverEnd.shutdown(SocketDirection.SEND);
+ server.close();
+ port.close();
+ });
+ });
+ });
+}
+
+void testSimpleConnectFail(String certificate) {
+ ReceivePort port = new ReceivePort();
+ RawSecureServerSocket.bind(SERVER_ADDRESS, 0, 5, certificate).then((server) {
+ var clientEndFuture = RawSecureSocket.connect(HOST_NAME, server.port)
+ .then((clientEnd) {
+ Expect.fail("No client connection expected.");
+ })
+ .catchError((e) {
+ Expect.isTrue(e is AsyncError);
+ Expect.isTrue(e.error is SocketIOException);
+ });
+ server.listen((serverEnd) {
+ Expect.fail("No server connection expected.");
+ },
+ onError: (e) {
+ Expect.isTrue(e is AsyncError);
+ Expect.isTrue(e.error is SocketIOException);
+ clientEndFuture.then((_) => port.close());
+ });
+ });
+}
+
+void testServerListenAfterConnect() {
+ ReceivePort port = new ReceivePort();
+ RawSecureServerSocket.bind(SERVER_ADDRESS, 0, 5, CERTIFICATE).then((server) {
+ Expect.isTrue(server.port > 0);
+ var clientEndFuture = RawSecureSocket.connect(HOST_NAME, server.port);
+ new Timer(500, (_) {
+ server.listen((serverEnd) {
+ clientEndFuture.then((clientEnd) {
+ clientEnd.shutdown(SocketDirection.SEND);
+ serverEnd.shutdown(SocketDirection.SEND);
+ server.close();
+ port.close();
+ });
+ });
+ });
+ });
+}
+
+void testSimpleReadWrite() {
+ // This test creates a server and a client connects. The client then
+ // writes and the server echos. When the server has finished its
+ // echo it half-closes. When the client gets the close event is
+ // closes fully.
+ ReceivePort port = new ReceivePort();
+
+ const messageSize = 1000;
+
+ List<int> createTestData() {
+ List<int> data = new List.fixedLength(messageSize);
+ for (int i = 0; i < messageSize; i++) {
+ data[i] = i & 0xff;
+ }
+ return data;
+ }
+
+ void verifyTestData(List<int> data) {
+ Expect.equals(messageSize, data.length);
+ List<int> expected = createTestData();
+ for (int i = 0; i < messageSize; i++) {
+ Expect.equals(expected[i], data[i]);
+ }
+ }
+
+ RawSecureServerSocket.bind(SERVER_ADDRESS, 0, 5, CERTIFICATE).then((server) {
+ server.listen((client) {
+ int bytesRead = 0;
+ int bytesWritten = 0;
+ List<int> data = new List.fixedLength(messageSize);
+
+ client.writeEventsEnabled = false;
+ client.listen((event) {
+ switch (event) {
+ case RawSocketEvent.READ:
+ Expect.isTrue(bytesWritten == 0);
+ Expect.isTrue(client.available() > 0);
+ var buffer = client.read();
+ if (buffer != null) {
+ data.setRange(bytesRead, buffer.length, buffer);
+ bytesRead += buffer.length;
+ for (var value in buffer) {
+ Expect.isTrue(value is int);
+ Expect.isTrue(value < 256 && value >= 0);
+ }
+ }
+ if (bytesRead == data.length) {
+ verifyTestData(data);
+ client.writeEventsEnabled = true;
+ }
+ break;
+ case RawSocketEvent.WRITE:
+ Expect.isFalse(client.writeEventsEnabled);
+ Expect.equals(bytesRead, data.length);
+ for (int i = bytesWritten; i < data.length; ++i) {
+ Expect.isTrue(data[i] is int);
+ Expect.isTrue(data[i] < 256 && data[i] >= 0);
+ }
+ bytesWritten += client.write(
+ data, bytesWritten, data.length - bytesWritten);
+ if (bytesWritten < data.length) {
+ client.writeEventsEnabled = true;
+ }
+ if (bytesWritten == data.length) {
+ client.shutdown(SocketDirection.SEND);
+ }
+ break;
+ case RawSocketEvent.READ_CLOSED:
+ server.close();
+ break;
+ default: throw "Unexpected event $event";
+ }
+ });
+ });
+
+ RawSecureSocket.connect(HOST_NAME, server.port).then((socket) {
+ int bytesRead = 0;
+ int bytesWritten = 0;
+ List<int> dataSent = createTestData();
+ List<int> dataReceived = new List<int>.fixedLength(dataSent.length);
+ socket.listen((event) {
+ switch (event) {
+ case RawSocketEvent.READ:
+ Expect.isTrue(socket.available() > 0);
+ var buffer = socket.read();
+ if (buffer != null) {
+ dataReceived.setRange(bytesRead, buffer.length, buffer);
+ bytesRead += buffer.length;
+ }
+ break;
+ case RawSocketEvent.WRITE:
+ Expect.isTrue(bytesRead == 0);
+ Expect.isFalse(socket.writeEventsEnabled);
+ bytesWritten += socket.write(
+ dataSent, bytesWritten, dataSent.length - bytesWritten);
+ if (bytesWritten < dataSent.length) {
+ socket.writeEventsEnabled = true;
+ }
+ break;
+ case RawSocketEvent.READ_CLOSED:
+ verifyTestData(dataReceived);
+ socket.close();
+ port.close();
+ break;
+ default: throw "Unexpected event $event";
+ }
+ });
+ });
+ });
+}
+
+main() {
+ Path scriptDir = new Path(new Options().script).directoryPath;
+ Path certificateDatabase = scriptDir.append('pkcert');
+ SecureSocket.initialize(database: certificateDatabase.toNativePath(),
+ password: 'dartdart',
+ useBuiltinRoots: false);
+ testArguments();
+ testSimpleBind();
+ testInvalidBind();
+ testSimpleConnect(CERTIFICATE);
+ testSimpleConnect("CN=localhost");
+ testSimpleConnectFail("not_a_nickname");
+ testSimpleConnectFail("CN=notARealDistinguishedName");
+ testServerListenAfterConnect();
+ testSimpleReadWrite();
+}
diff --git a/tests/standalone/io/raw_secure_socket_pause_test.dart b/tests/standalone/io/raw_secure_socket_pause_test.dart
new file mode 100644
index 0000000..23b2eff
--- /dev/null
+++ b/tests/standalone/io/raw_secure_socket_pause_test.dart
@@ -0,0 +1,81 @@
+// 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.
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
+
+import "dart:async";
+import "dart:io";
+import "dart:isolate";
+
+
+void main() {
+ List<int> message = "GET / HTTP/1.0\r\nHost: www.google.dk\r\n\r\n".charCodes;
+ int written = 0;
+ List<String> chunks = <String>[];
+ SecureSocket.initialize();
+ // TODO(whesse): Use a Dart HTTPS server for this test.
+ // The Dart HTTPS server works on bleeding-edge, but not on IOv2.
+ // When we use a Dart HTTPS server, allow --short_socket_write. The flag
+ // causes fragmentation of the client hello message, which doesn't seem to
+ // work with www.google.dk.
+ RawSecureSocket.connect("www.google.dk", 443).then((socket) {
+ StreamSubscription subscription;
+ bool paused = false;
+ bool readEventsTested = false;
+ bool readEventsPaused = false;
+
+ void runPauseTest() {
+ subscription.pause();
+ paused = true;
+ new Timer(500, (_) {
+ paused = false;
+ subscription.resume();
+ });
+ }
+
+ void runReadEventTest() {
+ if (readEventsTested) return;
+ readEventsTested = true;
+ socket.readEventsEnabled = false;
+ readEventsPaused = true;
+ new Timer(500, (_) {
+ readEventsPaused = false;
+ socket.readEventsEnabled = true;
+ });
+ }
+
+ subscription = socket.listen((RawSocketEvent event) {
+ Expect.isFalse(paused);
+ switch (event) {
+ case RawSocketEvent.READ:
+ Expect.isFalse(readEventsPaused);
+ runReadEventTest();
+ var data = socket.read();
+ var received = new String.fromCharCodes(data);
+ chunks.add(received);
+ break;
+ case RawSocketEvent.WRITE:
+ written +=
+ socket.write(message, written, message.length - written);
+ if (written < message.length) {
+ socket.writeEventsEnabled = true;
+ } else {
+ socket.shutdown(SocketDirection.SEND);
+ runPauseTest();
+ }
+ break;
+ case RawSocketEvent.READ_CLOSED:
+ String fullPage = chunks.join();
+ Expect.isTrue(fullPage.contains('</body></html>'));
+ break;
+ default: throw "Unexpected event $event";
+ }
+ }, onError: (AsyncError a) {
+ Expect.fail("onError handler of RawSecureSocket stream hit: $a");
+ });
+ });
+}
diff --git a/tests/standalone/io/raw_secure_socket_test.dart b/tests/standalone/io/raw_secure_socket_test.dart
new file mode 100644
index 0000000..b7f7068
--- /dev/null
+++ b/tests/standalone/io/raw_secure_socket_test.dart
@@ -0,0 +1,48 @@
+// 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.
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
+
+import "dart:async";
+import "dart:io";
+import "dart:isolate";
+
+
+void main() {
+ List<int> message = "GET / HTTP/1.0\r\nHost: www.google.dk\r\n\r\n".charCodes;
+ int written = 0;
+ List<String> chunks = <String>[];
+ SecureSocket.initialize();
+ // TODO(3593): Use a Dart HTTPS server for this test.
+ RawSecureSocket.connect("www.google.dk", 443).then((socket) {
+ socket.listen((RawSocketEvent event) {
+ switch (event) {
+ case RawSocketEvent.READ:
+ var data = socket.read();
+ var received = new String.fromCharCodes(data);
+ chunks.add(received);
+ break;
+ case RawSocketEvent.WRITE:
+ written +=
+ socket.write(message, written, message.length - written);
+ if (written < message.length) {
+ socket.writeEventsEnabled = true;
+ } else {
+ socket.shutdown(SocketDirection.SEND);
+ }
+ break;
+ case RawSocketEvent.READ_CLOSED:
+ String fullPage = chunks.join();
+ Expect.isTrue(fullPage.contains('</body></html>'));
+ break;
+ default: throw "Unexpected event $event";
+ }
+ }, onError: (AsyncError a) {
+ Expect.fail("onError handler of RawSecureSocket stream hit: $a");
+ });
+ });
+}
diff --git a/tests/standalone/io/raw_socket_test.dart b/tests/standalone/io/raw_socket_test.dart
new file mode 100644
index 0000000..d14dc9e
--- /dev/null
+++ b/tests/standalone/io/raw_socket_test.dart
@@ -0,0 +1,412 @@
+// 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.
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
+
+import "dart:async";
+import "dart:io";
+import "dart:isolate";
+
+void testArguments() {
+ Expect.throws(() => RawServerSocket.bind("127.0.0.1", 65536));
+ Expect.throws(() => RawServerSocket.bind("127.0.0.1", -1));
+ Expect.throws(() => RawServerSocket.bind("127.0.0.1", 0, -1));
+}
+
+void testSimpleBind() {
+ ReceivePort port = new ReceivePort();
+ RawServerSocket.bind().then((s) {
+ Expect.isTrue(s.port > 0);
+ port.close();
+ });
+}
+
+void testInvalidBind() {
+ int count = 0;
+ ReceivePort port = new ReceivePort();
+ port.receive((_, __) { count++; if (count == 3) port.close(); });
+
+ // Bind to a unknown DNS name.
+ RawServerSocket.bind("ko.faar.__hest__")
+ .then((_) { Expect.fail("Failure expected"); } )
+ .catchError((e) {
+ Expect.isTrue(e.error is SocketIOException);
+ port.toSendPort().send(1);
+ });
+
+ // Bind to an unavaliable IP-address.
+ RawServerSocket.bind("8.8.8.8")
+ .then((_) { Expect.fail("Failure expected"); } )
+ .catchError((e) {
+ Expect.isTrue(e.error is SocketIOException);
+ port.toSendPort().send(1);
+ });
+
+ // Bind to a port already in use.
+ // Either an error or a successful bind is allowed.
+ // Windows platforms allow multiple binding to the same socket, with
+ // unpredictable results.
+ RawServerSocket.bind("127.0.0.1")
+ .then((s) {
+ RawServerSocket.bind("127.0.0.1", s.port)
+ .then((t) {
+ Expect.equals('windows', Platform.operatingSystem);
+ Expect.equals(s.port, t.port);
+ port.toSendPort().send(1);
+ })
+ .catchError((e) {
+ Expect.notEquals('windows', Platform.operatingSystem);
+ Expect.isTrue(e.error is SocketIOException);
+ port.toSendPort().send(1);
+ });
+ });
+}
+
+void testSimpleConnect() {
+ ReceivePort port = new ReceivePort();
+ RawServerSocket.bind().then((server) {
+ server.listen((_) { });
+ RawSocket.connect("127.0.0.1", server.port).then((_) {
+ server.close();
+ port.close();
+ });
+ });
+}
+
+void testCloseOneEnd(String toClose) {
+ ReceivePort port = new ReceivePort();
+ Completer serverDone = new Completer();
+ Completer serverEndDone = new Completer();
+ Completer clientEndDone = new Completer();
+ Future.wait([serverDone.future, serverEndDone.future, clientEndDone.future])
+ .then((_) {
+ port.close();
+ });
+ RawServerSocket.bind().then((server) {
+ server.listen((serverConnection) {
+ serverConnection.listen((event) {
+ if (toClose == "server" || event == RawSocketEvent.READ_CLOSED) {
+ serverConnection.shutdown(SocketDirection.SEND);
+ }
+ },
+ onDone: () {
+ serverEndDone.complete(null);
+ });
+ },
+ onDone:() {
+ serverDone.complete(null);
+ });
+ RawSocket.connect("127.0.0.1", server.port).then((clientConnection) {
+ clientConnection.listen((event){
+ if (toClose == "client" || event == RawSocketEvent.READ_CLOSED) {
+ clientConnection.shutdown(SocketDirection.SEND);
+ }
+ },
+ onDone: () {
+ clientEndDone.complete(null);
+ server.close();
+ });
+ });
+ });
+}
+
+void testServerListenAfterConnect() {
+ ReceivePort port = new ReceivePort();
+ RawServerSocket.bind().then((server) {
+ Expect.isTrue(server.port > 0);
+ RawSocket.connect("127.0.0.1", server.port).then((_) {
+ server.listen((_) {
+ server.close();
+ port.close();
+ });
+ });
+ });
+}
+
+void testSimpleReadWrite() {
+ // This test creates a server and a client connects. The client then
+ // writes and the server echos. When the server has finished its
+ // echo it half-closes. When the client gets the close event is
+ // closes fully.
+ ReceivePort port = new ReceivePort();
+
+ const messageSize = 1000;
+
+ List<int> createTestData() {
+ List<int> data = new List.fixedLength(messageSize);
+ for (int i = 0; i < messageSize; i++) {
+ data[i] = i & 0xff;
+ }
+ return data;
+ }
+
+ void verifyTestData(List<int> data) {
+ Expect.equals(messageSize, data.length);
+ List<int> expected = createTestData();
+ for (int i = 0; i < messageSize; i++) {
+ Expect.equals(expected[i], data[i]);
+ }
+ }
+
+ RawServerSocket.bind().then((server) {
+ server.listen((client) {
+ int bytesRead = 0;
+ int bytesWritten = 0;
+ List<int> data = new List.fixedLength(messageSize);
+
+ client.writeEventsEnabled = false;
+ client.listen((event) {
+ switch (event) {
+ case RawSocketEvent.READ:
+ Expect.isTrue(bytesWritten == 0);
+ Expect.isTrue(client.available() > 0);
+ var buffer = client.read();
+ data.setRange(bytesRead, buffer.length, buffer);
+ bytesRead += buffer.length;
+ if (bytesRead == data.length) {
+ verifyTestData(data);
+ client.writeEventsEnabled = true;
+ }
+ break;
+ case RawSocketEvent.WRITE:
+ Expect.isFalse(client.writeEventsEnabled);
+ bytesWritten += client.write(
+ data, bytesWritten, data.length - bytesWritten);
+ if (bytesWritten < data.length) {
+ client.writeEventsEnabled = true;
+ }
+ if (bytesWritten == data.length) {
+ client.shutdown(SocketDirection.SEND);
+ }
+ break;
+ case RawSocketEvent.READ_CLOSED:
+ server.close();
+ break;
+ default: throw "Unexpected event $event";
+ }
+ });
+ });
+
+ RawSocket.connect("127.0.0.1", server.port).then((socket) {
+ int bytesRead = 0;
+ int bytesWritten = 0;
+ List<int> data = createTestData();
+
+ socket.listen((event) {
+ switch (event) {
+ case RawSocketEvent.READ:
+ Expect.isTrue(socket.available() > 0);
+ var buffer = socket.read();
+ data.setRange(bytesRead, buffer.length, buffer);
+ bytesRead += buffer.length;
+ break;
+ case RawSocketEvent.WRITE:
+ Expect.isTrue(bytesRead == 0);
+ Expect.isFalse(socket.writeEventsEnabled);
+ bytesWritten += socket.write(
+ data, bytesWritten, data.length - bytesWritten);
+ if (bytesWritten < data.length) {
+ socket.writeEventsEnabled = true;
+ } else {
+ data = new List.fixedLength(messageSize);
+ }
+ break;
+ case RawSocketEvent.READ_CLOSED:
+ verifyTestData(data);
+ socket.close();
+ break;
+ default: throw "Unexpected event $event";
+ }
+ },
+ onDone: () => port.close());
+ });
+ });
+}
+
+testPauseServerSocket() {
+ const int socketCount = 10;
+ var acceptCount = 0;
+ var resumed = false;
+
+ ReceivePort port = new ReceivePort();
+
+ RawServerSocket.bind().then((server) {
+ Expect.isTrue(server.port > 0);
+ var subscription = server.listen((_) {
+ Expect.isTrue(resumed);
+ if (++acceptCount == socketCount) {
+ server.close();
+ port.close();
+ }
+ });
+
+ // Pause the server socket subscription and resume it after having
+ // connected a number client sockets. Then connect more client
+ // sockets.
+ subscription.pause();
+ var connectCount = 0;
+ for (int i = 0; i <= socketCount / 2; i++) {
+ RawSocket.connect("127.0.0.1", server.port).then((_) {
+ if (++connectCount == socketCount / 2) {
+ subscription.resume();
+ resumed = true;
+ for (int i = connectCount; i < socketCount; i++) {
+ RawSocket.connect("127.0.0.1", server.port).then((_) {});
+ }
+ }
+ });
+ }
+ });
+}
+
+testCancelResubscribeServerSocket() {
+ const int socketCount = 10;
+ var acceptCount = 0;
+ var doneCount = 0;
+ var closeCount = 0;
+ var errorCount = 0;
+
+ ReceivePort port = new ReceivePort();
+
+ RawServerSocket.bind().then((server) {
+ Expect.isTrue(server.port > 0);
+
+ void checkDone() {
+ if (doneCount == socketCount &&
+ closeCount + errorCount == socketCount) {
+ port.close();
+ }
+ }
+
+ // Subscribe the server socket. Then cancel subscription and
+ // subscribe again.
+ var subscription;
+ subscription = server.listen((client) {
+ if (++acceptCount == socketCount / 2) {
+ subscription.cancel();
+ new Timer(0, (_) {
+ subscription = server.listen((_) {
+ // Close on cancel, so no more events.
+ Expect.fail("Event after closed through cancel");
+ });
+ });
+ }
+ // Close the client socket.
+ client.close();
+ });
+
+ // Connect a number of sockets.
+ for (int i = 0; i < socketCount; i++) {
+ RawSocket.connect("127.0.0.1", server.port).then((socket) {
+ socket.writeEventsEnabled = false;
+ var subscription;
+ subscription = socket.listen((event) {
+ Expect.equals(RawSocketEvent.READ_CLOSED, event);
+ socket.close();
+ closeCount++;
+ checkDone();
+ },
+ onDone: () { doneCount++; checkDone(); },
+ onError: (e) { errorCount++; checkDone(); });
+ });
+ }
+ });
+}
+
+testPauseSocket() {
+ const messageSize = 1000;
+ const loopCount = 10;
+ Completer connected = new Completer();
+ int pauseResumeCount = 0;
+ int bytesWritten = 0;
+ int bytesRead = 0;
+ var writeSubscription;
+ var readSubscription;
+
+ ReceivePort port = new ReceivePort();
+
+ RawServerSocket.bind().then((server) {
+ Expect.isTrue(server.port > 0);
+ server.listen((client) {
+ List<int> data = new List.fixedLength(messageSize, fill: 0);
+ writeSubscription = client.listen((event) {
+ switch (event) {
+ case RawSocketEvent.READ:
+ throw "Unexpected read event";
+ case RawSocketEvent.WRITE:
+ if (pauseResumeCount == loopCount) return;
+ Expect.isFalse(client.writeEventsEnabled);
+ Expect.equals(0, bytesRead); // Checks that reader is paused.
+ bytesWritten += client.write(
+ data, bytesWritten, data.length - bytesWritten);
+ // Ensure all data is written. When done disable the write
+ // event and resume the receiver.
+ if (bytesWritten == data.length) {
+ writeSubscription.pause();
+ bytesWritten = 0;
+ connected.future.then((_) { readSubscription.resume(); });
+ }
+ client.writeEventsEnabled = true;
+ break;
+ case RawSocketEvent.READ_CLOSED:
+ client.close();
+ server.close();
+ break;
+ default: throw "Unexpected event $event";
+ }
+ });
+ });
+
+ RawSocket.connect("127.0.0.1", server.port).then((socket) {
+ socket.writeEventsEnabled = false;
+ readSubscription = socket.listen((event) {
+ switch (event) {
+ case RawSocketEvent.READ:
+ Expect.equals(0, bytesWritten); // Checks that writer is paused.
+ Expect.isTrue(socket.available() > 0);
+ var buffer = socket.read();
+ bytesRead += buffer.length;
+ // Ensure all data is read. When done pause and resume the sender
+ if (bytesRead == messageSize) {
+ if (++pauseResumeCount == loopCount) {
+ socket.close();
+ port.close();
+ } else {
+ readSubscription.pause();
+ }
+ // Always resume writer as it needs the read closed
+ // event when done.
+ bytesRead = 0;
+ writeSubscription.resume();
+ }
+ break;
+ case RawSocketEvent.WRITE:
+ throw "Unexpected write event";
+ case RawSocketEvent.READ_CLOSED:
+ throw "Unexpected close event";
+ default: throw "Unexpected event $event";
+ }
+ });
+ readSubscription.pause();
+ connected.complete(true);
+ });
+ });
+}
+
+main() {
+ testArguments();
+ testSimpleBind();
+ testCloseOneEnd("client");
+ testCloseOneEnd("server");
+ testInvalidBind();
+ testSimpleConnect();
+ testServerListenAfterConnect();
+ testSimpleReadWrite();
+ testPauseServerSocket();
+ testCancelResubscribeServerSocket();
+ testPauseSocket();
+}
diff --git a/tests/standalone/io/raw_socket_write_destroy_test.dart b/tests/standalone/io/raw_socket_write_destroy_test.dart
new file mode 100644
index 0000000..b5e558a8
--- /dev/null
+++ b/tests/standalone/io/raw_socket_write_destroy_test.dart
@@ -0,0 +1,96 @@
+// 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.
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
+
+import "dart:async";
+import "dart:io";
+import "dart:isolate";
+const SERVER_ADDRESS = "127.0.0.1";
+
+void testWriteDestroyServer() {
+ int WROTE = 100000;
+ RawServerSocket.bind(SERVER_ADDRESS).then((server) {
+ server.listen((socket) {
+ socket.writeEventsEnabled = false;
+
+ var buffer = new List.fixedLength(WROTE, fill: 0);
+ int offset = 0;
+ void write() {
+ int n = socket.write(buffer, offset, buffer.length - offset);
+ offset += n;
+ socket.writeEventsEnabled = true;
+ }
+ socket.listen((e) {
+ if (e == RawSocketEvent.WRITE) {
+ if (offset == buffer.length) {
+ socket.close();
+ } else {
+ write();
+ }
+ }
+ });
+ write();
+ });
+ RawSocket.connect(SERVER_ADDRESS, server.port).then((socket) {
+ var bytes = 0;
+ socket.listen((e) {
+ if (e == RawSocketEvent.READ) {
+ bytes += socket.read().length;
+ } else if (e == RawSocketEvent.READ_CLOSED) {
+ Expect.equals(WROTE, bytes);
+ socket.close();
+ server.close();
+ }
+ });
+ });
+ });
+}
+
+void testWriteDestroyClient() {
+ int WROTE = 100000;
+ RawServerSocket.bind(SERVER_ADDRESS).then((server) {
+ server.listen((socket) {
+ var bytes = 0;
+ socket.listen((e) {
+ if (e == RawSocketEvent.READ) {
+ bytes += socket.read().length;
+ } else if (e == RawSocketEvent.READ_CLOSED) {
+ Expect.equals(WROTE, bytes);
+ socket.close();
+ server.close();
+ }
+ });
+ });
+ RawSocket.connect(SERVER_ADDRESS, server.port).then((socket) {
+ socket.writeEventsEnabled = false;
+
+ var buffer = new List.fixedLength(WROTE, fill: 0);
+ int offset = 0;
+ void write() {
+ int n = socket.write(buffer, offset, buffer.length - offset);
+ offset += n;
+ socket.writeEventsEnabled = true;
+ }
+ socket.listen((e) {
+ if (e == RawSocketEvent.WRITE) {
+ if (offset == buffer.length) {
+ socket.close();
+ } else {
+ write();
+ }
+ }
+ });
+ write();
+ });
+ });
+}
+
+void main() {
+ testWriteDestroyServer();
+ testWriteDestroyClient();
+}
\ No newline at end of file
diff --git a/tests/standalone/io/read_into_const_list_test.dart b/tests/standalone/io/read_into_const_list_test.dart
index 0362f3c..576cc3f 100644
--- a/tests/standalone/io/read_into_const_list_test.dart
+++ b/tests/standalone/io/read_into_const_list_test.dart
@@ -17,13 +17,14 @@
String filename = getFilename("bin/file_test.cc");
File file = new File(filename);
- InputStream input = file.openInputStream();
- try {
- input.readInto(a, 0, 1);
- Expect.fail("no exception thrown");
- } catch (e) {
- Expect.isTrue(e is UnsupportedError);
- }
- Expect.equals(0, a[0]);
- Expect.equals(0, b[0]);
+ file.open().then((input) {
+ try {
+ input.readListSync(a, 0, 1);
+ Expect.fail("no exception thrown");
+ } catch (e) {
+ Expect.isTrue(e is UnsupportedError);
+ }
+ Expect.equals(0, a[0]);
+ Expect.equals(0, b[0]);
+ });
}
diff --git a/tests/standalone/io/regress_6521_test.dart b/tests/standalone/io/regress_6521_test.dart
index cd625ee..73eb67a 100644
--- a/tests/standalone/io/regress_6521_test.dart
+++ b/tests/standalone/io/regress_6521_test.dart
@@ -11,29 +11,30 @@
var clientRequest;
void main() {
- var server = new HttpServer();
- server.listen("127.0.0.1", 0);
- server.defaultRequestHandler = (req, rsp) {
- req.inputStream.onData = () {
- req.inputStream.read();
- rsp.outputStream.close();
- };
- };
+ HttpServer.bind("127.0.0.1", 0)
+ .then((server) {
+ server.listen(
+ (req) {
+ req.pipe(req.response);
+ });
- var connection = client.openUrl(
- "POST",
- Uri.parse("http://localhost:${server.port}/"));
- connection.onRequest = (request) {
- // Keep a reference to the client request object.
- clientRequest = request;
- request.outputStream.write([0]);
- };
- connection.onResponse = (response) {
- response.inputStream.onClosed = () {
- // Wait with closing the client request until the response is done.
- clientRequest.outputStream.close();
- client.shutdown();
- server.close();
- };
- };
+ client.openUrl("POST", Uri.parse("http://localhost:${server.port}/"))
+ .then((request) {
+ // Keep a reference to the client request object.
+ clientRequest = request;
+ request.add([0]);
+ return request.response;
+ })
+ .then((response) {
+ // Wait with closing the client request until the response headers
+ // are done.
+ clientRequest.close();
+ response.listen(
+ (_) {},
+ onDone: () {
+ client.close();
+ server.close();
+ });
+ });
+ });
}
diff --git a/tests/standalone/io/regress_7097_test.dart b/tests/standalone/io/regress_7097_test.dart
deleted file mode 100644
index 214f92b..0000000
--- a/tests/standalone/io/regress_7097_test.dart
+++ /dev/null
@@ -1,82 +0,0 @@
-// 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.
-
-import 'dart:async';
-import 'dart:io';
-import 'dart:uri';
-
-void main() {
- var client = new HttpClient();
- var server = new HttpServer();
-
- var count = 0;
- server.defaultRequestHandler = (HttpRequest request, HttpResponse response) {
- List<int> data = [];
- request.inputStream.onData = () {
- data.addAll(request.inputStream.read());
- };
- request.inputStream.onClosed = () {
- count++;
- Expect.equals(count, data.length);
- switch (count - 1) {
- case 0:
- response.outputStream.writeFrom([65, 66, 67], 1, 1);
- break;
- case 1:
- response.outputStream.writeFrom([65, 66, 67], 1);
- break;
- case 2:
- response.outputStream.writeFrom([65, 66, 67]);
- break;
- default:
- Expect.fail("Unexpected state");
- }
- response.outputStream.close();
- };
- };
- server.listen('127.0.0.1', 0);
-
- Future makeRequest(int n) {
- var completer = new Completer();
- var url = Uri.parse("http://localhost:${server.port}");
- var connection = client.openUrl("POST", url);
- connection.onRequest = (HttpClientRequest request) {
- request.contentLength = n + 1;
- switch (n) {
- case 0:
- request.outputStream.writeFrom([65, 66, 67], 1, 1);
- break;
- case 1:
- request.outputStream.writeFrom([65, 66, 67], 1);
- break;
- case 2:
- request.outputStream.writeFrom([65, 66, 67]);
- break;
- default:
- Expect.fail("Unexpected state");
- }
- request.outputStream.close();
- };
- connection.onResponse = (HttpClientResponse response) {
- List<int> data = [];
- response.inputStream.onData = () {
- data.addAll(response.inputStream.read());
- };
- response.inputStream.onClosed = () {
- Expect.equals(count, data.length);
- completer.complete(null);
- };
- };
- return completer.future;
- }
-
- makeRequest(0).then((_) {
- makeRequest(1).then((_) {
- makeRequest(2).then((_) {
- client.shutdown();
- server.close();
- });
- });
- });
-}
diff --git a/tests/standalone/io/regress_7191_script.dart b/tests/standalone/io/regress_7191_script.dart
index d90f0b5..1b047f1 100644
--- a/tests/standalone/io/regress_7191_script.dart
+++ b/tests/standalone/io/regress_7191_script.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
@@ -9,22 +9,21 @@
// Open a port to make the script hang.
var port = new ReceivePort();
// Start sub-process when receiving data.
- stdin.onData = () {
- var data = stdin.read();
+ var subscription;
+ subscription = stdin.listen((data) {
var options = new Options();
Process.start(options.executable, [options.script]).then((p) {
- p.stdout.onData = p.stdout.read;
- p.stderr.onData = p.stderr.read;
+ p.stdout.listen((_) { });
+ p.stderr.listen((_) { });
// When receiving data again, kill sub-process and exit.
- stdin.onData = () {
- var data = stdin.read();
+ subscription.onData((data) {
Expect.listEquals([0], data);
p.kill();
- p.onExit = exit;
- };
+ p.exitCode.then(exit);
+ });
// Close stdout. If handles are incorrectly inherited this will
// not actually close stdout and the test will hang.
stdout.close();
});
- };
+ });
}
diff --git a/tests/standalone/io/regress_7191_test.dart b/tests/standalone/io/regress_7191_test.dart
index 4d72e52..fa92538 100644
--- a/tests/standalone/io/regress_7191_test.dart
+++ b/tests/standalone/io/regress_7191_test.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
@@ -19,12 +19,10 @@
var scriptDir = new Path(options.script).directoryPath;
var script = scriptDir.append('regress_7191_script.dart').toNativePath();
Process.start(executable, [script]).then((process) {
- process.stdin.write([0]);
- process.stdout.onData = process.stdout.read;
- process.stderr.onData = process.stderr.read;
- process.stdout.onClosed = () {
- process.stdin.write([0]);
- };
- process.onExit = (exitCode) => port.close();
+ process.stdin.add([0]);
+ process.stdout.listen((_) { },
+ onDone: () { process.stdin.add([0]); });
+ process.stderr.listen((_) { });
+ process.exitCode.then((exitCode) => port.close());
});
}
diff --git a/tests/standalone/io/secure_builtin_roots_test.dart b/tests/standalone/io/secure_builtin_roots_test.dart
deleted file mode 100644
index 0881ab3..0000000
--- a/tests/standalone/io/secure_builtin_roots_test.dart
+++ /dev/null
@@ -1,44 +0,0 @@
-// 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.
-
-import "dart:io";
-import "dart:uri";
-import "dart:isolate";
-
-void testGoogleUrl() {
- ReceivePort keepAlive = new ReceivePort();
- HttpClient client = new HttpClient();
-
- void testUrl(String url) {
- var requestUri = Uri.parse(url);
- var conn = client.getUrl(requestUri);
-
- conn.onRequest = (HttpClientRequest request) {
- request.outputStream.close();
- };
- conn.onResponse = (HttpClientResponse response) {
- Expect.isTrue(response.statusCode < 500);
- Expect.isTrue(response.statusCode != 404);
- response.inputStream.onData = () {
- response.inputStream.read();
- };
- response.inputStream.onClosed = () {
- client.shutdown();
- keepAlive.close();
- };
- };
- conn.onError = (error) => Expect.fail("Unexpected IO error $error");
- }
-
- testUrl('https://www.google.com');
-}
-
-void InitializeSSL() {
- SecureSocket.initialize();
-}
-
-void main() {
- InitializeSSL();
- testGoogleUrl();
-}
diff --git a/tests/standalone/io/secure_client_raw_server_test.dart b/tests/standalone/io/secure_client_raw_server_test.dart
new file mode 100644
index 0000000..331ea6c
--- /dev/null
+++ b/tests/standalone/io/secure_client_raw_server_test.dart
@@ -0,0 +1,91 @@
+// 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.
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
+
+import "dart:async";
+import "dart:io";
+import "dart:isolate";
+
+const SERVER_ADDRESS = "127.0.0.1";
+const HOST_NAME = "localhost";
+const CERTIFICATE = "localhost_cert";
+Future<RawSecureServerSocket> startEchoServer() {
+ return RawSecureServerSocket.bind(SERVER_ADDRESS,
+ 0,
+ 5,
+ CERTIFICATE).then((server) {
+ server.listen((RawSecureSocket client) {
+ List<List<int>> readChunks = <List<int>>[];
+ List<int> dataToWrite = null;
+ int bytesWritten = 0;
+ client.writeEventsEnabled = false;
+ client.listen((event) {
+ switch (event) {
+ case RawSocketEvent.READ:
+ Expect.isTrue(bytesWritten == 0);
+ Expect.isTrue(client.available() > 0);
+ readChunks.add(client.read());
+ break;
+ case RawSocketEvent.WRITE:
+ Expect.isFalse(client.writeEventsEnabled);
+ Expect.isNotNull(dataToWrite);
+ bytesWritten += client.write(
+ dataToWrite, bytesWritten, dataToWrite.length - bytesWritten);
+ if (bytesWritten < dataToWrite.length) {
+ client.writeEventsEnabled = true;
+ }
+ if (bytesWritten == dataToWrite.length) {
+ client.shutdown(SocketDirection.SEND);
+ }
+ break;
+ case RawSocketEvent.READ_CLOSED:
+ dataToWrite = readChunks.reduce(<int>[], (list, x) {
+ list.addAll(x);
+ return list;
+ });
+ client.writeEventsEnabled = true;
+ break;
+ }
+ });
+ });
+ return server;
+ });
+}
+
+Future testClient(server) {
+ Completer success = new Completer();
+ List<String> chunks = <String>[];
+ SecureSocket.connect(HOST_NAME, server.port).then((socket) {
+ socket.add("Hello server.".charCodes);
+ socket.close();
+ socket.listen(
+ (List<int> data) {
+ var received = new String.fromCharCodes(data);
+ chunks.add(received);
+ },
+ onDone: () {
+ String reply = chunks.join();
+ Expect.equals("Hello server.", reply);
+ success.complete(server);
+ });
+ });
+ return success.future;
+}
+
+void main() {
+ Path scriptDir = new Path(new Options().script).directoryPath;
+ Path certificateDatabase = scriptDir.append('pkcert');
+ SecureSocket.initialize(database: certificateDatabase.toNativePath(),
+ password: 'dartdart');
+
+ startEchoServer()
+ .then(testClient)
+ .then((server) {
+ server.close();
+ });
+}
diff --git a/tests/standalone/io/secure_client_server_test.dart b/tests/standalone/io/secure_client_server_test.dart
new file mode 100644
index 0000000..da48e87
--- /dev/null
+++ b/tests/standalone/io/secure_client_server_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.
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
+
+import "dart:async";
+import "dart:io";
+import "dart:isolate";
+
+const SERVER_ADDRESS = "127.0.0.1";
+const HOST_NAME = "localhost";
+const CERTIFICATE = "localhost_cert";
+Future<SecureServerSocket> startEchoServer() {
+ return SecureServerSocket.bind(SERVER_ADDRESS,
+ 0,
+ 5,
+ CERTIFICATE).then((server) {
+ server.listen((SecureSocket client) {
+ client.reduce(<int>[], (message, data) => message..addAll(data))
+ .then((message) {
+ client.add(message);
+ client.close();
+ });
+ });
+ return server;
+ });
+}
+
+Future testClient(server) {
+ return SecureSocket.connect(HOST_NAME, server.port).then((socket) {
+ socket.add("Hello server.".charCodes);
+ socket.close();
+ return socket.reduce(<int>[], (message, data) => message..addAll(data))
+ .then((message) {
+ Expect.listEquals("Hello server.".charCodes, message);
+ return server;
+ });
+ });
+}
+
+void main() {
+ Path scriptDir = new Path(new Options().script).directoryPath;
+ Path certificateDatabase = scriptDir.append('pkcert');
+ SecureSocket.initialize(database: certificateDatabase.toNativePath(),
+ password: 'dartdart');
+
+ startEchoServer()
+ .then(testClient)
+ .then((server) {
+ server.close();
+ });
+}
diff --git a/tests/standalone/io/secure_multiple_client_server_test.dart b/tests/standalone/io/secure_multiple_client_server_test.dart
new file mode 100644
index 0000000..52e0b56
--- /dev/null
+++ b/tests/standalone/io/secure_multiple_client_server_test.dart
@@ -0,0 +1,59 @@
+// 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.
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
+
+import "dart:async";
+import "dart:io";
+import "dart:isolate";
+
+const SERVER_ADDRESS = "127.0.0.1";
+const HOST_NAME = "localhost";
+const CERTIFICATE = "localhost_cert";
+Future<SecureServerSocket> startServer() {
+ return SecureServerSocket.bind(SERVER_ADDRESS,
+ 0,
+ 5,
+ CERTIFICATE).then((server) {
+ server.listen((SecureSocket client) {
+ client.reduce(<int>[], (message, data) => message..addAll(data))
+ .then((message) {
+ String received = new String.fromCharCodes(message);
+ Expect.isTrue(received.contains("Hello from client "));
+ String name = received.substring(received.indexOf("client ") + 7);
+ client.add("Welcome, client $name".charCodes);
+ client.close();
+ });
+ });
+ return server;
+ });
+}
+
+Future testClient(server, name) {
+ return SecureSocket.connect(HOST_NAME, server.port).then((socket) {
+ socket.add("Hello from client $name".charCodes);
+ socket.close();
+ return socket.reduce(<int>[], (message, data) => message..addAll(data))
+ .then((message) {
+ Expect.listEquals("Welcome, client $name".charCodes, message);
+ return server;
+ });
+ });
+}
+
+void main() {
+ Path scriptDir = new Path(new Options().script).directoryPath;
+ Path certificateDatabase = scriptDir.append('pkcert');
+ SecureSocket.initialize(database: certificateDatabase.toNativePath(),
+ password: 'dartdart');
+
+ startServer()
+ .then((server) => Future.wait(
+ ['able', 'baker', 'charlie', 'dozen', 'elapse']
+ .map((name) => testClient(server, name))))
+ .then((servers) => servers.first.close());
+}
diff --git a/tests/standalone/io/secure_no_builtin_roots_test.dart b/tests/standalone/io/secure_no_builtin_roots_test.dart
index 40dbe21..9b6f489 100644
--- a/tests/standalone/io/secure_no_builtin_roots_test.dart
+++ b/tests/standalone/io/secure_no_builtin_roots_test.dart
@@ -5,28 +5,20 @@
import "dart:io";
import "dart:uri";
import "dart:isolate";
+import "dart:async";
void testGoogleUrl() {
- ReceivePort keepalivePort = new ReceivePort();
+ ReceivePort keepAlive = new ReceivePort();
HttpClient client = new HttpClient();
-
- void testUrl(String url) {
- var requestUri = Uri.parse(url);
- var conn = client.getUrl(requestUri);
-
- conn.onRequest = (HttpClientRequest request) {
- request.outputStream.close();
- };
- conn.onResponse = (HttpClientResponse response) {
- Expect.fail("Https connection unexpectedly succeeded");
- };
- conn.onError = (error) {
- Expect.isTrue(error is SocketIOException);
- keepalivePort.close();
- };
- }
-
- testUrl('https://www.google.com');
+ client.getUrl(Uri.parse('https://www.google.com'))
+ .then((request) => request.close())
+ .then((response) => Expect.fail("Unexpected successful connection"))
+ .catchError((error) {
+ Expect.isTrue(error is AsyncError);
+ Expect.isTrue(error.error is SocketIOException);
+ keepAlive.close();
+ client.close();
+ });
}
void InitializeSSL() {
diff --git a/tests/standalone/io/secure_server_client_certificate_test.dart b/tests/standalone/io/secure_server_client_certificate_test.dart
index ad177f7..5d75c6c 100644
--- a/tests/standalone/io/secure_server_client_certificate_test.dart
+++ b/tests/standalone/io/secure_server_client_certificate_test.dart
@@ -48,7 +48,7 @@
server = new SecureServerSocket(SERVER_ADDRESS,
0,
10,
- "CN=$HOST_NAME",
+ "localhost_cert",
requireClientCertificate: true);
Expect.isNotNull(server);
server.onConnection = onConnection;
diff --git a/tests/standalone/io/secure_server_closing_test.dart b/tests/standalone/io/secure_server_closing_test.dart
new file mode 100644
index 0000000..794c54f
--- /dev/null
+++ b/tests/standalone/io/secure_server_closing_test.dart
@@ -0,0 +1,167 @@
+// 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.
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
+
+import "dart:async";
+import "dart:io";
+import "dart:isolate";
+
+const SERVER_ADDRESS = "127.0.0.1";
+const HOST_NAME = "localhost";
+const CERTIFICATE = "localhost_cert";
+
+void testCloseOneEnd(String toClose) {
+ ReceivePort port = new ReceivePort();
+ Completer serverDone = new Completer();
+ Completer serverEndDone = new Completer();
+ Completer clientEndDone = new Completer();
+ Future.wait([serverDone.future, serverEndDone.future, clientEndDone.future])
+ .then((_) {
+ port.close();
+ });
+ SecureServerSocket.bind(SERVER_ADDRESS, 0, 5, CERTIFICATE).then((server) {
+ server.listen((serverConnection) {
+ serverConnection.listen(
+ (data) {
+ Expect.fail("No data should be received by server");
+ },
+ onDone: () {
+ serverConnection.close();
+ serverEndDone.complete(null);
+ server.close();
+ });
+ if (toClose == "server") {
+ serverConnection.close();
+ }
+ },
+ onDone: () {
+ serverDone.complete(null);
+ });
+ SecureSocket.connect(HOST_NAME, server.port).then((clientConnection) {
+ clientConnection.listen(
+ (data) {
+ Expect.fail("No data should be received by client");
+ },
+ onDone: () {
+ clientConnection.close();
+ clientEndDone.complete(null);
+ });
+ if (toClose == "client") {
+ clientConnection.close();
+ }
+ });
+ });
+}
+
+void testCloseBothEnds() {
+ ReceivePort port = new ReceivePort();
+ SecureServerSocket.bind(SERVER_ADDRESS, 0, 5, CERTIFICATE).then((server) {
+ var clientEndFuture = SecureSocket.connect(HOST_NAME, server.port);
+ server.listen((serverEnd) {
+ clientEndFuture.then((clientEnd) {
+ clientEnd.destroy();
+ serverEnd.destroy();
+ server.close();
+ port.close();
+ });
+ });
+ });
+}
+
+testPauseServerSocket() {
+ const int socketCount = 10;
+ var acceptCount = 0;
+ var resumed = false;
+
+ ReceivePort port = new ReceivePort();
+
+ SecureServerSocket.bind(SERVER_ADDRESS,
+ 0,
+ 2 * socketCount,
+ CERTIFICATE).then((server) {
+ Expect.isTrue(server.port > 0);
+ var subscription;
+ subscription = server.listen((connection) {
+ Expect.isTrue(resumed);
+ connection.close();
+ if (++acceptCount == 2 * socketCount) {
+ server.close();
+ port.close();
+ }
+ });
+
+ // Pause the server socket subscription and resume it after having
+ // connected a number client sockets. Then connect more client
+ // sockets.
+ subscription.pause();
+ var connectCount = 0;
+ for (int i = 0; i < socketCount; i++) {
+ SecureSocket.connect(HOST_NAME, server.port).then((connection) {
+ connection.close();
+ });
+ }
+ new Timer(500, (_) {
+ subscription.resume();
+ resumed = true;
+ for (int i = 0; i < socketCount; i++) {
+ SecureSocket.connect(HOST_NAME, server.port).then((connection) {
+ connection.close();
+ });
+ }
+ });
+ });
+}
+
+
+testCloseServer() {
+ const int socketCount = 3;
+ var endCount = 0;
+ ReceivePort port = new ReceivePort();
+ List ends = [];
+
+ SecureServerSocket.bind(SERVER_ADDRESS, 0, 15, CERTIFICATE).then((server) {
+ Expect.isTrue(server.port > 0);
+ void checkDone() {
+ if (ends.length < 2 * socketCount) return;
+ for (var end in ends) {
+ end.destroy();
+ }
+ server.close();
+ port.close();
+ }
+
+ server.listen((connection) {
+ ends.add(connection);
+ checkDone();
+ });
+
+ for (int i = 0; i < socketCount; i++) {
+ SecureSocket.connect(HOST_NAME, server.port).then((connection) {
+ ends.add(connection);
+ checkDone();
+ });
+ }
+ });
+}
+
+
+main() {
+ Path scriptDir = new Path(new Options().script).directoryPath;
+ Path certificateDatabase = scriptDir.append('pkcert');
+ SecureSocket.initialize(database: certificateDatabase.toNativePath(),
+ password: 'dartdart',
+ useBuiltinRoots: false);
+
+ testCloseOneEnd("client");
+ testCloseOneEnd("server");
+ testCloseBothEnds();
+ testPauseServerSocket();
+ testCloseServer();
+ // TODO(whesse): Add testPauseSocket from raw_socket_test.dart.
+ // TODO(whesse): Add testCancelResubscribeSocket from raw_socket_test.dart.
+}
diff --git a/tests/standalone/io/secure_server_socket_test.dart b/tests/standalone/io/secure_server_socket_test.dart
new file mode 100644
index 0000000..cadabf3
--- /dev/null
+++ b/tests/standalone/io/secure_server_socket_test.dart
@@ -0,0 +1,218 @@
+// 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.
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
+
+import "dart:async";
+import "dart:io";
+import "dart:isolate";
+
+const SERVER_ADDRESS = "127.0.0.1";
+const HOST_NAME = "localhost";
+const CERTIFICATE = "localhost_cert";
+
+void testArguments() {
+ Expect.throws(() =>
+ SecureServerSocket.bind(SERVER_ADDRESS, 65536, 5, CERTIFICATE));
+ Expect.throws(() =>
+ SecureServerSocket.bind(SERVER_ADDRESS, -1, CERTIFICATE));
+ Expect.throws(() =>
+ SecureServerSocket.bind(SERVER_ADDRESS, 0, -1, CERTIFICATE));
+}
+
+void testSimpleBind() {
+ ReceivePort port = new ReceivePort();
+ SecureServerSocket.bind(SERVER_ADDRESS, 0, 5, CERTIFICATE).then((s) {
+ Expect.isTrue(s.port > 0);
+ s.close();
+ port.close();
+ });
+}
+
+void testInvalidBind() {
+ int count = 0;
+ ReceivePort port = new ReceivePort();
+ port.receive((_, __) { count++; if (count == 3) port.close(); });
+
+ // Bind to a unknown DNS name.
+ SecureServerSocket.bind("ko.faar.__hest__", 0, 5, CERTIFICATE).then((_) {
+ Expect.fail("Failure expected");
+ }).catchError((e) {
+ Expect.isTrue(e.error is SocketIOException);
+ port.toSendPort().send(1);
+ });
+
+ // Bind to an unavaliable IP-address.
+ SecureServerSocket.bind("8.8.8.8", 0, 5, CERTIFICATE).then((_) {
+ Expect.fail("Failure expected");
+ }).catchError((e) {
+ Expect.isTrue(e.error is SocketIOException);
+ port.toSendPort().send(1);
+ });
+
+ // Bind to a port already in use.
+ // Either an error or a successful bind is allowed.
+ // Windows platforms allow multiple binding to the same socket, with
+ // unpredictable results.
+ SecureServerSocket.bind(SERVER_ADDRESS, 0, 5, CERTIFICATE).then((s) {
+ SecureServerSocket.bind(SERVER_ADDRESS,
+ s.port,
+ 5,
+ CERTIFICATE).then((t) {
+ Expect.equals('windows', Platform.operatingSystem);
+ Expect.equals(s.port, t.port);
+ s.close();
+ t.close();
+ port.toSendPort().send(1);
+ }).catchError((e) {
+ Expect.notEquals('windows', Platform.operatingSystem);
+ Expect.isTrue(e.error is SocketIOException);
+ s.close();
+ port.toSendPort().send(1);
+ });
+ });
+}
+
+void testSimpleConnect(String certificate) {
+ ReceivePort port = new ReceivePort();
+ SecureServerSocket.bind(SERVER_ADDRESS, 0, 5, certificate).then((server) {
+ var clientEndFuture = SecureSocket.connect(HOST_NAME, server.port);
+ server.listen((serverEnd) {
+ clientEndFuture.then((clientEnd) {
+ clientEnd.close();
+ serverEnd.close();
+ server.close();
+ port.close();
+ });
+ });
+ });
+}
+
+void testSimpleConnectFail(String certificate) {
+ ReceivePort port = new ReceivePort();
+ SecureServerSocket.bind(SERVER_ADDRESS, 0, 5, certificate).then((server) {
+ var clientEndFuture = SecureSocket.connect(HOST_NAME, server.port)
+ .then((clientEnd) {
+ Expect.fail("No client connection expected.");
+ })
+ .catchError((e) {
+ Expect.isTrue(e is AsyncError);
+ Expect.isTrue(e.error is SocketIOException);
+ });
+ server.listen((serverEnd) {
+ Expect.fail("No server connection expected.");
+ },
+ onError: (e) {
+ Expect.isTrue(e is AsyncError);
+ Expect.isTrue(e.error is SocketIOException);
+ clientEndFuture.then((_) => port.close());
+ });
+ });
+}
+
+void testServerListenAfterConnect() {
+ ReceivePort port = new ReceivePort();
+ SecureServerSocket.bind(SERVER_ADDRESS, 0, 5, CERTIFICATE).then((server) {
+ Expect.isTrue(server.port > 0);
+ var clientEndFuture = SecureSocket.connect(HOST_NAME, server.port);
+ new Timer(500, (_) {
+ server.listen((serverEnd) {
+ clientEndFuture.then((clientEnd) {
+ clientEnd.close();
+ serverEnd.close();
+ server.close();
+ port.close();
+ });
+ });
+ });
+ });
+}
+
+void testSimpleReadWrite() {
+ // This test creates a server and a client connects. The client then
+ // writes and the server echos. When the server has finished its
+ // echo it half-closes. When the client gets the close event is
+ // closes fully.
+ ReceivePort port = new ReceivePort();
+
+ const messageSize = 1000;
+
+ List<int> createTestData() {
+ List<int> data = new List.fixedLength(messageSize);
+ for (int i = 0; i < messageSize; i++) {
+ data[i] = i & 0xff;
+ }
+ return data;
+ }
+
+ void verifyTestData(List<int> data) {
+ Expect.equals(messageSize, data.length);
+ List<int> expected = createTestData();
+ for (int i = 0; i < messageSize; i++) {
+ Expect.equals(expected[i], data[i]);
+ }
+ }
+
+ SecureServerSocket.bind(SERVER_ADDRESS, 0, 5, CERTIFICATE).then((server) {
+ server.listen((client) {
+ int bytesRead = 0;
+ int bytesWritten = 0;
+ List<int> data = new List.fixedLength(messageSize);
+
+ client.listen(
+ (buffer) {
+ Expect.isTrue(bytesWritten == 0);
+ data.setRange(bytesRead, buffer.length, buffer);
+ bytesRead += buffer.length;
+ if (bytesRead == data.length) {
+ verifyTestData(data);
+ client.add(data);
+ client.close();
+ }
+ },
+ onDone: () {
+ server.close();
+ });
+ });
+
+ SecureSocket.connect(HOST_NAME, server.port).then((socket) {
+ int bytesRead = 0;
+ int bytesWritten = 0;
+ List<int> dataSent = createTestData();
+ List<int> dataReceived = new List<int>.fixedLength(dataSent.length);
+ socket.add(dataSent);
+ socket.close(); // Can also be delayed.
+ socket.listen(
+ (List<int> buffer) {
+ dataReceived.setRange(bytesRead, buffer.length, buffer);
+ bytesRead += buffer.length;
+ },
+ onDone: () {
+ verifyTestData(dataReceived);
+ socket.close();
+ port.close();
+ });
+ });
+ });
+}
+
+main() {
+ Path scriptDir = new Path(new Options().script).directoryPath;
+ Path certificateDatabase = scriptDir.append('pkcert');
+ SecureSocket.initialize(database: certificateDatabase.toNativePath(),
+ password: 'dartdart',
+ useBuiltinRoots: false);
+ testArguments();
+ testSimpleBind();
+ testInvalidBind();
+ testSimpleConnect(CERTIFICATE);
+ testSimpleConnect("CN=localhost");
+ testSimpleConnectFail("not_a_nickname");
+ testSimpleConnectFail("CN=notARealDistinguishedName");
+ testServerListenAfterConnect();
+ testSimpleReadWrite();
+}
diff --git a/tests/standalone/io/secure_server_stream_test.dart b/tests/standalone/io/secure_server_stream_test.dart
deleted file mode 100644
index d99e93e..0000000
--- a/tests/standalone/io/secure_server_stream_test.dart
+++ /dev/null
@@ -1,101 +0,0 @@
-// 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.
-
-import "dart:io";
-import "dart:isolate";
-
-const SERVER_ADDRESS = "127.0.0.1";
-const HOST_NAME = "localhost";
-
-class SecureTestServer {
- void onConnection(Socket connection) {
- numConnections++;
- var input = connection.inputStream;
- String received = "";
- input.onData = () {
- received = received.concat(new String.fromCharCodes(input.read()));
- };
- input.onClosed = () {
- Expect.isTrue(received.contains("Hello from client "));
- String name = received.substring(received.indexOf("client ") + 7);
- connection.outputStream.write("Welcome, client $name".charCodes);
- connection.outputStream.close();
- };
- }
-
- void errorHandlerServer(Exception e) {
- Expect.fail("Server socket error $e");
- }
-
- int start() {
- server = new SecureServerSocket(SERVER_ADDRESS, 0, 10, "CN=$HOST_NAME");
- Expect.isNotNull(server);
- server.onConnection = onConnection;
- server.onError = errorHandlerServer;
- return server.port;
- }
-
- void stop() {
- server.close();
- }
-
- int numConnections = 0;
- SecureServerSocket server;
-}
-
-class SecureTestClient {
- SecureTestClient(int this.port, String this.name) {
- socket = new SecureSocket(HOST_NAME, port);
- numRequests++;
- socket.outputStream.write("Hello from client $name".charCodes);
- socket.outputStream.close();
- socket.inputStream.onData = () {
- reply = reply.concat(new String.fromCharCodes(socket.inputStream.read()));
- };
- socket.inputStream.onClosed = this.done;
- reply = "";
- }
-
- void done() {
- Expect.equals("Welcome, client $name", reply);
- numReplies++;
- if (numReplies == CLIENT_NAMES.length) {
- Expect.equals(numRequests, numReplies);
- EndTest();
- }
- }
-
- static int numRequests = 0;
- static int numReplies = 0;
-
- int port;
- String name;
- SecureSocket socket;
- String reply;
-}
-
-Function EndTest;
-
-const CLIENT_NAMES = const ['able', 'baker', 'camera', 'donut', 'echo'];
-
-void main() {
- ReceivePort keepAlive = new ReceivePort();
- Path scriptDir = new Path(new Options().script).directoryPath;
- Path certificateDatabase = scriptDir.append('pkcert');
- SecureSocket.initialize(database: certificateDatabase.toNativePath(),
- password: 'dartdart');
-
- var server = new SecureTestServer();
- int port = server.start();
-
- EndTest = () {
- Expect.equals(CLIENT_NAMES.length, server.numConnections);
- server.stop();
- keepAlive.close();
- };
-
- for (var x in CLIENT_NAMES) {
- new SecureTestClient(port, x);
- }
-}
diff --git a/tests/standalone/io/secure_server_test.dart b/tests/standalone/io/secure_server_test.dart
deleted file mode 100644
index f039284..0000000
--- a/tests/standalone/io/secure_server_test.dart
+++ /dev/null
@@ -1,119 +0,0 @@
-// 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.
-
-import "dart:io";
-import "dart:isolate";
-
-const SERVER_ADDRESS = "127.0.0.1";
-const HOST_NAME = "localhost";
-
-void WriteAndClose(Socket socket, String message) {
- var data = message.charCodes;
- int written = 0;
- void write() {
- written += socket.writeList(data, written, data.length - written);
- if (written < data.length) {
- socket.onWrite = write;
- } else {
- socket.close(true);
- }
- }
- write();
-}
-
-class SecureTestServer {
- void onConnection(Socket connection) {
- connection.onConnect = () {
- numConnections++;
- };
- String received = "";
- connection.onData = () {
- received = received.concat(new String.fromCharCodes(connection.read()));
- };
- connection.onClosed = () {
- Expect.isTrue(received.contains("Hello from client "));
- String name = received.substring(received.indexOf("client ") + 7);
- WriteAndClose(connection, "Welcome, client $name");
- };
- }
-
- void errorHandlerServer(Exception e) {
- Expect.fail("Server socket error $e");
- }
-
- int start() {
- server = new SecureServerSocket(SERVER_ADDRESS, 0, 10, "CN=$HOST_NAME");
- Expect.isNotNull(server);
- server.onConnection = onConnection;
- server.onError = errorHandlerServer;
- return server.port;
- }
-
- void stop() {
- server.close();
- }
-
- int numConnections = 0;
- SecureServerSocket server;
-}
-
-class SecureTestClient {
- SecureTestClient(int this.port, String this.name) {
- socket = new SecureSocket(HOST_NAME, port);
- socket.onConnect = this.onConnect;
- socket.onData = () {
- reply = reply.concat(new String.fromCharCodes(socket.read()));
- };
- socket.onClosed = done;
- reply = "";
- }
-
- void onConnect() {
- numRequests++;
- WriteAndClose(socket, "Hello from client $name");
- }
-
- void done() {
- Expect.equals("Welcome, client $name", reply);
- numReplies++;
- if (numReplies == CLIENT_NAMES.length) {
- Expect.equals(numRequests, numReplies);
- EndTest();
- }
- }
-
- static int numRequests = 0;
- static int numReplies = 0;
-
- int port;
- String name;
- SecureSocket socket;
- String reply;
-}
-
-Function EndTest;
-
-const CLIENT_NAMES = const ['able', 'baker', 'camera', 'donut', 'echo'];
-
-void main() {
- ReceivePort keepAlive = new ReceivePort();
- Path scriptDir = new Path(new Options().script).directoryPath;
- Path certificateDatabase = scriptDir.append('pkcert');
- SecureSocket.initialize(database: certificateDatabase.toNativePath(),
- password: 'dartdart',
- useBuiltinRoots: false);
-
- var server = new SecureTestServer();
- int port = server.start();
-
- EndTest = () {
- Expect.equals(CLIENT_NAMES.length, server.numConnections);
- server.stop();
- keepAlive.close();
- };
-
- for (var x in CLIENT_NAMES) {
- new SecureTestClient(port, x);
- }
-}
diff --git a/tests/standalone/io/secure_session_resume_test.dart b/tests/standalone/io/secure_session_resume_test.dart
index 76746eb..b1cf5ebf 100644
--- a/tests/standalone/io/secure_session_resume_test.dart
+++ b/tests/standalone/io/secure_session_resume_test.dart
@@ -1,7 +1,7 @@
-// Copyright (c) 2012, 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.
-
+//
// This test tests TLS session resume, by making multiple client connections
// on the same port to the same server, with a delay of 200 ms between them.
// The unmodified secure_server_test creates all sessions simultaneously,
@@ -10,6 +10,11 @@
//
// Session resume is currently disabled - see issue
// https://code.google.com/p/dart/issues/detail?id=7230
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
import "dart:async";
import "dart:io";
@@ -17,119 +22,54 @@
const SERVER_ADDRESS = "127.0.0.1";
const HOST_NAME = "localhost";
-
-void WriteAndClose(Socket socket, String message) {
- var data = message.charCodes;
- int written = 0;
- void write() {
- written += socket.writeList(data, written, data.length - written);
- if (written < data.length) {
- socket.onWrite = write;
- } else {
- socket.close(true);
- }
- }
- write();
+const CERTIFICATE = "localhost_cert";
+Future<SecureServerSocket> startServer() {
+ return SecureServerSocket.bind(SERVER_ADDRESS,
+ 0,
+ 5,
+ CERTIFICATE).then((server) {
+ server.listen((SecureSocket client) {
+ client.reduce(<int>[], (message, data) => message..addAll(data))
+ .then((message) {
+ String received = new String.fromCharCodes(message);
+ Expect.isTrue(received.contains("Hello from client "));
+ String name = received.substring(received.indexOf("client ") + 7);
+ client.add("Welcome, client $name".charCodes);
+ client.close();
+ });
+ });
+ return server;
+ });
}
-class SecureTestServer {
- void onConnection(Socket connection) {
- connection.onConnect = () {
- numConnections++;
- };
- String received = "";
- connection.onData = () {
- received = received.concat(new String.fromCharCodes(connection.read()));
- };
- connection.onClosed = () {
- Expect.isTrue(received.contains("Hello from client "));
- String name = received.substring(received.indexOf("client ") + 7);
- WriteAndClose(connection, "Welcome, client $name");
- };
- }
-
- void errorHandlerServer(Exception e) {
- Expect.fail("Server socket error $e");
- }
-
- int start() {
- server = new SecureServerSocket(SERVER_ADDRESS, 0, 10, "CN=$HOST_NAME");
- Expect.isNotNull(server);
- server.onConnection = onConnection;
- server.onError = errorHandlerServer;
- return server.port;
- }
-
- void stop() {
- server.close();
- }
-
- int numConnections = 0;
- SecureServerSocket server;
+Future testClient(server, name) {
+ return SecureSocket.connect(HOST_NAME, server.port).then((socket) {
+ socket.add("Hello from client $name".charCodes);
+ socket.close();
+ return socket.reduce(<int>[], (message, data) => message..addAll(data))
+ .then((message) {
+ Expect.listEquals("Welcome, client $name".charCodes, message);
+ return server;
+ });
+ });
}
-class SecureTestClient {
- SecureTestClient(int this.port, String this.name) {
- socket = new SecureSocket(HOST_NAME, port);
- socket.onConnect = this.onConnect;
- socket.onData = () {
- reply = reply.concat(new String.fromCharCodes(socket.read()));
- };
- socket.onClosed = done;
- reply = "";
- }
-
- void onConnect() {
- numRequests++;
- WriteAndClose(socket, "Hello from client $name");
- }
-
- void done() {
- Expect.equals("Welcome, client $name", reply);
- numReplies++;
- if (numReplies == CLIENT_NAMES.length) {
- Expect.equals(numRequests, numReplies);
- EndTest();
- }
- }
-
- static int numRequests = 0;
- static int numReplies = 0;
-
- int port;
- String name;
- SecureSocket socket;
- String reply;
-}
-
-Function EndTest;
-
-const CLIENT_NAMES = const ['able', 'baker'];
-
void main() {
- ReceivePort keepAlive = new ReceivePort();
Path scriptDir = new Path(new Options().script).directoryPath;
Path certificateDatabase = scriptDir.append('pkcert');
SecureSocket.initialize(database: certificateDatabase.toNativePath(),
- password: 'dartdart',
- useBuiltinRoots: false);
-
- var server = new SecureTestServer();
- int port = server.start();
-
- EndTest = () {
- Expect.equals(CLIENT_NAMES.length, server.numConnections);
- server.stop();
- keepAlive.close();
- };
+ password: 'dartdart');
Duration delay = const Duration(milliseconds: 0);
Duration delay_between_connections = const Duration(milliseconds: 300);
- for (var x in CLIENT_NAMES) {
- new Timer(delay, () {
- new SecureTestClient(port, x);
- });
- delay += delay_between_connections;
- }
+ startServer()
+ .then((server) => Future.wait(
+ ['able', 'baker', 'charlie', 'dozen', 'elapse']
+ .map((name) {
+ delay += delay_between_connections;
+ return new Future.delayed(delay, () => server)
+ .then((server) => testClient(server, name));
+ })))
+ .then((servers) => servers.first.close());
}
diff --git a/tests/standalone/io/secure_socket_argument_test.dart b/tests/standalone/io/secure_socket_argument_test.dart
new file mode 100644
index 0000000..7fda4a4
--- /dev/null
+++ b/tests/standalone/io/secure_socket_argument_test.dart
@@ -0,0 +1,12 @@
+// 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 "dart:io";
+
+void main() {
+ Expect.throws(() => SecureSocket.initialize(database: "foo.txt"));
+ Expect.throws(() => SecureSocket.initialize(password: false));
+ Expect.throws(() => SecureSocket.initialize(useBuiltinRoots: 7));
+ SecureSocket.initialize();
+}
diff --git a/tests/standalone/io/secure_socket_bad_certificate_test.dart b/tests/standalone/io/secure_socket_bad_certificate_test.dart
index d53dd93..4e8bc0e 100644
--- a/tests/standalone/io/secure_socket_bad_certificate_test.dart
+++ b/tests/standalone/io/secure_socket_bad_certificate_test.dart
@@ -4,6 +4,8 @@
//
// VMOptions=
// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_write --short_socket_read
// The --short_socket_write option does not work with external server
// www.google.dk. Add this to the test when we have secure server sockets.
// See TODO below.
@@ -12,26 +14,14 @@
import "dart:isolate";
import "dart:io";
-void WriteAndClose(Socket socket, String message) {
- var data = message.charCodes;
- int written = 0;
- void write() {
- written += socket.writeList(data, written, data.length - written);
- if (written < data.length) {
- socket.onWrite = write;
- } else {
- socket.close(true);
- }
- }
- write();
-}
-
void main() {
+ ReceivePort keepAlive = new ReceivePort();
SecureSocket.initialize(useBuiltinRoots: false);
testCertificateCallback(host: "www.google.dk",
acceptCertificate: false).then((_) {
testCertificateCallback(host: "www.google.dk",
acceptCertificate: true).then((_) {
+ keepAlive.close();
// TODO(7153): Open a receive port, and close it when we get here.
// Currently, it can happen that neither onClosed or onError is called.
// So we never reach this point. Diagnose this and fix.
@@ -40,36 +30,33 @@
}
Future testCertificateCallback({String host, bool acceptCertificate}) {
- Completer completer = new Completer();
- var secure = new SecureSocket(host, 443);
- List<String> chunks = <String>[];
- secure.onConnect = () {
- Expect.isTrue(acceptCertificate);
- WriteAndClose(secure, "GET / HTTP/1.0\r\nHost: $host\r\n\r\n");
- };
- secure.onBadCertificate = (_) { };
- secure.onBadCertificate = null;
- Expect.throws(() => secure.onBadCertificate = 7,
- (e) => e is TypeError || e is SocketIOException);
- secure.onBadCertificate = (X509Certificate certificate) {
+ Expect.throws(
+ () {
+ var x = 7;
+ SecureSocket.connect(host, 443, onBadCertificate: x);
+ },
+ (e) => e is ArgumentError || e is TypeError);
+
+ bool badCertificateCallback(X509Certificate certificate) {
Expect.isTrue(certificate.subject.contains("O=Google Inc"));
Expect.isTrue(certificate.startValidity < new DateTime.now());
Expect.isTrue(certificate.endValidity > new DateTime.now());
return acceptCertificate;
};
- secure.onData = () {
- Expect.isTrue(acceptCertificate);
- chunks.add(new String.fromCharCodes(secure.read()));
- };
- secure.onClosed = () {
- Expect.isTrue(acceptCertificate);
- String fullPage = chunks.join();
- Expect.isTrue(fullPage.contains('</body></html>'));
- completer.complete(null);
- };
- secure.onError = (e) {
- Expect.isFalse(acceptCertificate);
- completer.complete(null);
- };
- return completer.future;
+
+ return SecureSocket.connect(host,
+ 443,
+ onBadCertificate: badCertificateCallback)
+ .then((socket) {
+ Expect.isTrue(acceptCertificate);
+ socket.add("GET / HTTP/1.0\r\nHost: $host\r\n\r\n".charCodes);
+ socket.close();
+ return socket.reduce(<int>[], (message, data) => message..addAll(data))
+ .then((message) {
+ String received = new String.fromCharCodes(message);
+ Expect.isTrue(received.contains('</body></html>'));
+ });
+ }).catchError((e) {
+ Expect.isFalse(acceptCertificate);
+ });
}
diff --git a/tests/standalone/io/secure_socket_test.dart b/tests/standalone/io/secure_socket_test.dart
index f010444..fec1115 100644
--- a/tests/standalone/io/secure_socket_test.dart
+++ b/tests/standalone/io/secure_socket_test.dart
@@ -4,57 +4,29 @@
//
// VMOptions=
// VMOptions=--short_socket_read
-// The --short_socket_write option does not work with external server
-// www.google.dk. Add this to the test when we have secure server sockets.
-// See TODO below.
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
import "dart:isolate";
import "dart:io";
-void WriteAndClose(Socket socket, String message) {
- var data = message.charCodes;
- int written = 0;
- void write() {
- written += socket.writeList(data, written, data.length - written);
- if (written < data.length) {
- socket.onWrite = write;
- } else {
- socket.close(true);
- }
- }
- write();
-}
-
void main() {
ReceivePort keepAlive = new ReceivePort();
SecureSocket.initialize();
- // TODO(3593): Use a Dart HTTPS server for this test.
- // When we use a Dart HTTPS server, allow --short_socket_write. The flag
- // causes fragmentation of the client hello message, which doesn't seem to
- // work with www.google.dk.
- var secure = new SecureSocket("www.google.dk", 443);
List<String> chunks = <String>[];
- secure.onConnect = () {
- WriteAndClose(secure, "GET / HTTP/1.0\r\nHost: www.google.dk\r\n\r\n");
- };
- var useReadList; // Mutually recursive onData callbacks.
- void useRead() {
- var data = secure.read();
- var received = new String.fromCharCodes(data);
- chunks.add(received);
- secure.onData = useReadList;
- }
- useReadList = () {
- var buffer = new List.fixedLength(2000);
- int len = secure.readList(buffer, 0, 2000);
- var received = new String.fromCharCodes(buffer.getRange(0, len));
- chunks.add(received);
- secure.onData = useRead;
- };
- secure.onData = useRead;
- secure.onClosed = () {
- String fullPage = chunks.join();
- Expect.isTrue(fullPage.contains('</body></html>'));
- keepAlive.close();
- };
+ SecureSocket.connect("www.google.dk", 443).then((socket) {
+ socket.add("GET / HTTP/1.0\r\nHost: www.google.dk\r\n\r\n".charCodes);
+ socket.close();
+ socket.listen(
+ (List<int> data) {
+ var received = new String.fromCharCodes(data);
+ chunks.add(received);
+ },
+ onDone: () {
+ String fullPage = chunks.join();
+ Expect.isTrue(fullPage.contains('</body></html>'));
+ keepAlive.close();
+ },
+ onError: (e) => Expect.fail("Unexpected error $e"));
+ });
}
diff --git a/tests/standalone/io/secure_stream_test.dart b/tests/standalone/io/secure_stream_test.dart
deleted file mode 100644
index 11acb7b..0000000
--- a/tests/standalone/io/secure_stream_test.dart
+++ /dev/null
@@ -1,36 +0,0 @@
-// 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.
-//
-// VMOptions=
-// VMOptions=--short_socket_read
-// The --short_socket_write option does not work with external server
-// www.google.dk. Add this to the test when we have secure server sockets.
-// See TODO below.
-
-import "dart:isolate";
-import "dart:io";
-
-void main() {
- ReceivePort keepAlive = new ReceivePort();
- SecureSocket.initialize();
- // TODO(3593): Use a Dart HTTPS server for this test.
- // When we use a Dart HTTPS server, allow --short_socket_write. The flag
- // causes fragmentation of the client hello message, which doesn't seem to
- // work with www.google.dk.
- var secure = new SecureSocket("www.google.dk", 443);
- List<String> chunks = <String>[];
- var input = secure.inputStream;
- var output = secure.outputStream;
-
- output.write("GET / HTTP/1.0\r\nHost: www.google.dk\r\n\r\n".charCodes);
- output.close();
- input.onData = () {
- chunks.add(new String.fromCharCodes(input.read()));
- };
- input.onClosed = () {
- String fullPage = chunks.join();
- Expect.isTrue(fullPage.contains('</body></html>'));
- keepAlive.close();
- };
-}
diff --git a/tests/standalone/io/socket_close_test.dart b/tests/standalone/io/socket_close_test.dart
index 3dddf4e..dcb90a2 100644
--- a/tests/standalone/io/socket_close_test.dart
+++ b/tests/standalone/io/socket_close_test.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
//
@@ -41,7 +41,7 @@
void sendData() {
- void dataHandler() {
+ void dataHandler(bytes) {
switch (_mode) {
case 0:
case 1:
@@ -52,8 +52,7 @@
case 4:
case 5:
case 6:
- List<int> b = new List<int>.fixedLength(5);
- _readBytes += _socket.readList(b, 0, 5);
+ _readBytes += bytes.length;
if ((_readBytes % 5) == 0) {
_dataEvents++;
}
@@ -63,23 +62,23 @@
}
}
- void closeHandler() {
+ void closeHandler(socket) {
_closeEvents++;
switch (_mode) {
case 0:
case 1:
- Expect.fail("No close expected");
+ socket.close();
break;
case 2:
case 3:
- _socket.close();
+ socket.close();
proceed();
break;
case 4:
proceed();
break;
case 5:
- _socket.close();
+ socket.close();
proceed();
break;
case 6:
@@ -92,32 +91,28 @@
void errorHandler(Exception e) {
_errorEvents++;
- _socket.close();
+ socket.close();
}
- void connectHandler() {
- _socket.onData = dataHandler;
- _socket.onClosed = closeHandler;
- _socket.onError = errorHandler;
+ void connectHandler(socket) {
+ socket.listen(
+ dataHandler,
+ onDone: () => closeHandler(socket),
+ onError: (error) => errorHandler(socket));
void writeHello() {
- int bytesWritten = 0;
- while (bytesWritten != 5) {
- bytesWritten += _socket.writeList("Hello".charCodes,
- bytesWritten,
- 5 - bytesWritten);
- }
+ socket.add("Hello".charCodes);
}
_iterations++;
switch (_mode) {
case 0:
- _socket.close();
+ socket.destroy();
proceed();
break;
case 1:
writeHello();
- _socket.close();
+ socket.destroy();
proceed();
break;
case 2:
@@ -126,23 +121,21 @@
break;
case 4:
writeHello();
- _socket.close(true);
+ socket.close(); // Half close.
break;
case 5:
writeHello();
break;
case 6:
writeHello();
- _socket.close(true);
+ socket.close(); // Half close.
break;
default:
Expect.fail("Unknown test mode");
}
}
- _socket = new Socket(SocketCloseServer.HOST, _port);
- Expect.equals(true, _socket != null);
- _socket.onConnect = connectHandler;
+ Socket.connect(SocketCloseServer.HOST, _port).then(connectHandler);
}
void initialize() {
@@ -164,7 +157,7 @@
case 0:
case 1:
Expect.equals(0, _dataEvents);
- Expect.equals(0, _closeEvents);
+ Expect.equals(ITERATIONS, _closeEvents);
break;
case 2:
Expect.equals(0, _dataEvents);
@@ -172,6 +165,10 @@
break;
case 3:
case 4:
+ Expect.isTrue(_dataEvents <= ITERATIONS);
+ Expect.isTrue(_dataEvents >= 0);
+ Expect.equals(ITERATIONS, _closeEvents);
+ break;
case 5:
case 6:
Expect.equals(ITERATIONS, _dataEvents);
@@ -186,7 +183,6 @@
int _port;
ReceivePort _receivePort;
SendPort _sendPort;
- Socket _socket;
List<int> _buffer;
int _readBytes;
int _dataEvents;
@@ -219,56 +215,51 @@
void connectionHandler(ConnectionData data) {
var connection = data.connection;
- void readBytes(whenFiveBytes) {
- List<int> b = new List<int>.fixedLength(5);
- data.readBytes += connection.readList(b, 0, 5);
+ void readBytes(bytes, whenFiveBytes) {
+ data.readBytes += bytes.length;
+ Expect.isTrue(data.readBytes <= 5);
if (data.readBytes == 5) {
whenFiveBytes();
}
}
void writeHello() {
- int bytesWritten = 0;
- while (bytesWritten != 5) {
- bytesWritten += connection.writeList("Hello".charCodes,
- bytesWritten,
- 5 - bytesWritten);
- }
+ connection.add("Hello".charCodes);
}
- void dataHandler() {
+ void dataHandler(bytes) {
switch (_mode) {
case 0:
Expect.fail("No data expected");
break;
case 1:
- readBytes(() { _dataEvents++; });
+ readBytes(bytes, () { _dataEvents++; });
break;
case 2:
- readBytes(() {
+ readBytes(bytes, () {
_dataEvents++;
- connection.close();
+ connection.destroy();
});
break;
case 3:
- readBytes(() {
+ readBytes(bytes, () {
_dataEvents++;
writeHello();
- connection.close();
+ connection.destroy();
});
break;
case 4:
- readBytes(() {
+ readBytes(bytes, () {
_dataEvents++;
writeHello();
});
break;
case 5:
case 6:
- readBytes(() {
+ readBytes(bytes, () {
_dataEvents++;
writeHello();
- connection.close(true);
+ connection.close(); // Half close.
});
break;
default:
@@ -281,18 +272,19 @@
connection.close();
}
- void errorHandler(Exception e) {
+ void errorHandler(e) {
Expect.fail("Socket error $e");
}
_iterations++;
- connection.onData = dataHandler;
- connection.onClosed = closeHandler;
- connection.onError = errorHandler;
+ connection.listen(
+ dataHandler,
+ onDone: closeHandler,
+ onError: errorHandler);
}
- void errorHandlerServer(Exception e) {
+ void errorHandlerServer(e) {
Expect.fail("Server socket error");
}
@@ -310,13 +302,14 @@
Expect.equals(ITERATIONS, _closeEvents);
break;
case 1:
- Expect.equals(ITERATIONS, _dataEvents);
+ Expect.isTrue(_dataEvents <= ITERATIONS);
+ Expect.isTrue(_dataEvents >= 0);
Expect.equals(ITERATIONS, _closeEvents);
break;
case 2:
case 3:
Expect.equals(ITERATIONS, _dataEvents);
- Expect.equals(0, _closeEvents);
+ Expect.equals(ITERATIONS, _closeEvents);
break;
case 4:
case 5:
@@ -345,14 +338,17 @@
_closeEvents = 0;
_iterations = 0;
_mode = message;
- _server = new ServerSocket(HOST, 0, 10);
- Expect.equals(true, _server != null);
- _server.onConnection = (connection) {
- var data = new ConnectionData(connection);
- connectionHandler(data);
- };
- _server.onError = errorHandlerServer;
- replyTo.send(_server.port, null);
+ ServerSocket.bind().then((server) {
+ _server = server;
+ _server.listen(
+ (socket) {
+ var data = new ConnectionData(socket);
+ connectionHandler(data);
+ },
+ onError: errorHandlerServer
+ );
+ replyTo.send(_server.port, null);
+ });
} else {
Timer.run(waitForResult);
}
@@ -372,10 +368,10 @@
main() {
// Run the close test in these different "modes".
// 0: Client closes without sending at all.
- // 1: Client sends and closes.
- // 2: Client sends. Server closes.
- // 3: Client sends. Server responds and closes.
- // 4: Client sends and half-closes. Server responds and closes.
+ // 1: Client sends and destroys.
+ // 2: Client sends. Server destroys.
+ // 3: Client sends. Server responds and destroys.
+ // 4: Client sends and half-closes. Server responds and destroys.
// 5: Client sends. Server responds and half closes.
// 6: Client sends and half-closes. Server responds and half closes.
var tests = 7;
diff --git a/tests/standalone/io/socket_exception_test.dart b/tests/standalone/io/socket_exception_test.dart
index fe5399f..7624ac7 100644
--- a/tests/standalone/io/socket_exception_test.dart
+++ b/tests/standalone/io/socket_exception_test.dart
@@ -1,180 +1,213 @@
-// Copyright (c) 2012, 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.
//
// Tests socket exceptions.
+import "dart:async";
import "dart:isolate";
import "dart:io";
class SocketExceptionTest {
- static const PORT = 0;
- static const HOST = "127.0.0.1";
-
static void serverSocketExceptionTest() {
bool exceptionCaught = false;
bool wrongExceptionCaught = false;
- ServerSocket server = new ServerSocket(HOST, PORT, 10);
- Expect.equals(true, server != null);
- server.close();
- try {
+ ServerSocket.bind().then((server) {
+ Expect.isNotNull(server);
server.close();
- } on SocketIOException catch(ex) {
- exceptionCaught = true;
- } on Exception catch (ex) {
- wrongExceptionCaught = true;
- }
- Expect.equals(false, exceptionCaught);
- Expect.equals(true, !wrongExceptionCaught);
+ try {
+ server.close();
+ } on SocketIOException catch(ex) {
+ exceptionCaught = true;
+ } catch (ex) {
+ wrongExceptionCaught = true;
+ }
+ Expect.equals(false, exceptionCaught);
+ Expect.equals(true, !wrongExceptionCaught);
- // Test invalid host.
- Expect.throws(() => new ServerSocket("__INVALID_HOST__", PORT, 10),
- (e) => e is SocketIOException);
+ // Test invalid host.
+ ServerSocket.bind("__INVALID_HOST__")
+ .then((server) { })
+ .catchError((e) => e is SocketIOException);
+ });
+ }
+
+ static void serverSocketCloseListenTest() {
+ var port = new ReceivePort();
+ ServerSocket.bind().then((server) {
+ Socket.connect("127.0.0.1", server.port).then((socket) {
+ server.close();
+ server.listen(
+ (incoming) => Expect.fail("Unexpected socket"),
+ onDone: port.close);
+ });
+ });
+ }
+
+ static void serverSocketListenCloseTest() {
+ var port = new ReceivePort();
+ ServerSocket.bind().then((server) {
+ Socket.connect("127.0.0.1", server.port).then((socket) {
+ server.listen(
+ (incoming) => server.close(),
+ onDone: port.close());
+ });
+ });
}
static void clientSocketExceptionTest() {
bool exceptionCaught = false;
bool wrongExceptionCaught = false;
- ServerSocket server = new ServerSocket(HOST, PORT, 10);
- Expect.equals(true, server != null);
- int port = server.port;
- Socket client = new Socket(HOST, port);
- client.onConnect = () {
- Expect.equals(true, client != null);
- InputStream input = client.inputStream;
- OutputStream output = client.outputStream;
- client.close();
- try {
+ ServerSocket.bind().then((server) {
+ Expect.isNotNull(server);
+ int port = server.port;
+ Socket.connect("127.0.0.1", port).then((client) {
+ Expect.isNotNull(client);
client.close();
- } on SocketIOException catch(ex) {
- exceptionCaught = true;
- } on Exception catch (ex) {
- wrongExceptionCaught = true;
- }
- Expect.equals(false, exceptionCaught);
- Expect.equals(true, !wrongExceptionCaught);
- exceptionCaught = false;
- try {
- client.available();
- } on SocketIOException catch(ex) {
- exceptionCaught = true;
- } on Exception catch (ex) {
- wrongExceptionCaught = true;
- }
- Expect.equals(true, exceptionCaught);
- Expect.equals(true, !wrongExceptionCaught);
- exceptionCaught = false;
- try {
- List<int> buffer = new List<int>.fixedLength(10);
- client.readList(buffer, 0 , 10);
- } on SocketIOException catch(ex) {
- exceptionCaught = true;
- } on Exception catch (ex) {
- wrongExceptionCaught = true;
- }
- Expect.equals(true, exceptionCaught);
- Expect.equals(true, !wrongExceptionCaught);
- exceptionCaught = false;
- try {
- List<int> buffer = new List<int>.fixedLength(10);
- client.writeList(buffer, 0, 10);
- } on SocketIOException catch(ex) {
- exceptionCaught = true;
- } on Exception catch (ex) {
- wrongExceptionCaught = true;
- }
- Expect.equals(true, exceptionCaught);
- Expect.equals(true, !wrongExceptionCaught);
- exceptionCaught = false;
- try {
- List<int> buffer = new List<int>.fixedLength(42);
- input.readInto(buffer, 0, 12);
- } on SocketIOException catch(ex) {
- exceptionCaught = true;
- } on Exception catch (ex) {
- wrongExceptionCaught = true;
- }
- Expect.equals(true, exceptionCaught);
- Expect.equals(true, !wrongExceptionCaught);
- exceptionCaught = false;
- try {
- List<int> buffer = new List<int>.fixedLength(42);
- output.writeFrom(buffer, 0, 12);
- } on SocketIOException catch(ex) {
- exceptionCaught = true;
- } on Exception catch (ex) {
- wrongExceptionCaught = true;
- }
- Expect.equals(true, exceptionCaught);
- Expect.equals(true, !wrongExceptionCaught);
+ try {
+ client.close();
+ } on SocketIOException catch(ex) {
+ exceptionCaught = true;
+ } catch (ex) {
+ wrongExceptionCaught = true;
+ }
+ Expect.isFalse(exceptionCaught);
+ Expect.isFalse(wrongExceptionCaught);
+ try {
+ client.destroy();
+ } on SocketIOException catch(ex) {
+ exceptionCaught = true;
+ } catch (ex) {
+ print(ex);
+ wrongExceptionCaught = true;
+ }
+ Expect.isFalse(exceptionCaught);
+ Expect.isFalse(wrongExceptionCaught);
+ try {
+ List<int> buffer = new List<int>.fixedLength(10);
+ client.add(buffer);
+ } on StateError catch (ex) {
+ exceptionCaught = true;
+ } catch (ex) {
+ wrongExceptionCaught = true;
+ }
+ Expect.isTrue(exceptionCaught);
+ Expect.isFalse(wrongExceptionCaught);
- server.close();
- };
+ server.close();
+ });
+ });
}
- static void indexOutOfRangeExceptionTest() {
- bool exceptionCaught = false;
- bool wrongExceptionCaught = false;
+ static void clientSocketDestroyNoErrorTest() {
+ ServerSocket.bind().then((server) {
+ server.listen((socket) {
+ socket.pipe(socket);
+ });
+ Socket.connect("127.0.0.1", server.port).then((client) {
+ client.listen((data) {}, onDone: server.close);
+ client.destroy();
+ });
+ });
+ }
- ServerSocket server = new ServerSocket(HOST, PORT, 10);
- Expect.equals(true, server != null);
- int port = server.port;
- Socket client = new Socket(HOST, port);
- client.onConnect = () {
- Expect.equals(true, client != null);
- try {
- List<int> buffer = new List<int>.fixedLength(10);
- client.readList(buffer, -1, 1);
- } on RangeError catch (ex) {
- exceptionCaught = true;
- } on Exception catch (ex) {
- wrongExceptionCaught = true;
- }
- Expect.equals(true, exceptionCaught);
- Expect.equals(true, !wrongExceptionCaught);
- exceptionCaught = false;
+ static void clientSocketAddDestroyNoErrorTest() {
+ ServerSocket.bind().then((server) {
+ server.listen((socket) {
+ // Passive block data by not sobscribing to socket.
+ });
+ Socket.connect("127.0.0.1", server.port).then((client) {
+ client.listen((data) {}, onDone: server.close);
+ client.add(new List.fixedLength(1024 * 1024, fill: 0));
+ client.destroy();
+ });
+ });
+ }
- try {
- List<int> buffer = new List<int>.fixedLength(10);
- client.readList(buffer, 0, -1);
- } on RangeError catch (ex) {
- exceptionCaught = true;
- } on Exception catch (ex) {
- wrongExceptionCaught = true;
- }
- Expect.equals(true, exceptionCaught);
- Expect.equals(true, !wrongExceptionCaught);
- exceptionCaught = false;
+ static void clientSocketAddCloseNoErrorTest() {
+ ServerSocket.bind().then((server) {
+ var completer = new Completer();
+ server.listen((socket) {
+ // The socket is 'paused' until the future completes.
+ completer.future.then((_) => socket.pipe(socket));
+ });
+ Socket.connect("127.0.0.1", server.port).then((client) {
+ const int SIZE = 1024 * 1024;
+ int count = 0;
+ client.listen(
+ (data) => count += data.length,
+ onDone: () {
+ Expect.equals(SIZE, count);
+ server.close();
+ });
+ client.add(new List.fixedLength(SIZE, fill: 0));
+ client.close();
+ // Start piping now.
+ completer.complete(null);
+ });
+ });
+ }
- try {
- List<int> buffer = new List<int>.fixedLength(10);
- client.writeList(buffer, -1, 1);
- } on RangeError catch (ex) {
- exceptionCaught = true;
- } on Exception catch (ex) {
- wrongExceptionCaught = true;
- }
- Expect.equals(true, exceptionCaught);
- Expect.equals(true, !wrongExceptionCaught);
- exceptionCaught = false;
+ static void clientSocketAddCloseErrorTest() {
+ ServerSocket.bind().then((server) {
+ var completer = new Completer();
+ server.listen((socket) {
+ completer.future.then((_) => socket.destroy());
+ });
+ Socket.connect("127.0.0.1", server.port).then((client) {
+ const int SIZE = 1024 * 1024;
+ int errors = 0;
+ client.listen(
+ (data) => Expect.fail("Unexpected data"),
+ onError: (error) {
+ Expect.isTrue(error.error is SocketIOException);
+ errors++;
+ },
+ onDone: () {
+ // We get either a close or an error followed by a close
+ // on the socket. Whether we get both depends on
+ // whether the system notices the error for the read
+ // event or only for the write event.
+ Expect.isTrue(errors <= 1);
+ server.close();
+ });
+ client.add(new List.fixedLength(SIZE, fill: 0));
+ // Destroy other socket now.
+ completer.complete(null);
+ var port = new ReceivePort();
+ client.done.then(
+ (_) {
+ Expect.fail("Expected error");
+ },
+ onError: (error) {
+ Expect.isTrue(error.error is SocketIOException);
+ port.close();
+ });
+ });
+ });
+ }
- try {
- List<int> buffer = new List<int>.fixedLength(10);
- client.writeList(buffer, 0, -1);
- } on RangeError catch (ex) {
- exceptionCaught = true;
- } on Exception catch (ex) {
- wrongExceptionCaught = true;
- }
- Expect.equals(true, exceptionCaught);
- Expect.equals(true, !wrongExceptionCaught);
-
- server.close();
- client.close();
- };
+ static void clientSocketAddCloseResultErrorTest() {
+ ServerSocket.bind().then((server) {
+ var completer = new Completer();
+ server.listen((socket) {
+ completer.future.then((_) => socket.destroy());
+ });
+ Socket.connect("127.0.0.1", server.port).then((client) {
+ const int SIZE = 1024 * 1024;
+ int errors = 0;
+ client.add(new List.fixedLength(SIZE, fill: 0));
+ client.close();
+ client.done.catchError((error) {
+ server.close();
+ });
+ // Destroy other socket now.
+ completer.complete(null);
+ });
+ });
}
static void unknownHostTest() {
@@ -182,15 +215,22 @@
var port = new ReceivePort();
port.receive((message, replyTo) => null);
- Socket s = new Socket("hede.hule.hest", 1234);
- s.onError = (e) => port.close();
- s.onConnect = () => Expect.fail("Connection completed");
+ Socket.connect("hede.hule.hest", 1234)
+ .then((socket) => Expect.fail("Connection completed"))
+ .catchError((e) => port.close(), test: (e) => e is SocketIOException);
+
}
static void testMain() {
serverSocketExceptionTest();
+ serverSocketCloseListenTest();
+ serverSocketListenCloseTest();
clientSocketExceptionTest();
- indexOutOfRangeExceptionTest();
+ clientSocketDestroyNoErrorTest();
+ clientSocketAddDestroyNoErrorTest();
+ clientSocketAddCloseNoErrorTest();
+ clientSocketAddCloseErrorTest();
+ clientSocketAddCloseResultErrorTest();
unknownHostTest();
}
}
diff --git a/tests/standalone/io/socket_info_test.dart b/tests/standalone/io/socket_info_test.dart
index e35eab6..f3b675f 100644
--- a/tests/standalone/io/socket_info_test.dart
+++ b/tests/standalone/io/socket_info_test.dart
@@ -1,23 +1,24 @@
-// Copyright (c) 2012, 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.
import "dart:io";
void testHostAndPort() {
- ServerSocket server = new ServerSocket("127.0.0.1", 0, 5);
+ ServerSocket.bind().then((server) {
- Socket clientSocket = new Socket("127.0.0.1", server.port);
+ Socket.connect("127.0.0.1", server.port).then((clientSocket) {
+ server.listen((socket) {
+ Expect.equals(socket.port, server.port);
+ Expect.equals(clientSocket.port, socket.remotePort);
+ Expect.equals(clientSocket.remotePort, socket.port);
+ Expect.equals(socket.remoteHost, "127.0.0.1");
+ Expect.equals(clientSocket.remoteHost, "127.0.0.1");
- server.onConnection = (Socket socket) {
- Expect.equals(socket.port, server.port);
- Expect.equals(clientSocket.port, socket.remotePort);
- Expect.equals(clientSocket.remotePort, socket.port);
- Expect.equals(socket.remoteHost, "127.0.0.1");
- Expect.equals(clientSocket.remoteHost, "127.0.0.1");
-
- server.close();
- };
+ server.close();
+ });
+ });
+ });
}
void main() {
diff --git a/tests/standalone/io/socket_invalid_arguments_test.dart b/tests/standalone/io/socket_invalid_arguments_test.dart
index 91108b4..7505aaa 100644
--- a/tests/standalone/io/socket_invalid_arguments_test.dart
+++ b/tests/standalone/io/socket_invalid_arguments_test.dart
@@ -1,8 +1,9 @@
-// Copyright (c) 2012, 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.
import "dart:io";
+import "dart:isolate";
class NotAnInteger {
operator==(other) => other == 1;
@@ -16,39 +17,41 @@
}
testSocketCreation(host, port) {
- var s = new Socket(host, port);
- s.onError = (e) => null;
- s.onConnect = () => Expect.fail("Shouldn't get connected");
+ Socket.connect(host, port)
+ .then((socket) => Expect.fail("Shouldn't get connected"))
+ .catchError((e) => null, test: (e) => e is SocketIOException)
+ .catchError((e) => null, test: (e) => e is ArgumentError);
}
-testReadList(buffer, offset, length) {
- var server = new ServerSocket("127.0.0.1", 0, 5);
- var s = new Socket("127.0.0.1", server.port);
- s.onConnect = () {
- try { s.readList(buffer, offset, length); } catch (e) {}
- s.close();
- };
- s.onError = (e) => null;
-}
-
-testWriteList(buffer, offset, length) {
- var server = new ServerSocket("127.0.0.1", 0, 5);
- var s = new Socket("127.0.0.1", server.port);
- s.onConnect = () {
- try { s.writeList(buffer, offset, length); } catch (e) {}
- s.close();
- };
- s.onError = (e) => null;
+testAdd(buffer) {
+ ServerSocket.bind("127.0.0.1", 0, 5).then((server) {
+ server.listen((socket) => socket.destroy());
+ Socket.connect("127.0.0.1", server.port).then((socket) {
+ int errors = 0;
+ socket.done.catchError((e) { errors++; });
+ socket.listen(
+ (_) { },
+ onError: (error) {
+ Expect.fail("Error on stream");
+ },
+ onDone: () {
+ Expect.equals(1, errors);
+ socket.destroy();
+ server.close();
+ });
+ socket.add(buffer);
+ });
+ });
}
testServerSocketCreation(address, port, backlog) {
var server;
+ var port = new ReceivePort();
try {
- server = new ServerSocket(address, port, backlog);
- server.onError = (e) => null;
- server.onConnection = (c) => Expect.fail("Shouldn't get connection");
+ ServerSocket.bind(address, port, backlog)
+ .then((_) { Expect.fail("ServerSocket bound"); });
} catch (e) {
- // ignore
+ port.close();
}
}
@@ -56,15 +59,14 @@
testSocketCreation(123, 123);
testSocketCreation("string", null);
testSocketCreation(null, null);
- testReadList(null, 123, 123);
- testReadList(new NotAList(), 1, 1);
- testReadList([1, 2, 3], new NotAnInteger(), new NotAnInteger());
- testReadList([1, 2, 3], 1, new NotAnInteger());
- testWriteList(null, 123, 123);
- testWriteList(new NotAList(), 1, 1);
- testWriteList([1, 2, 3], new NotAnInteger(), new NotAnInteger());
- testWriteList([1, 2, 3], 1, new NotAnInteger());
- testWriteList([1, 2, 3], new NotAnInteger(), 1);
+ testAdd(null);
+ testAdd(new NotAList());
+ testAdd(42);
+ // TODO(8233): Throw ArgumentError from API implementation.
+ // testAdd([-1]);
+ // testAdd([2222222222222222222222222222222]);
+ // testAdd([1, 2, 3, null]);
+ // testAdd([new NotAnInteger()]);
testServerSocketCreation(123, 123, 123);
testServerSocketCreation("string", null, null);
testServerSocketCreation("string", 123, null);
diff --git a/tests/standalone/io/socket_many_connections_test.dart b/tests/standalone/io/socket_many_connections_test.dart
index 3aabf60..c313fe1 100644
--- a/tests/standalone/io/socket_many_connections_test.dart
+++ b/tests/standalone/io/socket_many_connections_test.dart
@@ -28,19 +28,18 @@
_connections++;
if (_connections == CONNECTIONS) {
for (int i = 0; i < CONNECTIONS; i++) {
- _sockets[i].close();
+ _sockets[i].destroy();
}
- shutdown();
+ close();
}
}
for (int i = 0; i < CONNECTIONS; i++) {
- _sockets[i] = new Socket(TestingServer.HOST, _port);
- if (_sockets[i] != null) {
- _sockets[i].onConnect = connectHandler;
- } else {
- Expect.fail("socket creation failed");
- }
+ Socket.connect(TestingServer.HOST, _port).then((socket) {
+ Expect.isNotNull(socket);
+ _sockets[i] = socket;
+ connectHandler();
+ });
}
}
@@ -52,7 +51,7 @@
_sendPort.send(TestingServer.INIT, _receivePort.toSendPort());
}
- void shutdown() {
+ void close() {
_sendPort.send(TestingServer.SHUTDOWN, _receivePort.toSendPort());
_receivePort.close();
}
@@ -79,14 +78,16 @@
connection.close();
}
- void errorHandler(Exception e) {
+ void errorHandler(e) {
print("Socket error $e");
connection.close();
}
_connections++;
- connection.onClosed = closeHandler;
- connection.onError = errorHandler;
+ connection.listen(
+ (data) {},
+ onDone: closeHandler,
+ onError: errorHandler);
}
int _connections = 0;
diff --git a/tests/standalone/io/socket_port_test.dart b/tests/standalone/io/socket_port_test.dart
index 841e1444..009a522 100644
--- a/tests/standalone/io/socket_port_test.dart
+++ b/tests/standalone/io/socket_port_test.dart
@@ -1,21 +1,20 @@
-// Copyright (c) 2012, 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.
import "dart:io";
void testPort() {
- ServerSocket server = new ServerSocket("127.0.0.1", 0, 5);
-
- Socket clientSocket = new Socket("127.0.0.1", server.port);
-
- server.onConnection = (Socket socket) {
- Expect.equals(socket.port, server.port);
- Expect.equals(clientSocket.port, socket.remotePort);
- Expect.equals(clientSocket.remotePort, socket.port);
-
- server.close();
- };
+ ServerSocket.bind().then((server) {
+ Socket.connect("127.0.0.1", server.port).then((clientSocket) {
+ server.listen((Socket socket) {
+ Expect.equals(socket.port, server.port);
+ Expect.equals(clientSocket.port, socket.remotePort);
+ Expect.equals(clientSocket.remotePort, socket.port);
+ server.close();
+ });
+ });
+ });
}
void main() {
diff --git a/tests/standalone/io/socket_stream_close_test.dart b/tests/standalone/io/socket_stream_close_test.dart
deleted file mode 100644
index 4082c81..0000000
--- a/tests/standalone/io/socket_stream_close_test.dart
+++ /dev/null
@@ -1,418 +0,0 @@
-// 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.
-//
-// VMOptions=
-// VMOptions=--short_socket_read
-// VMOptions=--short_socket_write
-// VMOptions=--short_socket_read --short_socket_write
-//
-// Test socket close events.
-
-import 'dart:async';
-import 'dart:io';
-import 'dart:isolate';
-
-const SERVERSHUTDOWN = -1;
-const ITERATIONS = 10;
-
-
-class SocketClose {
-
- SocketClose.start(this._mode, this._donePort)
- : _receivePort = new ReceivePort(),
- _sendPort = null,
- _readBytes = 0,
- _dataEvents = 0,
- _closeEvents = 0,
- _errorEvents = 0,
- _iterations = 0 {
- _sendPort = spawnFunction(startSocketCloseServer);
- initialize();
- }
-
- void proceed() {
- if (_iterations < ITERATIONS) {
- Timer.run(sendData);
- } else {
- shutdown();
- }
- }
-
- void sendData() {
-
- void dataHandler() {
- switch (_mode) {
- case 0:
- case 1:
- case 2:
- Expect.fail("No data expected");
- break;
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- case 8:
- var read = _socket.inputStream.read();
- _readBytes += read.length;
- if ((_readBytes % 5) == 0) {
- _dataEvents++;
- }
- break;
- default:
- Expect.fail("Unknown test mode");
- }
- }
-
- void closeHandler() {
- _closeEvents++;
- switch (_mode) {
- case 0:
- case 1:
- break;
- case 2:
- case 3:
- case 4:
- _socket.outputStream.close();
- proceed();
- break;
- case 5:
- proceed();
- break;
- case 6:
- _socket.outputStream.close();
- proceed();
- break;
- case 7:
- case 8:
- proceed();
- break;
- default:
- Expect.fail("Unknown test mode");
- }
- }
-
- void errorHandler(Exception e) {
- _errorEvents++;
- _socket.close();
- }
-
- void connectHandler() {
- _socket.inputStream.onData = dataHandler;
- _socket.inputStream.onClosed = closeHandler;
- _socket.onError = errorHandler;
-
- _iterations++;
- switch (_mode) {
- case 0:
- _socket.inputStream.close();
- proceed();
- break;
- case 1:
- _socket.outputStream.write("Hello".charCodes);
- _socket.outputStream.onNoPendingWrites = () {
- _socket.inputStream.close();
- proceed();
- };
- break;
- case 2:
- case 3:
- case 4:
- _socket.outputStream.write("Hello".charCodes);
- break;
- case 5:
- _socket.outputStream.write("Hello".charCodes);
- _socket.outputStream.onNoPendingWrites = () {
- _socket.outputStream.close();
- };
- break;
- case 6:
- _socket.outputStream.write("Hello".charCodes);
- break;
- case 7:
- case 8:
- _socket.outputStream.write("Hello".charCodes);
- _socket.outputStream.onNoPendingWrites = () {
- _socket.outputStream.close();
- };
- break;
- default:
- Expect.fail("Unknown test mode");
- }
- }
-
- _socket = new Socket(SocketCloseServer.HOST, _port);
- Expect.equals(true, _socket != null);
- _socket.onConnect = connectHandler;
- }
-
- void initialize() {
- _receivePort.receive((var message, SendPort replyTo) {
- _port = message;
- proceed();
- });
- _sendPort.send(_mode, _receivePort.toSendPort());
- }
-
- void shutdown() {
- _sendPort.send(SERVERSHUTDOWN, _receivePort.toSendPort());
- _receivePort.receive((message, ignore) {
- _donePort.send(null);
- _receivePort.close();
- });
-
- switch (_mode) {
- case 0:
- case 1:
- Expect.equals(0, _dataEvents);
- Expect.equals(0, _closeEvents);
- break;
- case 2:
- Expect.equals(0, _dataEvents);
- Expect.equals(ITERATIONS, _closeEvents);
- break;
- case 3:
- Expect.equals(ITERATIONS, _dataEvents);
- Expect.equals(ITERATIONS, _closeEvents);
- break;
- case 4:
- Expect.equals(ITERATIONS, _closeEvents);
- break;
- case 5:
- case 6:
- case 7:
- case 8:
- Expect.equals(ITERATIONS, _dataEvents);
- Expect.equals(ITERATIONS, _closeEvents);
- break;
- default:
- Expect.fail("Unknown test mode");
- }
- Expect.equals(0, _errorEvents);
- }
-
- int _port;
- ReceivePort _receivePort;
- SendPort _sendPort;
- Socket _socket;
- List<int> _buffer;
- int _readBytes;
- int _dataEvents;
- int _closeEvents;
- int _errorEvents;
- int _iterations;
- int _mode;
- SendPort _donePort;
-}
-
-
-class ConnectionData {
- ConnectionData(Socket this.connection) : readBytes = 0;
- Socket connection;
- int readBytes;
-}
-
-
-void startSocketCloseServer() {
- var server = new SocketCloseServer();
- port.receive(server.dispatch);
-}
-
-
-class SocketCloseServer {
-
- static const HOST = "127.0.0.1";
-
- SocketCloseServer() : super() {}
-
- void connectionHandler(ConnectionData data) {
- var connection = data.connection;
-
- void readBytes(whenFiveBytes) {
- var read = connection.inputStream.read();
- data.readBytes += read.length;
- if (data.readBytes == 5) {
- whenFiveBytes();
- }
- }
-
- void dataHandler() {
- switch (_mode) {
- case 0:
- Expect.fail("No data expected");
- break;
- case 1:
- readBytes(() {
- _dataEvents++;
- });
- break;
- case 2:
- readBytes(() {
- _dataEvents++;
- connection.inputStream.close();
- });
- break;
- case 3:
- readBytes(() {
- _dataEvents++;
- connection.outputStream.write("Hello".charCodes);
- connection.outputStream.onNoPendingWrites = () {
- connection.inputStream.close();
- };
- });
- break;
- case 4:
- readBytes(() {
- _dataEvents++;
- connection.outputStream.write("Hello".charCodes);
- connection.inputStream.close();
- });
- break;
- case 5:
- readBytes(() {
- _dataEvents++;
- connection.outputStream.write("Hello".charCodes);
- });
- break;
- case 6:
- case 7:
- readBytes(() {
- _dataEvents++;
- connection.outputStream.write("Hello".charCodes);
- connection.outputStream.onNoPendingWrites = () {
- connection.outputStream.close();
- };
- });
- break;
- case 8:
- readBytes(() {
- _dataEvents++;
- connection.outputStream.write("Hello".charCodes);
- connection.outputStream.close();
- });
- break;
- default:
- Expect.fail("Unknown test mode");
- }
- }
-
- void closeHandler() {
- _closeEvents++;
- connection.outputStream.close();
- }
-
- void errorHandler(Exception e) {
- Expect.fail("Socket error $e");
- }
-
- _iterations++;
-
- connection.inputStream.onData = dataHandler;
- connection.inputStream.onClosed = closeHandler;
- connection.onError = errorHandler;
- }
-
- void errorHandlerServer(Exception e) {
- Expect.fail("Server socket error");
- }
-
- waitForResult() {
- // Make sure all iterations have been run. In multiple of these
- // scenarios it is possible to get the SERVERSHUTDOWN message
- // before we have received the last close event on the
- // server. In these cases we wait for the correct number of
- // close events.
- if (_iterations == ITERATIONS &&
- (_closeEvents == ITERATIONS ||
- (_mode == 2 || _mode == 3 || _mode == 4))) {
- switch (_mode) {
- case 0:
- Expect.equals(0, _dataEvents);
- Expect.equals(ITERATIONS, _closeEvents);
- break;
- case 1:
- Expect.equals(ITERATIONS, _dataEvents);
- Expect.equals(ITERATIONS, _closeEvents);
- break;
- case 2:
- case 3:
- case 4:
- Expect.equals(ITERATIONS, _dataEvents);
- Expect.equals(0, _closeEvents);
- break;
- case 5:
- case 6:
- case 7:
- case 8:
- Expect.equals(ITERATIONS, _dataEvents);
- Expect.equals(ITERATIONS, _closeEvents);
- break;
- default:
- Expect.fail("Unknown test mode");
- }
- Expect.equals(0, _errorEvents);
- _server.close();
- port.close();
- _donePort.send(null);
- } else {
- new Timer(new Duration(milliseconds: 100), waitForResult);
- }
- }
-
- void dispatch(message, replyTo) {
- _donePort = replyTo;
- if (message != SERVERSHUTDOWN) {
- _readBytes = 0;
- _errorEvents = 0;
- _dataEvents = 0;
- _closeEvents = 0;
- _iterations = 0;
- _mode = message;
- _server = new ServerSocket(HOST, 0, 10);
- Expect.equals(true, _server != null);
- _server.onConnection = (connection) {
- var data = new ConnectionData(connection);
- connectionHandler(data);
- };
- _server.onError = errorHandlerServer;
- replyTo.send(_server.port, null);
- } else {
- Timer.run(waitForResult);
- }
- }
-
- ServerSocket _server;
- SendPort _donePort;
- int _readBytes;
- int _errorEvents;
- int _dataEvents;
- int _closeEvents;
- int _iterations;
- int _mode;
-}
-
-
-main() {
- // Run the close test in these different "modes".
- // 0: Client closes without sending at all.
- // 1: Client sends and closes.
- // 2: Client sends. Server closes.
- // 3: Client sends. Server responds and closes.
- // 4: Client sends. Server responds and closes without waiting for everything
- // being sent.
- // 5: Client sends and half-closes. Server responds and closes.
- // 6: Client sends. Server responds and half closes.
- // 7: Client sends and half-closes. Server responds and half closes.
- // 8: Client sends and half-closes. Server responds and half closes without
- // explicitly waiting for everything being sent.
- var tests = 9;
- var port = new ReceivePort();
- var completed = 0;
- port.receive((message, ignore) {
- if (++completed == tests) port.close();
- });
- for (var i = 0; i < tests; i++) {
- new SocketClose.start(i, port.toSendPort());
- }
-}
diff --git a/tests/standalone/io/socket_test.dart b/tests/standalone/io/socket_test.dart
new file mode 100644
index 0000000..6ce7a75
--- /dev/null
+++ b/tests/standalone/io/socket_test.dart
@@ -0,0 +1,230 @@
+// 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.
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
+
+import "dart:async";
+import "dart:io";
+import "dart:isolate";
+
+void testArguments() {
+ Expect.throws(() => ServerSocket.bind("127.0.0.1", 65536));
+ Expect.throws(() => ServerSocket.bind("127.0.0.1", -1));
+ Expect.throws(() => ServerSocket.bind("127.0.0.1", 0, -1));
+}
+
+void testSimpleBind() {
+ ReceivePort port = new ReceivePort();
+ ServerSocket.bind().then((s) {
+ Expect.isTrue(s.port > 0);
+ port.close();
+ });
+}
+
+void testInvalidBind() {
+ int count = 0;
+ ReceivePort port = new ReceivePort();
+ port.receive((_, __) { count++; if (count == 3) port.close(); });
+
+ // Bind to a unknown DNS name.
+ ServerSocket.bind("ko.faar.__hest__")
+ .then((_) { Expect.fail("Failure expected"); } )
+ .catchError((e) {
+ Expect.isTrue(e.error is SocketIOException);
+ port.toSendPort().send(1);
+ });
+
+ // Bind to an unavaliable IP-address.
+ ServerSocket.bind("8.8.8.8")
+ .then((_) { Expect.fail("Failure expected"); } )
+ .catchError((e) {
+ Expect.isTrue(e.error is SocketIOException);
+ port.toSendPort().send(1);
+ });
+
+ // Bind to a port already in use.
+ // Either an error or a successful bind is allowed.
+ // Windows platforms allow multiple binding to the same socket, with
+ // unpredictable results.
+ ServerSocket.bind("127.0.0.1")
+ .then((s) {
+ ServerSocket.bind("127.0.0.1", s.port)
+ .then((t) {
+ Expect.equals('windows', Platform.operatingSystem);
+ Expect.equals(s.port, t.port);
+ port.toSendPort().send(1);
+ })
+ .catchError((e) {
+ Expect.notEquals('windows', Platform.operatingSystem);
+ Expect.isTrue(e.error is SocketIOException);
+ port.toSendPort().send(1);
+ });
+ });
+}
+
+void testConnectNoDestroy() {
+ ReceivePort port = new ReceivePort();
+ ServerSocket.bind().then((server) {
+ server.listen((_) { });
+ Socket.connect("127.0.0.1", server.port).then((_) {
+ server.close();
+ port.close();
+ });
+ });
+}
+
+void testConnectImmediateDestroy() {
+ ReceivePort port = new ReceivePort();
+ ServerSocket.bind().then((server) {
+ server.listen((_) { });
+ Socket.connect("127.0.0.1", server.port).then((socket) {
+ socket.destroy();
+ server.close();
+ port.close();
+ });
+ });
+}
+
+void testConnectConsumerClose() {
+ // Connect socket then immediate close the consumer without
+ // listening on the stream.
+ ReceivePort port = new ReceivePort();
+ ServerSocket.bind().then((server) {
+ server.listen((_) { });
+ Socket.connect("127.0.0.1", server.port).then((socket) {
+ socket.close();
+ socket.done.then((_) {
+ socket.destroy();
+ server.close();
+ port.close();
+ });
+ });
+ });
+}
+
+void testConnectConsumerWriteClose() {
+ // Connect socket write some data immediate close the consumer
+ // without listening on the stream.
+ ReceivePort port = new ReceivePort();
+ ServerSocket.bind().then((server) {
+ server.listen((_) { });
+ Socket.connect("127.0.0.1", server.port).then((socket) {
+ socket.add([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ socket.close();
+ socket.done.then((_) {
+ socket.destroy();
+ server.close();
+ port.close();
+ });
+ });
+ });
+}
+
+void testConnectStreamClose() {
+ // Connect socket and listen on the stream. The server closes
+ // immediately so only a done event is received.
+ ReceivePort port = new ReceivePort();
+ ServerSocket.bind().then((server) {
+ server.listen((client) {
+ client.close();
+ client.done.then((_) => client.destroy());
+ });
+ Socket.connect("127.0.0.1", server.port).then((socket) {
+ bool onDoneCalled = false;
+ socket.listen((_) { Expect.fail("Unexpected data"); },
+ onDone: () {
+ Expect.isFalse(onDoneCalled);
+ onDoneCalled = true;
+ socket.close();
+ server.close();
+ port.close();
+ });
+ });
+ });
+}
+
+void testConnectStreamDataClose(bool useDestroy) {
+ // Connect socket and listen on the stream. The server sends data
+ // and then closes so both data and a done event is received.
+ List<int> sendData = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
+ ReceivePort port = new ReceivePort();
+ ServerSocket.bind().then((server) {
+ server.listen(
+ (client) {
+ client.add(sendData);
+ if (useDestroy) {
+ client.destroy();
+ } else {
+ client.close();
+ }
+ client.done.then((_) { if (!useDestroy) { client.destroy(); } });
+ });
+ Socket.connect("127.0.0.1", server.port).then((socket) {
+ List<int> data = [];
+ bool onDoneCalled = false;
+ socket.listen(data.addAll,
+ onDone: () {
+ Expect.isFalse(onDoneCalled);
+ onDoneCalled = true;
+ if (!useDestroy) Expect.listEquals(sendData, data);
+ socket.add([0]);
+ socket.close();
+ server.close();
+ port.close();
+ });
+ });
+ });
+}
+
+void testConnectStreamDataCloseCancel(bool useDestroy) {
+ List<int> sendData = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
+ ReceivePort port = new ReceivePort();
+ ServerSocket.bind().then((server) {
+ server.listen(
+ (client) {
+ client.add(sendData);
+ if (useDestroy) {
+ client.destroy();
+ } else {
+ client.close();
+ }
+ client.done
+ .then((_) {
+ if (!useDestroy) client.destroy();
+ })
+ .catchError((e) { /* can happen with short writes */ });
+ });
+ Socket.connect("127.0.0.1", server.port).then((socket) {
+ List<int> data = [];
+ bool onDoneCalled = false;
+ var subscription;
+ subscription = socket.listen(
+ (_) {
+ subscription.cancel();
+ socket.close();
+ server.close();
+ port.close();
+ },
+ onDone: () { Expect.fail("Unexpected pipe completion"); });
+ });
+ });
+}
+
+main() {
+ testArguments();
+ testSimpleBind();
+ testInvalidBind();
+ testConnectNoDestroy();
+ testConnectImmediateDestroy();
+ testConnectConsumerClose();
+ testConnectConsumerWriteClose();
+ testConnectStreamClose();
+ testConnectStreamDataClose(true);
+ testConnectStreamDataClose(false);
+ testConnectStreamDataCloseCancel(true);
+ testConnectStreamDataCloseCancel(false);
+}
diff --git a/tests/standalone/io/stream_pipe_test.dart b/tests/standalone/io/stream_pipe_test.dart
index 8a1b910..f663e81 100644
--- a/tests/standalone/io/stream_pipe_test.dart
+++ b/tests/standalone/io/stream_pipe_test.dart
@@ -10,7 +10,6 @@
import "dart:io";
import "dart:isolate";
-part "testing_server.dart";
// Helper method to be able to run the test from the runtime
// directory, or the top directory.
@@ -56,112 +55,6 @@
}
-// This test does:
-// 1. Opens a socket to the testing server.
-// 2. Pipes the content of a file to that sockets input stream.
-// 3. Creates a temp file.
-// 4. Pipes the socket output stream to the temp file.
-// 5. Expects the original file and the temp file to be equal.
-class PipeServerGame {
- int count = 0;
-
- PipeServerGame.start()
- : _receivePort = new ReceivePort(),
- _sendPort = null,
- _messages = 0 {
- _sendPort = spawnFunction(startPipeServer);
- initialize();
- }
-
- void runTest() {
-
- void connectHandler() {
- String srcFileName =
- getDataFilename("tests/standalone/io/readline_test1.dat");
-
- OutputStream socketOutput = _socket.outputStream;
- InputStream fileInput = new File(srcFileName).openInputStream();
-
- fileInput.onClosed = () {
- InputStream socketInput = _socket.inputStream;
- var tempDir = new Directory('').createTempSync();
- var dstFileName = tempDir.path.concat("/readline_test1.dat");
- var dstFile = new File(dstFileName);
- dstFile.createSync();
- var fileOutput = dstFile.openOutputStream();
-
- socketInput.onClosed = () {
- // Check that the resulting file is equal to the initial
- // file.
- fileOutput.onClosed = () {
- bool result = compareFileContent(srcFileName, dstFileName);
- new File(dstFileName).deleteSync();
- tempDir.deleteSync();
- Expect.isTrue(result);
-
- _socket.close();
-
- // Run this twice.
- if (count++ < 2) {
- runTest();
- } else {
- shutdown();
- }
- };
- };
-
- socketInput.pipe(fileOutput);
- };
-
- fileInput.pipe(socketOutput);
- }
-
- // Connect to the server.
- _socket = new Socket(TestingServer.HOST, _port);
- if (_socket != null) {
- _socket.onConnect = connectHandler;
- } else {
- Expect.fail("socket creation failed");
- }
- }
-
- void initialize() {
- _receivePort.receive((var message, SendPort replyTo) {
- _port = message;
- runTest();
- });
- _sendPort.send(TestingServer.INIT, _receivePort.toSendPort());
- }
-
- void shutdown() {
- _sendPort.send(TestingServer.SHUTDOWN, _receivePort.toSendPort());
- _receivePort.close();
- }
-
- int _port;
- ReceivePort _receivePort;
- SendPort _sendPort;
- Socket _socket;
- int _messages;
-}
-
-
-void startPipeServer() {
- var server = new PipeServer();
- port.receive(server.dispatch);
-}
-
-
-// The testing server will simply pipe each connecting sockets input
-// stream to its output stream.
-class PipeServer extends TestingServer {
- void onConnection(Socket connection) {
- connection.onError = (Exception e) { Expect.fail("Socket error $e"); };
- connection.inputStream.pipe(connection.outputStream);
- }
-}
-
-
// Test piping from one file to another and closing both streams
// after wards.
testFileToFilePipe1() {
@@ -172,22 +65,19 @@
String srcFileName =
getDataFilename("tests/standalone/io/readline_test1.dat");
- var srcStream = new File(srcFileName).openInputStream();
+ var srcStream = new File(srcFileName).openRead();
var tempDir = new Directory('').createTempSync();
String dstFileName = tempDir.path.concat("/readline_test1.dat");
new File(dstFileName).createSync();
- var dstStream = new File(dstFileName).openOutputStream();
-
- dstStream.onClosed = () {
+ var output = new File(dstFileName).openWrite();
+ srcStream.pipe(output).then((_) {
bool result = compareFileContent(srcFileName, dstFileName);
new File(dstFileName).deleteSync();
tempDir.deleteSync();
Expect.isTrue(result);
donePort.toSendPort().send(null);
- };
-
- srcStream.pipe(dstStream);
+ });
}
@@ -202,18 +92,17 @@
String srcFileName =
getDataFilename("tests/standalone/io/readline_test1.dat");
var srcFile = new File(srcFileName);
- var srcStream = srcFile.openInputStream();
+ var srcStream = srcFile.openRead();
var tempDir = new Directory('').createTempSync();
var dstFileName = tempDir.path.concat("/readline_test1.dat");
var dstFile = new File(dstFileName);
dstFile.createSync();
- var dstStream = dstFile.openOutputStream();
-
- srcStream.onClosed = () {
- dstStream.write([32]);
- dstStream.close();
- dstStream.onClosed = () {
+ var output = dstFile.openWrite();
+ output.addStream(srcStream).then((_) {
+ output.add([32]);
+ output.close();
+ output.done.then((_) {
var src = srcFile.openSync();
var dst = dstFile.openSync();
var srcLength = src.lengthSync();
@@ -231,10 +120,8 @@
dstFile.deleteSync();
tempDir.deleteSync();
donePort.toSendPort().send(null);
- };
- };
-
- srcStream.pipe(dstStream, close: false);
+ });
+ });
}
@@ -248,42 +135,38 @@
String srcFileName =
getDataFilename("tests/standalone/io/readline_test1.dat");
var srcFile = new File(srcFileName);
- var srcStream = srcFile.openInputStream();
+ var srcStream = srcFile.openRead();
var tempDir = new Directory('').createTempSync();
var dstFileName = tempDir.path.concat("/readline_test1.dat");
var dstFile = new File(dstFileName);
dstFile.createSync();
- var dstStream = dstFile.openOutputStream();
-
- srcStream.onClosed = () {
- var srcStream2 = srcFile.openInputStream();
-
- dstStream.onClosed = () {
- var src = srcFile.openSync();
- var dst = dstFile.openSync();
- var srcLength = src.lengthSync();
- var dstLength = dst.lengthSync();
- Expect.equals(srcLength * 2, dstLength);
- Expect.isTrue(compareFileContent(srcFileName,
- dstFileName,
- count: srcLength));
- Expect.isTrue(compareFileContent(srcFileName,
- dstFileName,
- file2Offset: srcLength,
- count: srcLength));
- src.closeSync();
- dst.closeSync();
- dstFile.deleteSync();
- tempDir.deleteSync();
- donePort.toSendPort().send(null);
- };
-
- // Pipe another copy of the source file.
- srcStream2.pipe(dstStream);
- };
-
- srcStream.pipe(dstStream, close: false);
+ var output = dstFile.openWrite();
+ output.addStream(srcStream).then((_) {
+ var srcStream2 = srcFile.openRead();
+ output.addStream(srcStream2).then((_) {
+ output.close();
+ output.done.then((_) {
+ var src = srcFile.openSync();
+ var dst = dstFile.openSync();
+ var srcLength = src.lengthSync();
+ var dstLength = dst.lengthSync();
+ Expect.equals(srcLength * 2, dstLength);
+ Expect.isTrue(compareFileContent(srcFileName,
+ dstFileName,
+ count: srcLength));
+ Expect.isTrue(compareFileContent(srcFileName,
+ dstFileName,
+ file2Offset: srcLength,
+ count: srcLength));
+ src.closeSync();
+ dst.closeSync();
+ dstFile.deleteSync();
+ tempDir.deleteSync();
+ donePort.toSendPort().send(null);
+ });
+ });
+ });
}
@@ -291,5 +174,4 @@
testFileToFilePipe1();
testFileToFilePipe2();
testFileToFilePipe3();
- PipeServerGame echoServerGame = new PipeServerGame.start();
}
diff --git a/tests/standalone/io/string_decoder_test.dart b/tests/standalone/io/string_decoder_test.dart
index 879cf8c..6a81f95 100644
--- a/tests/standalone/io/string_decoder_test.dart
+++ b/tests/standalone/io/string_decoder_test.dart
@@ -2,49 +2,60 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+import "dart:async";
import "dart:io";
void test() {
// Code point U+10FFFF is the largest code point supported by Dart.
- //var decoder = _StringDecoders.decoder(Encoding.UTF_8);
- ListInputStream lis = new ListInputStream();
- lis.write([0xf0, 0x90, 0x80, 0x80]); // U+10000
- lis.write([0xf4, 0x8f, 0xbf, 0xbf]); // U+10FFFF
- lis.write([0xf4, 0x90, 0x80, 0x80]); // U+110000
- lis.write([0xfa, 0x80, 0x80, 0x80, 0x80]); // U+2000000
- lis.write([0xfd, 0x80, 0x80, 0x80, 0x80, 0x80]); // U+40000000
- lis.markEndOfStream();
+ var controller = new StreamController();
+ controller.add([0xf0, 0x90, 0x80, 0x80]); // U+10000
+ controller.add([0xf4, 0x8f, 0xbf, 0xbf]); // U+10FFFF
+ controller.add([0xf4, 0x90, 0x80, 0x80]); // U+110000
+ controller.add([0xfa, 0x80, 0x80, 0x80, 0x80]); // U+2000000
+ controller.add([0xfd, 0x80, 0x80, 0x80, 0x80, 0x80]); // U+40000000
+ controller.close();
- var sis = new StringInputStream(lis);
- sis.onData = () {
- var decoded = sis.read();
- Expect.equals(7, decoded.length);
+ var decoder = new StringDecoder(Encoding.UTF_8, '?'.charCodeAt(0));
+ var stream = controller.stream.transform(decoder);
+ stream.reduce(
+ new StringBuffer(),
+ (b, e) {
+ b.add(e);
+ return b;
+ })
+ .then((b) => b.toString())
+ .then((decoded) {
+ Expect.equals(7, decoded.length);
- var replacementChar = '?'.charCodeAt(0);
- Expect.equals(0xd800, decoded.charCodeAt(0));
- Expect.equals(0xdc00, decoded.charCodeAt(1));
- Expect.equals(0xdbff, decoded.charCodeAt(2));
- Expect.equals(0xdfff, decoded.charCodeAt(3));
- Expect.equals(replacementChar, decoded.charCodeAt(4));
- Expect.equals(replacementChar, decoded.charCodeAt(5));
- Expect.equals(replacementChar, decoded.charCodeAt(6));
- };
+ var replacementChar = '?'.charCodeAt(0);
+ Expect.equals(0xd800, decoded.charCodeAt(0));
+ Expect.equals(0xdc00, decoded.charCodeAt(1));
+ Expect.equals(0xdbff, decoded.charCodeAt(2));
+ Expect.equals(0xdfff, decoded.charCodeAt(3));
+ Expect.equals(replacementChar, decoded.charCodeAt(4));
+ Expect.equals(replacementChar, decoded.charCodeAt(5));
+ Expect.equals(replacementChar, decoded.charCodeAt(6));
+ });
}
void testInvalid() {
- void invalid(var bytes) {
- ListInputStream lis = new ListInputStream();
- lis.write(bytes);
- lis.markEndOfStream();
- var sis = new StringInputStream(lis);
- sis.onData = () { throw "onData not expected"; };
- sis.onError = (e) { Expect.isTrue(e is DecoderException); };
- sis.onClosed = () { throw "onClosed not expected"; };
+ void invalid(var bytes, var outputLength) {
+ var controller = new StreamController();
+ controller.add(bytes);
+ controller.close();
+ controller.stream.transform(new StringDecoder()).listen((string) {
+ Expect.equals(outputLength, string.length);
+ for (var i = 0; i < outputLength; i++) {
+ Expect.equals(0xFFFD, string.charCodeAt(i));
+ }
+ });
}
- invalid([0x80]);
- invalid([0xff]);
- invalid([0xf0, 0xc0]);
+ invalid([0x80], 1);
+ invalid([0xff], 1);
+ invalid([0xf0, 0xc0], 1);
+ invalid([0xc0, 0x80], 1);
+ invalid([0xfd, 0x80, 0x80], 3); // Unfinished encoding.
}
void main() {
diff --git a/tests/standalone/io/string_stream_test.dart b/tests/standalone/io/string_stream_test.dart
deleted file mode 100644
index 4ef0534..0000000
--- a/tests/standalone/io/string_stream_test.dart
+++ /dev/null
@@ -1,335 +0,0 @@
-// 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.
-
-import "dart:io";
-import "dart:isolate";
-
-void testUtf8() {
- List<int> data = [0x01,
- 0x7f,
- 0xc2, 0x80,
- 0xdf, 0xbf,
- 0xe0, 0xa0, 0x80,
- 0xef, 0xbf, 0xbf,
- 0xf0, 0x9d, 0x84, 0x9e];
- ListInputStream s = new ListInputStream();
- s.write(data);
- s.markEndOfStream();
- StringInputStream stream = new StringInputStream(s);
- void stringData() {
- String s = stream.read();
- Expect.equals(8, s.length);
- Expect.equals(new String.fromCharCodes([0x01]), s[0]);
- Expect.equals(new String.fromCharCodes([0x7f]), s[1]);
- Expect.equals(new String.fromCharCodes([0x80]), s[2]);
- Expect.equals(new String.fromCharCodes([0x7ff]), s[3]);
- Expect.equals(new String.fromCharCodes([0x800]), s[4]);
- Expect.equals(new String.fromCharCodes([0xffff]), s[5]);
- Expect.equals(new String.fromCharCodes([0xffff]), s[5]);
-
- // Surrogate pair for U+1D11E.
- Expect.equals(new String.fromCharCodes([0xd834, 0xdd1e]),
- s.substring(6, 8));
- }
- stream.onData = stringData;
-}
-
-void testLatin1() {
- List<int> data = [0x01,
- 0x7f,
- 0x44, 0x61, 0x72, 0x74,
- 0x80,
- 0xff];
- ListInputStream s = new ListInputStream();
- s.write(data);
- s.markEndOfStream();
- StringInputStream stream = new StringInputStream(s, Encoding.ISO_8859_1);
- void stringData() {
- String s = stream.read();
- Expect.equals(8, s.length);
- Expect.equals(new String.fromCharCodes([0x01]), s[0]);
- Expect.equals(new String.fromCharCodes([0x7f]), s[1]);
- Expect.equals("Dart", s.substring(2, 6));
- Expect.equals(new String.fromCharCodes([0x80]), s[6]);
- Expect.equals(new String.fromCharCodes([0xff]), s[7]);
- }
- stream.onData = stringData;
-}
-
-void testAscii() {
- List<int> data = [0x01,
- 0x44, 0x61, 0x72, 0x74,
- 0x7f];
- ListInputStream s = new ListInputStream();
- s.write(data);
- s.markEndOfStream();
- StringInputStream stream =
- new StringInputStream(s, Encoding.ASCII);
- void stringData() {
- String s = stream.read();
- Expect.equals(6, s.length);
- Expect.equals(new String.fromCharCodes([0x01]), s[0]);
- Expect.equals("Dart", s.substring(1, 5));
- Expect.equals(new String.fromCharCodes([0x7f]), s[5]);
- }
- stream.onData = stringData;
-}
-
-void testReadLine1() {
- ListInputStream s = new ListInputStream();
- StringInputStream stream = new StringInputStream(s);
- var stage = 0;
-
- void stringData() {
- var line;
- if (stage == 0) {
- line = stream.readLine();
- Expect.equals(null, line);
- stage++;
- s.markEndOfStream();
- } else if (stage == 1) {
- line = stream.readLine();
- Expect.equals("Line", line);
- line = stream.readLine();
- Expect.equals(null, line);
- stage++;
- }
- }
-
- void streamClosed() {
- Expect.equals(true, stream.closed);
- Expect.equals(2, stage);
- }
-
- stream.onData = stringData;
- stream.onClosed = streamClosed;
- s.write("Line".charCodes);
-}
-
-void testReadLine2() {
- ListInputStream s = new ListInputStream();
- StringInputStream stream = new StringInputStream(s);
- var stage = 0;
-
- void stringData() {
- var line;
- if (stage == 0) {
- Expect.equals(21, stream.available());
- line = stream.readLine();
- Expect.equals("Line1", line);
- Expect.equals(15, stream.available());
- line = stream.readLine();
- Expect.equals("Line2", line);
- Expect.equals(8, stream.available());
- line = stream.readLine();
- Expect.equals("Line3", line);
- line = stream.readLine();
- Expect.equals(2, stream.available());
- Expect.equals(null, line);
- stage++;
- s.write("ne4\n".charCodes);
- } else if (stage == 1) {
- Expect.equals(6, stream.available());
- line = stream.readLine();
- Expect.equals("Line4", line);
- Expect.equals(0, stream.available());
- line = stream.readLine();
- Expect.equals(null, line);
- stage++;
- s.write("\n\n\r\n\r\n\r\r".charCodes);
- } else if (stage == 2) {
- // Expect 5 empty lines. As long as the stream is not closed the
- // final \r cannot be interpreted as a end of line.
- Expect.equals(8, stream.available());
- for (int i = 0; i < 5; i++) {
- line = stream.readLine();
- Expect.equals("", line);
- }
- Expect.equals(1, stream.available());
- line = stream.readLine();
- Expect.equals(null, line);
- stage++;
- s.markEndOfStream();
- } else if (stage == 3) {
- // The final \r can now be interpreted as an end of line.
- Expect.equals(1, stream.available());
- line = stream.readLine();
- Expect.equals("", line);
- line = stream.readLine();
- Expect.equals(null, line);
- stage++;
- }
- }
-
- void streamClosed() {
- Expect.equals(4, stage);
- Expect.equals(true, stream.closed);
- }
-
- stream.onLine = stringData;
- stream.onClosed = streamClosed;
- s.write("Line1\nLine2\r\nLine3\rLi".charCodes);
-}
-
-void testReadChunks() {
- ListInputStream s = new ListInputStream();
- StringInputStream stream = new StringInputStream(s);
-
- void stringData() {
- var data;
- Expect.equals(8, stream.available());
- data = stream.read(1);
- Expect.equals("A", data);
- Expect.equals(7, stream.available());
- data = stream.read(2);
- Expect.equals("BC", data);
- Expect.equals(5, stream.available());
- data = stream.read(3);
- Expect.equals("D12", data);
- Expect.equals(2, stream.available());
- data = stream.read();
- Expect.equals("34", data);
- Expect.equals(0, stream.available());
- data = stream.read(1);
- Expect.equals(null, data);
- Expect.equals(0, stream.available());
- data = stream.read(2);
- Expect.equals(null, data);
- Expect.equals(0, stream.available());
- data = stream.read(3);
- Expect.equals(null, data);
- Expect.equals(0, stream.available());
- data = stream.read();
- Expect.equals(null, data);
- Expect.equals(0, stream.available());
- }
-
- s.write("ABCD1234".charCodes);
- stream.onData = stringData;
-}
-
-void testReadMixed() {
- ListInputStream s = new ListInputStream();
- StringInputStream stream = new StringInputStream(s);
- var stage = 0;
-
- void stringData() {
- var data;
- if (stage == 0) {
- Expect.equals(11, stream.available());
- data = stream.read(2);
- Expect.equals("A\r", data);
- Expect.equals(9, stream.available());
- data = stream.readLine();
- Expect.equals("", data);
- Expect.equals(8, stream.available());
- data = stream.read(4);
- Expect.equals("BCD\n", data);
- Expect.equals(4, stream.available());
- data = stream.readLine();
- Expect.equals(null, data);
- data = stream.read(4);
- Expect.equals("1234", data);
- s.write("A\r\nBCD\n1234".charCodes);
- stage++;
- } else if (stage == 1) {
- Expect.equals(11, stream.available());
- data = stream.read(1);
- Expect.equals("A", data);
- Expect.equals(10, stream.available());
- data = stream.read(1);
- Expect.equals("\r", data);
- Expect.equals(9, stream.available());
- data = stream.read(1);
- Expect.equals("\n", data);
- Expect.equals(8, stream.available());
- data = stream.readLine();
- Expect.equals("BCD", data);
- data = stream.readLine();
- Expect.equals(null, data);
- data = stream.read(4);
- Expect.equals("1234", data);
- s.write("A\r\nBCD\n1234".charCodes);
- stage++;
- } else if (stage == 2) {
- Expect.equals(11, stream.available());
- data = stream.read(7);
- Expect.equals("A\r\nBCD\n", data);
- Expect.equals(4, stream.available());
- data = stream.readLine();
- Expect.equals(null, data);
- data = stream.read();
- Expect.equals("1234", data);
- stage++;
- s.markEndOfStream();
- }
- }
-
- void streamClosed() {
- Expect.equals(3, stage);
- Expect.equals(true, stream.closed);
- }
-
- s.write("A\r\nBCD\n1234".charCodes);
- stream.onData = stringData;
- stream.onClosed = streamClosed;
-}
-
-class TestException implements Exception {
- TestException();
-}
-
-class ErrorInputStream implements InputStream {
- ErrorInputStream();
- List<int> read([int len]) => null;
- int readInto(List<int> buffer, [int offset, int len]) => 0;
- int available() => 0;
- void pipe(OutputStream output, {bool close: true}){ }
- void close() { }
- bool get closed => true;
- void set onData(void callback()) { }
- void set onClosed(void callback()) { }
- void set onError(void callback(Exception e)) {
- callback(new TestException());
- }
-}
-
-testErrorHandler() {
- var errors = 0;
- var stream = new StringInputStream(new ErrorInputStream());
- stream.onError = (e) {
- errors++;
- Expect.isTrue(e is TestException);
- };
- Expect.equals(1, errors);
-}
-
-testEncodingErrorWithHandler() {
- var port = new ReceivePort();
- var errors = 0;
- var expected = [206, 187, 120, 46, 32, 120, 10];
- ListInputStream input = new ListInputStream();
- input.write(expected);
- var stringStream = new StringInputStream(input, Encoding.ASCII);
- stringStream.onData = () {
- Expect.fail("We should not get any data");
- };
- stringStream.onError = (e) {
- port.close();
- Expect.isTrue(e is Exception);
- };
-}
-
-
-main() {
- testUtf8();
- testLatin1();
- testAscii();
- testReadLine1();
- testReadLine2();
- testReadChunks();
- testReadMixed();
- testErrorHandler();
- testEncodingErrorWithHandler();
-}
diff --git a/tests/standalone/io/string_transformer_test.dart b/tests/standalone/io/string_transformer_test.dart
new file mode 100644
index 0000000..16b1b3f
--- /dev/null
+++ b/tests/standalone/io/string_transformer_test.dart
@@ -0,0 +1,238 @@
+// 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.
+
+import "dart:async";
+import "dart:io";
+import "dart:isolate";
+import "dart:utf";
+
+void testUtf8() {
+ List<int> data = [0x01,
+ 0x7f,
+ 0xc2, 0x80,
+ 0xdf, 0xbf,
+ 0xe0, 0xa0, 0x80,
+ 0xef, 0xbf, 0xbf,
+ 0xf0, 0x9d, 0x84, 0x9e,
+ 0x100, -0x1, -0xFF];
+ var controller = new StreamController();
+ controller.add(data);
+ controller.close();
+ var stringStream = controller.stream
+ .transform(new StringDecoder(Encoding.UTF_8));
+ stringStream.listen(
+ (s) {
+ Expect.equals(11, s.length);
+ Expect.equals(new String.fromCharCodes([0x01]), s[0]);
+ Expect.equals(new String.fromCharCodes([0x7f]), s[1]);
+ Expect.equals(new String.fromCharCodes([0x80]), s[2]);
+ Expect.equals(new String.fromCharCodes([0x7ff]), s[3]);
+ Expect.equals(new String.fromCharCodes([0x800]), s[4]);
+ Expect.equals(new String.fromCharCodes([0xffff]), s[5]);
+ Expect.equals(new String.fromCharCodes([0xffff]), s[5]);
+
+ // Surrogate pair for U+1D11E.
+ Expect.equals(new String.fromCharCodes([0xd834, 0xdd1e]),
+ s.substring(6, 8));
+
+ Expect.equals(new String.fromCharCodes(
+ [UNICODE_REPLACEMENT_CHARACTER_CODEPOINT,
+ UNICODE_REPLACEMENT_CHARACTER_CODEPOINT,
+ UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]),
+ s.substring(8, 11));
+ });
+}
+
+void testLatin1() {
+ List<int> data = [0x01,
+ 0x7f,
+ 0x44, 0x61, 0x72, 0x74,
+ 0x80,
+ 0xff,
+ 0x100, -0x1, -0xff];
+ var controller = new StreamController();
+ controller.add(data);
+ controller.close();
+ var stream = controller.stream
+ .transform(new StringDecoder(Encoding.ISO_8859_1));
+ stream.listen((s) {
+ Expect.equals(11, s.length);
+ Expect.equals(new String.fromCharCodes([0x01]), s[0]);
+ Expect.equals(new String.fromCharCodes([0x7f]), s[1]);
+ Expect.equals("Dart", s.substring(2, 6));
+ Expect.equals(new String.fromCharCodes([0x80]), s[6]);
+ Expect.equals(new String.fromCharCodes([0xff]), s[7]);
+ Expect.equals('???', s.substring(8, 11));
+ });
+}
+
+void testAscii() {
+ List<int> data = [0x01,
+ 0x44, 0x61, 0x72, 0x74,
+ 0x7f,
+ 0xf4, 0x100, -0x1, -0xff];
+ var controller = new StreamController();
+ controller.add(data);
+ controller.close();
+ var stream = controller.stream
+ .transform(new StringDecoder(Encoding.ASCII));
+ stream.listen((s) {
+ Expect.equals(10, s.length);
+ Expect.equals(new String.fromCharCodes([0x01]), s[0]);
+ Expect.equals("Dart", s.substring(1, 5));
+ Expect.equals(new String.fromCharCodes([0x7f]), s[5]);
+ Expect.equals('????', s.substring(6, 10));
+ });
+}
+
+void testReadLine1() {
+ var controller = new StreamController();
+ var stream = controller.stream
+ .transform(new StringDecoder())
+ .transform(new LineTransformer());
+
+ var stage = 0;
+
+ void stringData(line) {
+ var line;
+ if (stage == 0) {
+ Expect.equals(null, line);
+ stage++;
+ controller.close();
+ } else if (stage == 1) {
+ Expect.equals("Line", line);
+ stage++;
+ }
+ }
+
+ void streamClosed() {
+ Expect.equals(2, stage);
+ }
+
+ stream.listen(
+ stringData,
+ onDone: streamClosed);
+
+ controller.add("Line".charCodes);
+}
+
+void testReadLine2() {
+ var controller = new StreamController();
+
+ var stream = controller.stream
+ .transform(new StringDecoder())
+ .transform(new LineTransformer());
+
+ var stage = 0;
+ var subStage = 0;
+ stream.listen((line) {
+ if (stage == 0) {
+ if (subStage == 0) {
+ Expect.equals("Line1", line);
+ subStage++;
+ } else if (subStage == 1) {
+ Expect.equals("Line2", line);
+ subStage++;
+ } else if (subStage == 2) {
+ Expect.equals("Line3", line);
+ subStage = 0;
+ stage++;
+ controller.add("ne4\n".charCodes);
+ } else {
+ Expect.fail("Stage 0 failed");
+ }
+ } else if (stage == 1) {
+ if (subStage == 0) {
+ Expect.equals("Line4", line);
+ subStage = 0;
+ stage++;
+ controller.add("\n\n\r\n\r\n\r\r".charCodes);
+ } else {
+ Expect.fail("Stage 1 failed");
+ }
+ } else if (stage == 2) {
+ if (subStage < 4) {
+ // Expect 5 empty lines. As long as the stream is not closed the
+ // final \r cannot be interpreted as a end of line.
+ Expect.equals("", line);
+ subStage++;
+ } else if (subStage == 4) {
+ Expect.equals("", line);
+ subStage = 0;
+ stage++;
+ controller.close();
+ } else {
+ Expect.fail("Stage 2 failed");
+ }
+ } else if (stage == 3) {
+ if (subStage == 0) {
+ Expect.equals("", line);
+ stage++;
+ } else {
+ Expect.fail("Stage 3 failed");
+ }
+ }
+ }, onDone: () {
+ Expect.equals(4, stage);
+ Expect.equals(0, subStage);
+ });
+
+ controller.add("Line1\nLine2\r\nLine3\rLi".charCodes);
+}
+
+class TestException implements Exception {
+ TestException();
+}
+
+testErrorHandler() {
+ var controller = new StreamController();
+ var errors = 0;
+ var stream = controller.stream
+ .transform(new StringDecoder())
+ .transform(new LineTransformer());
+ stream.listen(
+ (_) {},
+ onDone: () {
+ Expect.equals(1, errors);
+ },
+ onError: (e) {
+ errors++;
+ Expect.isTrue(e.error is TestException);
+ });
+ controller.signalError(new TestException());
+ controller.close();
+}
+
+testLatin1EncoderError() {
+ List<int> data = [0x01,
+ 0x7f,
+ 0x44, 0x61, 0x72, 0x74,
+ 0x80,
+ 0xff,
+ 0x100];
+ var controller = new StreamController();
+ controller.add(new String.fromCharCodes(data));
+ controller.close();
+ var stream = controller.stream
+ .transform(new StringEncoder(Encoding.ISO_8859_1));
+ stream.listen(
+ (s) {
+ Expect.fail("data not expected");
+ },
+ onError: (error) {
+ print(error);
+ Expect.isTrue(error.error is FormatException);
+ });
+
+}
+
+main() {
+ testUtf8();
+ testLatin1();
+ testAscii();
+ testReadLine1();
+ testReadLine2();
+ testErrorHandler();
+ testLatin1EncoderError();
+}
diff --git a/tests/standalone/io/testing_server.dart b/tests/standalone/io/testing_server.dart
index e37a989..79ab136 100644
--- a/tests/standalone/io/testing_server.dart
+++ b/tests/standalone/io/testing_server.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, 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.
@@ -12,17 +12,19 @@
void onConnection(Socket connection); // Abstract.
- void errorHandlerServer(Exception e) {
+ void errorHandlerServer(e) {
Expect.fail("Server socket error $e");
}
void dispatch(message, SendPort replyTo) {
if (message == INIT) {
- _server = new ServerSocket(HOST, 0, 10);
- Expect.equals(true, _server != null);
- _server.onConnection = onConnection;
- _server.onError = errorHandlerServer;
- replyTo.send(_server.port, null);
+ ServerSocket.bind(HOST, 0, 10).then((server) {
+ _server = server;
+ _server.listen(
+ onConnection,
+ onError: errorHandlerServer);
+ replyTo.send(_server.port, null);
+ });
} else if (message == SHUTDOWN) {
_server.close();
port.close();
diff --git a/tests/standalone/io/url_encoding_test.dart b/tests/standalone/io/url_encoding_test.dart
index cee5022..cfeefe5 100644
--- a/tests/standalone/io/url_encoding_test.dart
+++ b/tests/standalone/io/url_encoding_test.dart
@@ -1,18 +1,16 @@
-// Copyright (c) 2012, 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.
import "dart:async";
import "dart:utf";
-part "../../../sdk/lib/io/input_stream.dart";
-part "../../../sdk/lib/io/output_stream.dart";
-part "../../../sdk/lib/io/chunked_stream.dart";
-part "../../../sdk/lib/io/string_stream.dart";
-part "../../../sdk/lib/io/stream_util.dart";
+
+part '../../../sdk/lib/io/io_stream_consumer.dart';
part "../../../sdk/lib/io/http.dart";
part "../../../sdk/lib/io/http_impl.dart";
part "../../../sdk/lib/io/http_parser.dart";
part "../../../sdk/lib/io/http_utils.dart";
+part "../../../sdk/lib/io/socket.dart";
void testParseEncodedString() {
String encodedString = 'foo+bar%20foobar%25%26';
diff --git a/tests/standalone/io/web_socket_no_secure_test.dart b/tests/standalone/io/web_socket_no_secure_test.dart
index 7aae7a3..46e1d03 100644
--- a/tests/standalone/io/web_socket_no_secure_test.dart
+++ b/tests/standalone/io/web_socket_no_secure_test.dart
@@ -1,19 +1,191 @@
-// Copyright (c) 2012, 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.
//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
// TODO(7157): Remove this test once the bug is fixed.
// This is a copy of web_socket_test.dart with the secure connection
// tests disabled, so it does not crash on Windows.
-import "dart:async";
import "dart:io";
import "dart:isolate";
import "dart:scalarlist";
-import "dart:uri";
-const SERVER_ADDRESS = "127.0.0.1";
-const HOST_NAME = "localhost";
+void testRequestResponseClientCloses(
+ int totalConnections, int closeStatus, String closeReason) {
+ HttpServer.bind().then((server) {
+
+ server.transform(new WebSocketTransformer()).listen((webSocket) {
+ webSocket.listen((event) {
+ if (event is MessageEvent) {
+ webSocket.send(event.data);
+ } else if (event is CloseEvent) {
+ Expect.equals(closeStatus == null
+ ? WebSocketStatus.NO_STATUS_RECEIVED
+ : closeStatus, event.code);
+ Expect.equals(closeReason == null ? "" : closeReason, event.reason);
+ }
+ });
+ });
+
+ int closeCount = 0;
+ String messageText = "Hello, world!";
+ for (int i = 0; i < totalConnections; i++) {
+ int messageCount = 0;
+ WebSocket.connect("ws://127.0.0.1:${server.port}/")
+ .then((webSocket) {
+ webSocket.send(messageText);
+ webSocket.listen((event) {
+ if (event is MessageEvent) {
+ messageCount++;
+ if (messageCount < 1 ) {
+ Expect.equals(messageText, event.data);
+ webSocket.send(event.data);
+ } else {
+ webSocket.close(closeStatus, closeReason);
+ }
+ } else if (event is CloseEvent) {
+ Expect.equals(closeStatus == null
+ ? WebSocketStatus.NO_STATUS_RECEIVED
+ : closeStatus, event.code);
+ Expect.equals("", event.reason);
+ closeCount++;
+ if (closeCount == totalConnections) {
+ server.close();
+ }
+ }
+ });
+ });
+ }
+
+ });
+}
+
+
+void testRequestResponseServerCloses(
+ int totalConnections, int closeStatus, String closeReason) {
+ HttpServer.bind().then((server) {
+
+ int closeCount = 0;
+ server.transform(new WebSocketTransformer()).listen((webSocket) {
+ String messageText = "Hello, world!";
+ int messageCount = 0;
+ webSocket.listen((event) {
+ if (event is MessageEvent) {
+ messageCount++;
+ if (messageCount < 10) {
+ Expect.equals(messageText, event.data);
+ webSocket.send(event.data);
+ } else {
+ webSocket.close(closeStatus, closeReason);
+ }
+ } else if (event is CloseEvent) {
+ Expect.equals(closeStatus == null
+ ? WebSocketStatus.NO_STATUS_RECEIVED
+ : closeStatus, event.code);
+ Expect.equals("", event.reason);
+ closeCount++;
+ if (closeCount == totalConnections) {
+ server.close();
+ }
+ }
+ });
+ webSocket.send(messageText);
+ });
+
+ for (int i = 0; i < totalConnections; i++) {
+ WebSocket.connect("ws://127.0.0.1:${server.port}/")
+ .then((webSocket) {
+ webSocket.listen((event) {
+ if (event is MessageEvent) {
+ webSocket.send(event.data);
+ } else if (event is CloseEvent) {
+ Expect.equals(closeStatus == null
+ ? WebSocketStatus.NO_STATUS_RECEIVED
+ : closeStatus, event.code);
+ Expect.equals(
+ closeReason == null ? "" : closeReason, event.reason);
+ }
+ });
+ });
+ }
+
+ });
+}
+
+
+void testMessageLength(int messageLength) {
+ HttpServer.bind().then((server) {
+
+ Uint8List originalMessage = new Uint8List(messageLength);
+ server.transform(new WebSocketTransformer()).listen((webSocket) {
+ webSocket.listen((event) {
+ if (event is MessageEvent) {
+ Expect.listEquals(originalMessage, event.data);
+ webSocket.send(event.data);
+ } else if (event is CloseEvent) {
+ }
+ });
+ });
+
+ WebSocket.connect("ws://127.0.0.1:${server.port}/")
+ .then((webSocket) {
+ webSocket.listen((event) {
+ if (event is MessageEvent) {
+ Expect.listEquals(originalMessage, event.data);
+ webSocket.close();
+ } else if (event is CloseEvent) {
+ server.close();
+ }
+ });
+ webSocket.send(originalMessage);
+ });
+
+ });
+}
+
+
+void testNoUpgrade() {
+ HttpServer.bind().then((server) {
+
+ // Create a server which always responds with NOT_FOUND.
+ server.listen((request) {
+ request.response.statusCode = HttpStatus.NOT_FOUND;
+ request.response.close();
+ });
+
+ WebSocket.connect("ws://127.0.0.1:${server.port}/").catchError((error) {
+ server.close();
+ });
+
+ });
+}
+
+
+void testUsePOST() {
+ HttpServer.bind().then((server) {
+
+ var errorPort = new ReceivePort();
+ server.transform(new WebSocketTransformer()).listen((webSocket) {
+ Expect.fail("No connection expected");
+ }, onError: (e) {
+ errorPort.close();
+ });
+
+ HttpClient client = new HttpClient();
+ client.post("127.0.0.1", server.port, "/")
+ .then((request) => request.close())
+ .then((response) {
+ Expect.equals(HttpStatus.BAD_REQUEST, response.statusCode);
+ client.close();
+ server.close();
+ });
+
+ });
+}
class WebSocketInfo {
@@ -21,360 +193,89 @@
}
-/**
- * A SecurityConfiguration lets us run the tests over HTTP or HTTPS.
- */
-class SecurityConfiguration {
- final bool secure;
- HttpClient client;
-
- SecurityConfiguration({bool this.secure}) : client = new HttpClient();
-
- HttpServer createServer({int backlog}) {
- HttpServer server = secure ? new HttpsServer() : new HttpServer();
- server.listen(SERVER_ADDRESS,
- 0,
- backlog: backlog,
- certificate_name: "CN=$HOST_NAME");
- return server;
- }
-
- WebSocketClientConnection createClient(int port,
- {bool followRedirects,
- String method: "GET"}) {
- HttpClientConnection conn = client.openUrl(method, Uri.parse(
- '${secure ? "https" : "http"}://$HOST_NAME:$port/'));
- if (followRedirects != null) {
- conn.followRedirects = followRedirects;
- }
- return new WebSocketClientConnection(conn);
- }
-
- void testRequestResponseClientCloses(
- int totalConnections, int closeStatus, String closeReason) {
- HttpServer server = createServer(backlog: totalConnections);
- HttpClient client = new HttpClient();
-
- // Make a web socket handler and set it as the HTTP server default handler.
- WebSocketHandler wsHandler = new WebSocketHandler();
- wsHandler.onOpen = (WebSocketConnection conn) {
- var count = 0;
- conn.onMessage = (Object message) => conn.send(message);
- conn.onClosed = (status, reason) {
- Expect.equals(closeStatus == null
- ? WebSocketStatus.NO_STATUS_RECEIVED
- : closeStatus, status);
- Expect.equals(closeReason == null ? "" : closeReason, reason);
- };
- };
- server.defaultRequestHandler = wsHandler.onRequest;
+void testW3CInterface(
+ int totalConnections, int closeStatus, String closeReason) {
+ HttpServer.bind().then((server) {
int closeCount = 0;
- String messageText = "Hello, world!";
- for (int i = 0; i < totalConnections; i++) {
- int messageCount = 0;
- WebSocketClientConnection wsconn = createClient(server.port);
- wsconn.onOpen = () => wsconn.send(messageText);
- wsconn.onMessage = (message) {
- messageCount++;
- if (messageCount < 10) {
- Expect.equals(messageText, message);
- wsconn.send(message);
- } else {
- wsconn.close(closeStatus, closeReason);
- }
- };
- wsconn.onClosed = (status, reason) {
- Expect.equals(closeStatus == null
- ? WebSocketStatus.NO_STATUS_RECEIVED
- : closeStatus, status);
- Expect.equals("", reason);
- closeCount++;
- if (closeCount == totalConnections) {
- client.shutdown();
- server.close();
- }
- };
- }
- }
-
-
- void testRequestResponseServerCloses(
- int totalConnections, int closeStatus, String closeReason) {
- ReceivePort keepAlive = new ReceivePort();
- HttpServer server = createServer(backlog: totalConnections);
-
- // Create a web socket handler and set is as the HTTP server default
- // handler.
- int closeCount = 0;
- WebSocketHandler wsHandler = new WebSocketHandler();
- wsHandler.onOpen = (WebSocketConnection conn) {
+ server.transform(new WebSocketTransformer()).listen((webSocket) {
String messageText = "Hello, world!";
int messageCount = 0;
- conn.onMessage = (Object message) {
- messageCount++;
- if (messageCount < 10) {
- Expect.equals(messageText, message);
- conn.send(message);
- } else {
- conn.close(closeStatus, closeReason);
+ webSocket.listen((event) {
+ if (event is MessageEvent) {
+ messageCount++;
+ if (messageCount < 10) {
+ Expect.equals(messageText, event.data);
+ webSocket.send(event.data);
+ } else {
+ webSocket.close(closeStatus, closeReason);
+ }
+ } else if (event is CloseEvent) {
+ Expect.equals(closeStatus, event.code);
+ Expect.equals("", event.reason);
+ closeCount++;
+ if (closeCount == totalConnections) {
+ server.close();
+ }
}
- };
- conn.onClosed = (status, reason) {
- Expect.equals(closeStatus == null
- ? WebSocketStatus.NO_STATUS_RECEIVED
- : closeStatus, status);
- Expect.equals("", reason);
- closeCount++;
- if (closeCount == totalConnections) {
- client.shutdown();
- server.close();
- keepAlive.close();
- }
- };
- conn.send(messageText);
- };
- server.defaultRequestHandler = wsHandler.onRequest;
-
- for (int i = 0; i < totalConnections; i++) {
- WebSocketClientConnection wsconn = createClient(server.port);
- wsconn.onMessage = (message) => wsconn.send(message);
- wsconn.onClosed = (status, reason) {
- Expect.equals(closeStatus == null
- ? WebSocketStatus.NO_STATUS_RECEIVED
- : closeStatus, status);
- Expect.equals(closeReason == null ? "" : closeReason, reason);
- };
- }
- }
-
-
- void testMessageLength(int messageLength) {
- HttpServer server = createServer(backlog: 1);
- bool serverReceivedMessage = false;
- bool clientReceivedMessage = false;
-
- // Create a web socket handler and set is as the HTTP server default
- // handler.
- Uint8List originalMessage = new Uint8List(messageLength);
- WebSocketHandler wsHandler = new WebSocketHandler();
- wsHandler.onOpen = (WebSocketConnection conn) {
- conn.onMessage = (Object message) {
- serverReceivedMessage = true;
- Expect.listEquals(originalMessage, message);
- conn.send(message);
- };
- conn.onClosed = (status, reason) {
- };
- };
- server.defaultRequestHandler = wsHandler.onRequest;
-
- WebSocketClientConnection wsconn = createClient(server.port);
- wsconn.onMessage = (message) {
- clientReceivedMessage = true;
- Expect.listEquals(originalMessage, message);
- wsconn.close();
- };
- wsconn.onClosed = (status, reason) {
- Expect.isTrue(serverReceivedMessage);
- Expect.isTrue(clientReceivedMessage);
- client.shutdown();
- server.close();
- };
- wsconn.onOpen = () {
- wsconn.send(originalMessage);
- };
- }
-
-
- void testNoUpgrade() {
- HttpServer server = createServer(backlog: 5);
-
- // Create a server which always responds with a redirect.
- server.defaultRequestHandler = (request, response) {
- response.statusCode = HttpStatus.MOVED_PERMANENTLY;
- response.outputStream.close();
- };
-
- WebSocketClientConnection wsconn = createClient(server.port,
- followRedirects: false);
- wsconn.onNoUpgrade = (response) {
- Expect.equals(HttpStatus.MOVED_PERMANENTLY, response.statusCode);
- client.shutdown();
- server.close();
- };
- }
-
-
- void testUsePOST() {
- HttpServer server = createServer(backlog: 5);
-
- // Create a web socket handler and set is as the HTTP server default
- // handler.
- int closeCount = 0;
- WebSocketHandler wsHandler = new WebSocketHandler();
- wsHandler.onOpen = (WebSocketConnection conn) {
- Expect.fail("No connection expected");
- };
- server.defaultRequestHandler = wsHandler.onRequest;
-
- WebSocketClientConnection wsconn = createClient(server.port,
- method: "POST");
- wsconn.onNoUpgrade = (response) {
- Expect.equals(HttpStatus.BAD_REQUEST, response.statusCode);
- client.shutdown();
- server.close();
- };
- }
-
-
- void testHashCode(int totalConnections) {
- ReceivePort keepAlive = new ReceivePort();
- HttpServer server = createServer(backlog: totalConnections);
- Map connections = new Map();
-
- void handleMessage(conn, message) {
- var info = connections[conn];
- Expect.isNotNull(info);
- info.messageCount++;
- if (info.messageCount < 10) {
- conn.send(message);
- } else {
- conn.close();
- }
- }
-
- // Create a web socket handler and set is as the HTTP server default
- // handler.
- int closeCount = 0;
- WebSocketHandler wsHandler = new WebSocketHandler();
- wsHandler.onOpen = (WebSocketConnection conn) {
- connections[conn] = new WebSocketInfo();
- String messageText = "Hello, world!";
- conn.onMessage = (Object message) {
- handleMessage(conn, message);
- };
- conn.onClosed = (status, reason) {
- closeCount++;
- var info = connections[conn];
- Expect.equals(10, info.messageCount);
- if (closeCount == totalConnections) {
- client.shutdown();
- server.close();
- keepAlive.close();
- }
- };
- conn.send(messageText);
- };
- server.defaultRequestHandler = wsHandler.onRequest;
-
- for (int i = 0; i < totalConnections; i++) {
- WebSocketClientConnection wsconn = createClient(server.port);
- wsconn.onMessage = (message) => wsconn.send(message);
- }
- }
-
-
- void testW3CInterface(
- int totalConnections, int closeStatus, String closeReason) {
- HttpServer server = createServer(backlog: totalConnections);
-
- // Create a web socket handler and set is as the HTTP server default
- // handler.
- int closeCount = 0;
- WebSocketHandler wsHandler = new WebSocketHandler();
- wsHandler.onOpen = (WebSocketConnection conn) {
- String messageText = "Hello, world!";
- int messageCount = 0;
- conn.onMessage = (Object message) {
- messageCount++;
- if (messageCount < 10) {
- Expect.equals(messageText, message);
- conn.send(message);
- } else {
- conn.close(closeStatus, closeReason);
- }
- };
- conn.onClosed = (status, reason) {
- Expect.equals(closeStatus, status);
- Expect.equals("", reason);
- closeCount++;
- if (closeCount == totalConnections) {
- server.close();
- }
- };
- conn.send(messageText);
- };
- server.defaultRequestHandler = wsHandler.onRequest;
+ });
+ webSocket.send(messageText);
+ });
void webSocketConnection() {
bool onopenCalled = false;
int onmessageCalled = 0;
bool oncloseCalled = false;
- var websocket =
- new WebSocket('${secure ? "wss" : "ws"}://$HOST_NAME:${server.port}');
- Expect.equals(WebSocket.CONNECTING, websocket.readyState);
- websocket.onopen = () {
+ WebSocket.connect("ws://127.0.0.1:${server.port}").then((webSocket) {
Expect.isFalse(onopenCalled);
Expect.equals(0, onmessageCalled);
Expect.isFalse(oncloseCalled);
onopenCalled = true;
- Expect.equals(WebSocket.OPEN, websocket.readyState);
- };
- websocket.onmessage = (event) {
- onmessageCalled++;
- Expect.isTrue(onopenCalled);
- Expect.isFalse(oncloseCalled);
- Expect.equals(WebSocket.OPEN, websocket.readyState);
- websocket.send(event.data);
- };
- websocket.onclose = (event) {
- Expect.isTrue(onopenCalled);
- Expect.equals(10, onmessageCalled);
- Expect.isFalse(oncloseCalled);
- oncloseCalled = true;
- Expect.isTrue(event.wasClean);
- Expect.equals(3002, event.code);
- Expect.equals("Got tired", event.reason);
- Expect.equals(WebSocket.CLOSED, websocket.readyState);
- };
+ Expect.equals(WebSocket.OPEN, webSocket.readyState);
+ webSocket.listen((event) {
+ if (event is MessageEvent) {
+ onmessageCalled++;
+ Expect.isTrue(onopenCalled);
+ Expect.isFalse(oncloseCalled);
+ Expect.equals(WebSocket.OPEN, webSocket.readyState);
+ webSocket.send(event.data);
+ } else if (event is CloseEvent) {
+ Expect.isTrue(onopenCalled);
+ Expect.equals(10, onmessageCalled);
+ Expect.isFalse(oncloseCalled);
+ oncloseCalled = true;
+ Expect.isTrue(event.wasClean);
+ Expect.equals(3002, event.code);
+ Expect.equals("Got tired", event.reason);
+ Expect.equals(WebSocket.CLOSED, webSocket.readyState);
+ }
+ });
+ });
}
for (int i = 0; i < totalConnections; i++) {
webSocketConnection();
}
- }
-
- runTests() {
- testRequestResponseClientCloses(2, null, null);
- testRequestResponseClientCloses(2, 3001, null);
- testRequestResponseClientCloses(2, 3002, "Got tired");
- testRequestResponseServerCloses(2, null, null);
- testRequestResponseServerCloses(2, 3001, null);
- testRequestResponseServerCloses(2, 3002, "Got tired");
- testMessageLength(125);
- testMessageLength(126);
- testMessageLength(127);
- testMessageLength(65535);
- testMessageLength(65536);
- testNoUpgrade();
- testUsePOST();
- testHashCode(2);
- testW3CInterface(2, 3002, "Got tired");
- }
-}
-
-
-void initializeSSL() {
- var testPkcertDatabase =
- new Path(new Options().script).directoryPath.append("pkcert/");
- SecureSocket.initialize(database: testPkcertDatabase.toNativePath(),
- password: "dartdart");
+ });
}
main() {
- new SecurityConfiguration(secure: false).runTests();
+ testRequestResponseClientCloses(2, null, null);
+ testRequestResponseClientCloses(2, 3001, null);
+ testRequestResponseClientCloses(2, 3002, "Got tired");
+ testRequestResponseServerCloses(2, null, null);
+ testRequestResponseServerCloses(2, 3001, null);
+ testRequestResponseServerCloses(2, 3002, "Got tired");
+ testMessageLength(125);
+ testMessageLength(126);
+ testMessageLength(127);
+ testMessageLength(65535);
+ testMessageLength(65536);
+ testNoUpgrade();
+ testUsePOST();
+
+ testW3CInterface(2, 3002, "Got tired");
}
diff --git a/tests/standalone/io/web_socket_protocol_processor_test.dart b/tests/standalone/io/web_socket_protocol_processor_test.dart
index 763a8bf..6ab0da9 100644
--- a/tests/standalone/io/web_socket_protocol_processor_test.dart
+++ b/tests/standalone/io/web_socket_protocol_processor_test.dart
@@ -1,9 +1,12 @@
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// 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.
import "dart:math";
+import "dart:async";
+part "../../../sdk/lib/io/http.dart";
+part "../../../sdk/lib/io/io_stream_consumer.dart";
part "../../../sdk/lib/io/websocket.dart";
part "../../../sdk/lib/io/websocket_impl.dart";
@@ -107,7 +110,7 @@
// Update the processor with one big chunk.
messageCount++;
- processor.update(frame);
+ processor.update(frame, 0, frame.length);
Expect.isNull(mc.data);
Expect.equals(0, processor._state);
@@ -116,7 +119,7 @@
// Update the processor one byte at the time.
messageCount++;
for (int i = 0; i < frame.length; i++) {
- processor.update(frame.getRange(i, 1));
+ processor.update(frame, i, 1);
}
Expect.equals(0, processor._state);
Expect.isNull(mc.data);
@@ -124,7 +127,7 @@
// Update the processor two bytes at the time.
messageCount++;
for (int i = 0; i < frame.length; i += 2) {
- processor.update(frame.getRange(i, i + 1 < frame.length ? 2 : 1));
+ processor.update(frame, i, i + 1 < frame.length ? 2 : 1);
}
Expect.equals(0, processor._state);
Expect.isNull(mc.data);
@@ -177,7 +180,7 @@
payloadSize);
frameCount++;
messageIndex += payloadSize;
- processor.update(frame);
+ processor.update(frame, 0, frame.length);
remaining -= payloadSize;
firstFrame = false;
}
diff --git a/tests/standalone/io/web_socket_secure_test.dart b/tests/standalone/io/web_socket_secure_test.dart
deleted file mode 100644
index cb78bd3..0000000
--- a/tests/standalone/io/web_socket_secure_test.dart
+++ /dev/null
@@ -1,63 +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.
-
-import "dart:io";
-import "dart:uri";
-import "dart:isolate";
-
-const SERVER_ADDRESS = "127.0.0.1";
-const HOST_NAME = "localhost";
-
-void test() {
- HttpsServer server = new HttpsServer();
- var client = new HttpClient();
-
- // Create a web socket handler and set it as the HTTP server default
- // handler.
- WebSocketHandler wsHandler = new WebSocketHandler();
- wsHandler.onOpen = (WebSocketConnection conn) {
- conn.onMessage = (Object message) => conn.send(message);
- conn.onClosed = (status, reason) {
- conn.close();
- server.close();
- client.shutdown();
- };
- };
- server.defaultRequestHandler = wsHandler.onRequest;
-
- server.onError = (Exception e) {
- Expect.fail("Unexpected error in Https Server: $e");
- };
-
- server.listen(SERVER_ADDRESS,
- 0,
- backlog: 5,
- certificate_name: "CN=$HOST_NAME");
-
- // Connect web socket over HTTPS.
- var conn = new WebSocketClientConnection(
- client.getUrl(
- Uri.parse("https://$HOST_NAME:${server.port}/")));
- conn.onOpen = () {
- conn.send("hello");
- };
- conn.onMessage = (msg) {
- Expect.equals("hello", msg);
- print(msg);
- conn.close();
- };
-
-}
-
-void InitializeSSL() {
- var testPkcertDatabase =
- new Path(new Options().script).directoryPath.append("pkcert/");
- SecureSocket.initialize(database: testPkcertDatabase.toNativePath(),
- password: "dartdart");
-}
-
-void main() {
- InitializeSSL();
- test();
-}
diff --git a/tests/standalone/io/web_socket_test.dart b/tests/standalone/io/web_socket_test.dart
index 2520992c..baa6ddd 100644
--- a/tests/standalone/io/web_socket_test.dart
+++ b/tests/standalone/io/web_socket_test.dart
@@ -1,16 +1,188 @@
-// Copyright (c) 2012, 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.
//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
-import "dart:async";
import "dart:io";
import "dart:isolate";
import "dart:scalarlist";
-import "dart:uri";
-const SERVER_ADDRESS = "127.0.0.1";
-const HOST_NAME = "localhost";
+void testRequestResponseClientCloses(
+ int totalConnections, int closeStatus, String closeReason) {
+ HttpServer.bind().then((server) {
+
+ server.transform(new WebSocketTransformer()).listen((webSocket) {
+ webSocket.listen((event) {
+ if (event is MessageEvent) {
+ webSocket.send(event.data);
+ } else if (event is CloseEvent) {
+ Expect.equals(closeStatus == null
+ ? WebSocketStatus.NO_STATUS_RECEIVED
+ : closeStatus, event.code);
+ Expect.equals(closeReason == null ? "" : closeReason, event.reason);
+ }
+ });
+ });
+
+ int closeCount = 0;
+ String messageText = "Hello, world!";
+ for (int i = 0; i < totalConnections; i++) {
+ int messageCount = 0;
+ WebSocket.connect("ws://127.0.0.1:${server.port}/")
+ .then((webSocket) {
+ webSocket.send(messageText);
+ webSocket.listen((event) {
+ if (event is MessageEvent) {
+ messageCount++;
+ if (messageCount < 1 ) {
+ Expect.equals(messageText, event.data);
+ webSocket.send(event.data);
+ } else {
+ webSocket.close(closeStatus, closeReason);
+ }
+ } else if (event is CloseEvent) {
+ Expect.equals(closeStatus == null
+ ? WebSocketStatus.NO_STATUS_RECEIVED
+ : closeStatus, event.code);
+ Expect.equals("", event.reason);
+ closeCount++;
+ if (closeCount == totalConnections) {
+ server.close();
+ }
+ }
+ });
+ });
+ }
+
+ });
+}
+
+
+void testRequestResponseServerCloses(
+ int totalConnections, int closeStatus, String closeReason) {
+ HttpServer.bind().then((server) {
+
+ int closeCount = 0;
+ server.transform(new WebSocketTransformer()).listen((webSocket) {
+ String messageText = "Hello, world!";
+ int messageCount = 0;
+ webSocket.listen((event) {
+ if (event is MessageEvent) {
+ messageCount++;
+ if (messageCount < 10) {
+ Expect.equals(messageText, event.data);
+ webSocket.send(event.data);
+ } else {
+ webSocket.close(closeStatus, closeReason);
+ }
+ } else if (event is CloseEvent) {
+ Expect.equals(closeStatus == null
+ ? WebSocketStatus.NO_STATUS_RECEIVED
+ : closeStatus, event.code);
+ Expect.equals("", event.reason);
+ closeCount++;
+ if (closeCount == totalConnections) {
+ server.close();
+ }
+ }
+ });
+ webSocket.send(messageText);
+ });
+
+ for (int i = 0; i < totalConnections; i++) {
+ WebSocket.connect("ws://127.0.0.1:${server.port}/")
+ .then((webSocket) {
+ webSocket.listen((event) {
+ if (event is MessageEvent) {
+ webSocket.send(event.data);
+ } else if (event is CloseEvent) {
+ Expect.equals(closeStatus == null
+ ? WebSocketStatus.NO_STATUS_RECEIVED
+ : closeStatus, event.code);
+ Expect.equals(
+ closeReason == null ? "" : closeReason, event.reason);
+ }
+ });
+ });
+ }
+
+ });
+}
+
+
+void testMessageLength(int messageLength) {
+ HttpServer.bind().then((server) {
+
+ Uint8List originalMessage = new Uint8List(messageLength);
+ server.transform(new WebSocketTransformer()).listen((webSocket) {
+ webSocket.listen((event) {
+ if (event is MessageEvent) {
+ Expect.listEquals(originalMessage, event.data);
+ webSocket.send(event.data);
+ } else if (event is CloseEvent) {
+ }
+ });
+ });
+
+ WebSocket.connect("ws://127.0.0.1:${server.port}/")
+ .then((webSocket) {
+ webSocket.listen((event) {
+ if (event is MessageEvent) {
+ Expect.listEquals(originalMessage, event.data);
+ webSocket.close();
+ } else if (event is CloseEvent) {
+ server.close();
+ }
+ });
+ webSocket.send(originalMessage);
+ });
+
+ });
+}
+
+
+void testNoUpgrade() {
+ HttpServer.bind().then((server) {
+
+ // Create a server which always responds with NOT_FOUND.
+ server.listen((request) {
+ request.response.statusCode = HttpStatus.NOT_FOUND;
+ request.response.close();
+ });
+
+ WebSocket.connect("ws://127.0.0.1:${server.port}/").catchError((error) {
+ server.close();
+ });
+
+ });
+}
+
+
+void testUsePOST() {
+ HttpServer.bind().then((server) {
+
+ var errorPort = new ReceivePort();
+ server.transform(new WebSocketTransformer()).listen((webSocket) {
+ Expect.fail("No connection expected");
+ }, onError: (e) {
+ errorPort.close();
+ });
+
+ HttpClient client = new HttpClient();
+ client.post("127.0.0.1", server.port, "/")
+ .then((request) => request.close())
+ .then((response) {
+ Expect.equals(HttpStatus.BAD_REQUEST, response.statusCode);
+ client.close();
+ server.close();
+ });
+
+ });
+}
class WebSocketInfo {
@@ -18,362 +190,89 @@
}
-/**
- * A SecurityConfiguration lets us run the tests over HTTP or HTTPS.
- */
-class SecurityConfiguration {
- final bool secure;
- HttpClient client;
-
- SecurityConfiguration({bool this.secure}) : client = new HttpClient();
-
- HttpServer createServer({int backlog}) {
- HttpServer server = secure ? new HttpsServer() : new HttpServer();
- server.listen(SERVER_ADDRESS,
- 0,
- backlog: backlog,
- certificate_name: "CN=$HOST_NAME");
- return server;
- }
-
- WebSocketClientConnection createClient(int port,
- {bool followRedirects,
- String method: "GET"}) {
- HttpClientConnection conn = client.openUrl(method, Uri.parse(
- '${secure ? "https" : "http"}://$HOST_NAME:$port/'));
- if (followRedirects != null) {
- conn.followRedirects = followRedirects;
- }
- return new WebSocketClientConnection(conn);
- }
-
- void testRequestResponseClientCloses(
- int totalConnections, int closeStatus, String closeReason) {
- HttpServer server = createServer(backlog: totalConnections);
- HttpClient client = new HttpClient();
-
- // Make a web socket handler and set it as the HTTP server default handler.
- WebSocketHandler wsHandler = new WebSocketHandler();
- wsHandler.onOpen = (WebSocketConnection conn) {
- var count = 0;
- conn.onMessage = (Object message) => conn.send(message);
- conn.onClosed = (status, reason) {
- Expect.equals(closeStatus == null
- ? WebSocketStatus.NO_STATUS_RECEIVED
- : closeStatus, status);
- Expect.equals(closeReason == null ? "" : closeReason, reason);
- };
- };
- server.defaultRequestHandler = wsHandler.onRequest;
+void testW3CInterface(
+ int totalConnections, int closeStatus, String closeReason) {
+ HttpServer.bind().then((server) {
int closeCount = 0;
- String messageText = "Hello, world!";
- for (int i = 0; i < totalConnections; i++) {
- int messageCount = 0;
- WebSocketClientConnection wsconn = createClient(server.port);
- wsconn.onOpen = () => wsconn.send(messageText);
- wsconn.onMessage = (message) {
- messageCount++;
- if (messageCount < 10) {
- Expect.equals(messageText, message);
- wsconn.send(message);
- } else {
- wsconn.close(closeStatus, closeReason);
- }
- };
- wsconn.onClosed = (status, reason) {
- Expect.equals(closeStatus == null
- ? WebSocketStatus.NO_STATUS_RECEIVED
- : closeStatus, status);
- Expect.equals("", reason);
- closeCount++;
- if (closeCount == totalConnections) {
- client.shutdown();
- server.close();
- }
- };
- }
- }
-
-
- void testRequestResponseServerCloses(
- int totalConnections, int closeStatus, String closeReason) {
- ReceivePort keepAlive = new ReceivePort();
- HttpServer server = createServer(backlog: totalConnections);
-
- // Create a web socket handler and set is as the HTTP server default
- // handler.
- int closeCount = 0;
- WebSocketHandler wsHandler = new WebSocketHandler();
- wsHandler.onOpen = (WebSocketConnection conn) {
+ server.transform(new WebSocketTransformer()).listen((webSocket) {
String messageText = "Hello, world!";
int messageCount = 0;
- conn.onMessage = (Object message) {
- messageCount++;
- if (messageCount < 10) {
- Expect.equals(messageText, message);
- conn.send(message);
- } else {
- conn.close(closeStatus, closeReason);
+ webSocket.listen((event) {
+ if (event is MessageEvent) {
+ messageCount++;
+ if (messageCount < 10) {
+ Expect.equals(messageText, event.data);
+ webSocket.send(event.data);
+ } else {
+ webSocket.close(closeStatus, closeReason);
+ }
+ } else if (event is CloseEvent) {
+ Expect.equals(closeStatus, event.code);
+ Expect.equals("", event.reason);
+ closeCount++;
+ if (closeCount == totalConnections) {
+ server.close();
+ }
}
- };
- conn.onClosed = (status, reason) {
- Expect.equals(closeStatus == null
- ? WebSocketStatus.NO_STATUS_RECEIVED
- : closeStatus, status);
- Expect.equals("", reason);
- closeCount++;
- if (closeCount == totalConnections) {
- client.shutdown();
- server.close();
- keepAlive.close();
- }
- };
- conn.send(messageText);
- };
- server.defaultRequestHandler = wsHandler.onRequest;
-
- for (int i = 0; i < totalConnections; i++) {
- WebSocketClientConnection wsconn = createClient(server.port);
- wsconn.onMessage = (message) => wsconn.send(message);
- wsconn.onClosed = (status, reason) {
- Expect.equals(closeStatus == null
- ? WebSocketStatus.NO_STATUS_RECEIVED
- : closeStatus, status);
- Expect.equals(closeReason == null ? "" : closeReason, reason);
- };
- }
- }
-
-
- void testMessageLength(int messageLength) {
- HttpServer server = createServer(backlog: 1);
- bool serverReceivedMessage = false;
- bool clientReceivedMessage = false;
-
- // Create a web socket handler and set is as the HTTP server default
- // handler.
- Uint8List originalMessage = new Uint8List(messageLength);
- WebSocketHandler wsHandler = new WebSocketHandler();
- wsHandler.onOpen = (WebSocketConnection conn) {
- conn.onMessage = (Object message) {
- serverReceivedMessage = true;
- Expect.listEquals(originalMessage, message);
- conn.send(message);
- };
- conn.onClosed = (status, reason) {
- };
- };
- server.defaultRequestHandler = wsHandler.onRequest;
-
- WebSocketClientConnection wsconn = createClient(server.port);
- wsconn.onMessage = (message) {
- clientReceivedMessage = true;
- Expect.listEquals(originalMessage, message);
- wsconn.close();
- };
- wsconn.onClosed = (status, reason) {
- Expect.isTrue(serverReceivedMessage);
- Expect.isTrue(clientReceivedMessage);
- client.shutdown();
- server.close();
- };
- wsconn.onOpen = () {
- wsconn.send(originalMessage);
- };
- }
-
-
- void testNoUpgrade() {
- HttpServer server = createServer(backlog: 5);
-
- // Create a server which always responds with a redirect.
- server.defaultRequestHandler = (request, response) {
- response.statusCode = HttpStatus.MOVED_PERMANENTLY;
- response.outputStream.close();
- };
-
- WebSocketClientConnection wsconn = createClient(server.port,
- followRedirects: false);
- wsconn.onNoUpgrade = (response) {
- Expect.equals(HttpStatus.MOVED_PERMANENTLY, response.statusCode);
- client.shutdown();
- server.close();
- };
- }
-
-
- void testUsePOST() {
- HttpServer server = createServer(backlog: 5);
-
- // Create a web socket handler and set is as the HTTP server default
- // handler.
- int closeCount = 0;
- WebSocketHandler wsHandler = new WebSocketHandler();
- wsHandler.onOpen = (WebSocketConnection conn) {
- Expect.fail("No connection expected");
- };
- server.defaultRequestHandler = wsHandler.onRequest;
-
- WebSocketClientConnection wsconn = createClient(server.port,
- method: "POST");
- wsconn.onNoUpgrade = (response) {
- Expect.equals(HttpStatus.BAD_REQUEST, response.statusCode);
- client.shutdown();
- server.close();
- };
- }
-
-
- void testHashCode(int totalConnections) {
- ReceivePort keepAlive = new ReceivePort();
- HttpServer server = createServer(backlog: totalConnections);
- Map connections = new Map();
-
- void handleMessage(conn, message) {
- var info = connections[conn];
- Expect.isNotNull(info);
- info.messageCount++;
- if (info.messageCount < 10) {
- conn.send(message);
- } else {
- conn.close();
- }
- }
-
- // Create a web socket handler and set is as the HTTP server default
- // handler.
- int closeCount = 0;
- WebSocketHandler wsHandler = new WebSocketHandler();
- wsHandler.onOpen = (WebSocketConnection conn) {
- connections[conn] = new WebSocketInfo();
- String messageText = "Hello, world!";
- conn.onMessage = (Object message) {
- handleMessage(conn, message);
- };
- conn.onClosed = (status, reason) {
- closeCount++;
- var info = connections[conn];
- Expect.equals(10, info.messageCount);
- if (closeCount == totalConnections) {
- client.shutdown();
- server.close();
- keepAlive.close();
- }
- };
- conn.send(messageText);
- };
- server.defaultRequestHandler = wsHandler.onRequest;
-
- for (int i = 0; i < totalConnections; i++) {
- WebSocketClientConnection wsconn = createClient(server.port);
- wsconn.onMessage = (message) => wsconn.send(message);
- }
- }
-
-
- void testW3CInterface(
- int totalConnections, int closeStatus, String closeReason) {
- HttpServer server = createServer(backlog: totalConnections);
-
- // Create a web socket handler and set is as the HTTP server default
- // handler.
- int closeCount = 0;
- WebSocketHandler wsHandler = new WebSocketHandler();
- wsHandler.onOpen = (WebSocketConnection conn) {
- String messageText = "Hello, world!";
- int messageCount = 0;
- conn.onMessage = (Object message) {
- messageCount++;
- if (messageCount < 10) {
- Expect.equals(messageText, message);
- conn.send(message);
- } else {
- conn.close(closeStatus, closeReason);
- }
- };
- conn.onClosed = (status, reason) {
- Expect.equals(closeStatus, status);
- Expect.equals("", reason);
- closeCount++;
- if (closeCount == totalConnections) {
- server.close();
- }
- };
- conn.send(messageText);
- };
- server.defaultRequestHandler = wsHandler.onRequest;
+ });
+ webSocket.send(messageText);
+ });
void webSocketConnection() {
bool onopenCalled = false;
int onmessageCalled = 0;
bool oncloseCalled = false;
- var websocket =
- new WebSocket('${secure ? "wss" : "ws"}://$HOST_NAME:${server.port}');
- Expect.equals(WebSocket.CONNECTING, websocket.readyState);
- websocket.onopen = () {
+ WebSocket.connect("ws://127.0.0.1:${server.port}").then((webSocket) {
Expect.isFalse(onopenCalled);
Expect.equals(0, onmessageCalled);
Expect.isFalse(oncloseCalled);
onopenCalled = true;
- Expect.equals(WebSocket.OPEN, websocket.readyState);
- };
- websocket.onmessage = (event) {
- onmessageCalled++;
- Expect.isTrue(onopenCalled);
- Expect.isFalse(oncloseCalled);
- Expect.equals(WebSocket.OPEN, websocket.readyState);
- websocket.send(event.data);
- };
- websocket.onclose = (event) {
- Expect.isTrue(onopenCalled);
- Expect.equals(10, onmessageCalled);
- Expect.isFalse(oncloseCalled);
- oncloseCalled = true;
- Expect.isTrue(event.wasClean);
- Expect.equals(3002, event.code);
- Expect.equals("Got tired", event.reason);
- Expect.equals(WebSocket.CLOSED, websocket.readyState);
- };
+ Expect.equals(WebSocket.OPEN, webSocket.readyState);
+ webSocket.listen((event) {
+ if (event is MessageEvent) {
+ onmessageCalled++;
+ Expect.isTrue(onopenCalled);
+ Expect.isFalse(oncloseCalled);
+ Expect.equals(WebSocket.OPEN, webSocket.readyState);
+ webSocket.send(event.data);
+ } else if (event is CloseEvent) {
+ Expect.isTrue(onopenCalled);
+ Expect.equals(10, onmessageCalled);
+ Expect.isFalse(oncloseCalled);
+ oncloseCalled = true;
+ Expect.isTrue(event.wasClean);
+ Expect.equals(3002, event.code);
+ Expect.equals("Got tired", event.reason);
+ Expect.equals(WebSocket.CLOSED, webSocket.readyState);
+ }
+ });
+ });
}
for (int i = 0; i < totalConnections; i++) {
webSocketConnection();
}
- }
-
- runTests() {
- testRequestResponseClientCloses(2, null, null);
- testRequestResponseClientCloses(2, 3001, null);
- testRequestResponseClientCloses(2, 3002, "Got tired");
- testRequestResponseServerCloses(2, null, null);
- testRequestResponseServerCloses(2, 3001, null);
- testRequestResponseServerCloses(2, 3002, "Got tired");
- testMessageLength(125);
- testMessageLength(126);
- testMessageLength(127);
- testMessageLength(65535);
- testMessageLength(65536);
- testNoUpgrade();
- testUsePOST();
- testHashCode(2);
- testW3CInterface(2, 3002, "Got tired");
- }
-}
-
-
-void initializeSSL() {
- var testPkcertDatabase =
- new Path(new Options().script).directoryPath.append("pkcert/");
- SecureSocket.initialize(database: testPkcertDatabase.toNativePath(),
- password: "dartdart");
+ });
}
main() {
- new SecurityConfiguration(secure: false).runTests();
- initializeSSL();
- new SecurityConfiguration(secure: true).runTests();
+ testRequestResponseClientCloses(2, null, null);
+ testRequestResponseClientCloses(2, 3001, null);
+ testRequestResponseClientCloses(2, 3002, "Got tired");
+ testRequestResponseServerCloses(2, null, null);
+ testRequestResponseServerCloses(2, 3001, null);
+ testRequestResponseServerCloses(2, 3002, "Got tired");
+ testMessageLength(125);
+ testMessageLength(126);
+ testMessageLength(127);
+ testMessageLength(65535);
+ testMessageLength(65536);
+ testNoUpgrade();
+ testUsePOST();
+
+ testW3CInterface(2, 3002, "Got tired");
}
diff --git a/tests/standalone/left_shift_bit_and_op_test.dart b/tests/standalone/left_shift_bit_and_op_test.dart
new file mode 100644
index 0000000..c9e142a
--- /dev/null
+++ b/tests/standalone/left_shift_bit_and_op_test.dart
@@ -0,0 +1,75 @@
+// 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.
+//
+// Tests optimizing (a << b) & c if c is a Smi constant.
+
+main() {
+ checkshiftAnd32();
+ checkShiftAnd64();
+ // Optimize shiftAnd32.
+ for (int i = 0; i < 10000; i++) {
+ A.shiftAnd32(12, 17);
+ A.shiftAnd64(12, 17);
+ Expect.equals(72, A.multipleConstantUses(3, 4));
+ Expect.equals(34493956096, A.multipleShiftUse(134742016, 8));
+ }
+ checkshiftAnd32();
+ checkShiftAnd64();
+
+ Expect.throws(() => A.shiftAnd32(12, -5));
+
+ // Check environment dependency.
+ final a = new A(), b = new B();
+ for (var i = 0; i < 10000; i++) {
+ Expect.equals(0, bar(a));
+ }
+ Expect.equals(4294967296, bar(b));
+}
+
+
+checkshiftAnd32() {
+ Expect.equals(1572864, A.shiftAnd32(12, 17));
+ Expect.equals(12, A.shiftAnd32(12, 0));
+ Expect.equals(285212672, A.shiftAnd32(16779392, 17));
+}
+
+
+checkShiftAnd64() {
+ Expect.equals(1125936481173504, A.shiftAnd64(4611694814806147072, 7));
+}
+
+
+class A {
+ static const int MASK_32 = (1 << 30) - 1;
+ static const int MASK_64 = (1 << 62) - 1;
+
+ static shiftAnd32(a, c) {
+ return (a << c) & MASK_32;
+ }
+
+ static shiftAnd64(a, c) {
+ return (a << c) & MASK_64;
+ }
+
+ static multipleConstantUses(a, c) {
+ var j = (a << c) & 0xFF;
+ var k = (a << 3) & 0xFF;
+ return j + k;
+ }
+
+ // Make sure that left shift is nor marked as truncating.
+ static multipleShiftUse(a, c) {
+ var y = (a << c);
+ var x = y & 0x7F;
+ return y + x;
+ }
+
+ foo(x) { return x & 0xf; }
+}
+
+class B { foo(x) { return x; } }
+
+bar (o) {
+ return o.foo(1 << 32);
+}
diff --git a/tests/standalone/standalone.status b/tests/standalone/standalone.status
index cbaa158..0bedddf 100644
--- a/tests/standalone/standalone.status
+++ b/tests/standalone/standalone.status
@@ -5,8 +5,30 @@
package/invalid_uri_test: Fail, OK # Fails intentionally
[ $runtime == vm ]
-package/package_isolate_test: Fail # Issue 7520.
+# Temporarily disabled until the test scripts are rewritten for
+# dart:io v2.
+io/skipping_dart2js_compilations_test: skip # Non-fatal process error.
+io/status_file_parser_test: fail
+io/test_runner_exit_code_test: fail
+io/test_runner_test: fail
+
+[ $runtime == vm ]
+io/secure_server_client_certificate_test: fail
+
+[ $runtime == vm && ( $system == windows ) ]
io/web_socket_test: Fail, Pass # Issue 8495.
+io/https_client_certificate_test: Pass, Timeout # Issue 8674
+io/raw_socket_test: Pass, Timeout # Issue 8675
+io/socket_test: Pass, Timeout # Issue 8676
+io/socket_exception_test: Pass, Timeout # Issue 8677
+io/https_client_test: Pass, Timeout # Issue 8674
+io/https_server_test: Pass, Timeout # Issue 8674
+io/https_client_socket_reuse_test: Pass, Timeout # Issue 8674
+io/http_client_request_test: Pass, Fail # Issue 8681
+io/http_proxy_test: Pass, Timeout # Issue 7773
+
+[ $runtime == vm ]
+package/package_isolate_test: Fail # http://dartbug.com/7520.
[ $runtime == vm && $checked ]
# These tests have type errors on purpose.
@@ -19,7 +41,7 @@
io/file_fuzz_test: Skip
io/directory_fuzz_test: Skip
-[ $runtime == vm && $system == macos && $arch == x64 ]
+[ $runtime == vm && $system == macos ]
io/regress_7191_test: Pass, Timeout # http://dartbug.com/8091
[ $runtime == vm ]
@@ -45,7 +67,6 @@
[ $runtime == vm && $system == windows ]
io/file_system_links_test: Skip # No links on Windows.
-io/socket_stream_close_test: Fail, Pass # Issue 8556.
[ $compiler == none && $runtime == drt ]
io/*: Skip # Don't run tests using dart:io in the browser
@@ -88,6 +109,7 @@
io/process_exit_negative_test: Fail, OK # relies on a static error that is a warning now.
package/package_isolate_test: Skip # spawnUri does not work in dart2js. See issue 3051
debugger/*: Skip # Do not run standalone vm debugger tests with dart2js.
+left_shift_bit_and_op_test: Skip # Integers exceed dart2js precision.
[ $compiler == dart2js && $jscl ]
assert_test: Fail, OK # Assumes unspecified fields on the AssertionError.
diff --git a/tests/utils/utils.status b/tests/utils/utils.status
index bea35e0..4d6e186 100644
--- a/tests/utils/utils.status
+++ b/tests/utils/utils.status
@@ -24,8 +24,6 @@
# Skip until we stabilize language tests.
*: Skip
-[ $compiler == dartc ]
-
[ $arch == arm ]
*: Skip
diff --git a/tools/VERSION b/tools/VERSION
index f532fc3..2288fc6 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -1,4 +1,4 @@
MAJOR 0
-MINOR 3
-BUILD 7
-PATCH 6
+MINOR 4
+BUILD 0
+PATCH 0
diff --git a/tools/build.py b/tools/build.py
index 84aa62b..b60579c 100755
--- a/tools/build.py
+++ b/tools/build.py
@@ -40,13 +40,14 @@
help='The number of parallel jobs to run.',
metavar=HOST_CPUS,
default=str(HOST_CPUS))
+ (vs_directory, vs_executable) = utils.GuessVisualStudioPath()
result.add_option("--devenv",
help='Path containing devenv.com on Windows',
- default='C:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7'
- '\\IDE')
+ default=vs_directory)
result.add_option("--executable",
help='Name of the devenv.com/msbuild executable on Windows (varies for '
- 'different versions of Visual Studio)', default='devenv.com')
+ 'different versions of Visual Studio)',
+ default=vs_executable)
return result
diff --git a/tools/create_sdk.py b/tools/create_sdk.py
index a8df879..4e87ffb 100755
--- a/tools/create_sdk.py
+++ b/tools/create_sdk.py
@@ -208,7 +208,8 @@
'json', 'math', 'mirrors', 'scalarlist',
join('svg', 'dart2js'), join('svg', 'dartium'),
'uri', 'utf',
- join('web_audio', 'dart2js'), join('web_audio', 'dartium')]:
+ join('web_audio', 'dart2js'), join('web_audio', 'dartium'),
+ join('web_sql', 'dart2js'), join('web_sql', 'dartium')]:
copytree(join(HOME, 'sdk', 'lib', library), join(LIB, library),
ignore=ignore_patterns('*.svn', 'doc', '*.py', '*.gypi', '*.sh'))
diff --git a/tools/dom/scripts/dartdomgenerator.py b/tools/dom/scripts/dartdomgenerator.py
index 9007790..b5f63cc 100755
--- a/tools/dom/scripts/dartdomgenerator.py
+++ b/tools/dom/scripts/dartdomgenerator.py
@@ -29,7 +29,7 @@
_logger = logging.getLogger('dartdomgenerator')
-_libraries = ['chrome', 'html', 'indexed_db', 'svg', 'web_audio']
+_libraries = ['chrome', 'html', 'indexed_db', 'svg', 'web_audio', 'web_sql']
class GeneratorOptions(object):
def __init__(self, templates, database, type_registry, renamer):
diff --git a/tools/dom/scripts/generator.py b/tools/dom/scripts/generator.py
index 0fc3789..fa6fcac 100644
--- a/tools/dom/scripts/generator.py
+++ b/tools/dom/scripts/generator.py
@@ -541,8 +541,8 @@
],
'DOMWindow.openDatabase': [
- "@Creates('Database')",
- "@Creates('DatabaseSync')",
+ "@Creates('SqlDatabase')",
+ "@Creates('SqlDatabaseSync')",
],
# To be in callback with the browser-created Event, we had to have called
diff --git a/tools/dom/scripts/htmlrenamer.py b/tools/dom/scripts/htmlrenamer.py
index 32bb712..c312a9f 100644
--- a/tools/dom/scripts/htmlrenamer.py
+++ b/tools/dom/scripts/htmlrenamer.py
@@ -9,6 +9,8 @@
html_interface_renames = monitored.Dict('htmlrenamer.html_interface_renames', {
'CDATASection': 'CDataSection',
'Clipboard': 'DataTransfer',
+ 'Database': 'SqlDatabase', # Avoid conflict with Index DB's Database.
+ 'DatabaseSync': 'SqlDatabaseSync',
'DOMApplicationCache': 'ApplicationCache',
'DOMCoreException': 'DomException',
'DOMFileSystem': 'FileSystem',
@@ -21,6 +23,8 @@
'NamedNodeMap': '_NamedNodeMap',
'NavigatorUserMediaErrorCallback': '_NavigatorUserMediaErrorCallback',
'NavigatorUserMediaSuccessCallback': '_NavigatorUserMediaSuccessCallback',
+ 'PositionCallback': '_PositionCallback',
+ 'PositionErrorCallback': '_PositionErrorCallback',
'SVGDocument': 'SvgDocument', # Manual to avoid name conflicts.
'SVGElement': 'SvgElement', # Manual to avoid name conflicts.
'SVGException': 'SvgException', # Manual of avoid conflict with Exception.
@@ -146,6 +150,9 @@
'Event.initEvent',
'EventTarget.addEventListener',
'EventTarget.removeEventListener',
+ 'Geolocation.clearWatch',
+ 'Geolocation.getCurrentPosition',
+ 'Geolocation.watchPosition',
'HashChangeEvent.initHashChangeEvent',
'IDBFactory.deleteDatabase',
'IDBFactory.open',
@@ -569,6 +576,10 @@
return 'svg'
if 'INDEXED_DATABASE' in interface.ext_attrs['Conditional']:
return 'indexed_db'
+ if 'SQL_DATABASE' in interface.ext_attrs['Conditional']:
+ # WorkerContext has attributes merged in from many other interfaces.
+ if interface.id != 'WorkerContext':
+ return 'web_sql'
return 'html'
diff --git a/tools/dom/scripts/systemhtml.py b/tools/dom/scripts/systemhtml.py
index 0ef461e..e3ba590 100644
--- a/tools/dom/scripts/systemhtml.py
+++ b/tools/dom/scripts/systemhtml.py
@@ -58,6 +58,7 @@
'Navigator.webkitGetUserMedia',
'URL.createObjectURL',
'URL.revokeObjectURL',
+ 'WheelEvent.deltaMode',
'WheelEvent.wheelDeltaX',
'WheelEvent.wheelDeltaY',
'Window.cancelAnimationFrame',
@@ -260,6 +261,7 @@
'indexed_db': {},
'svg': _svg_element_constructors,
'web_audio': {},
+ 'web_sql': {},
}
_factory_ctr_strings = {
@@ -279,6 +281,10 @@
'provider_name': 'document',
'constructor_name': '$dom_createElement'
},
+ 'web_sql': {
+ 'provider_name': 'document',
+ 'constructor_name': '$dom_createElement'
+ },
}
def ElementConstructorInfos(typename, element_constructors,
diff --git a/tools/dom/templates/html/dart2js/chrome_dart2js.darttemplate b/tools/dom/templates/html/dart2js/chrome_dart2js.darttemplate
index 1752434..6449f29 100644
--- a/tools/dom/templates/html/dart2js/chrome_dart2js.darttemplate
+++ b/tools/dom/templates/html/dart2js/chrome_dart2js.darttemplate
@@ -3,7 +3,8 @@
// 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.
-// DO NOT EDIT
+// DO NOT EDIT - unless you are editing documentation as per:
+// https://code.google.com/p/dart/wiki/ContributingHTMLDocumentation
// Auto-generated dart:chrome library.
/// Native wrappers for the Chrome Packaged App APIs.
@@ -26,4 +27,4 @@
// Generated files below this line.
part "$AUXILIARY_DIR/chrome/app_window.dart";
-part "$AUXILIARY_DIR/chrome/app_runtime.dart";
\ No newline at end of file
+part "$AUXILIARY_DIR/chrome/app_runtime.dart";
diff --git a/tools/dom/templates/html/dart2js/html_dart2js.darttemplate b/tools/dom/templates/html/dart2js/html_dart2js.darttemplate
index 20350cc..2823cdd 100644
--- a/tools/dom/templates/html/dart2js/html_dart2js.darttemplate
+++ b/tools/dom/templates/html/dart2js/html_dart2js.darttemplate
@@ -2,7 +2,8 @@
// 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.
-// DO NOT EDIT
+// DO NOT EDIT - unless you are editing documentation as per:
+// https://code.google.com/p/dart/wiki/ContributingHTMLDocumentation
// Auto-generated dart:html library.
/// The Dart HTML library.
@@ -15,6 +16,7 @@
import 'dart:isolate';
import 'dart:json' as json;
import 'dart:math';
+import 'dart:web_sql';
// Not actually used, but imported since dart:html can generate these objects.
import 'dart:svg' as svg;
import 'dart:web_audio' as web_audio;
diff --git a/tools/dom/templates/html/dart2js/indexed_db_dart2js.darttemplate b/tools/dom/templates/html/dart2js/indexed_db_dart2js.darttemplate
index cf7542f..0d29c1c 100644
--- a/tools/dom/templates/html/dart2js/indexed_db_dart2js.darttemplate
+++ b/tools/dom/templates/html/dart2js/indexed_db_dart2js.darttemplate
@@ -2,7 +2,8 @@
// 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.
-// DO NOT EDIT
+// DO NOT EDIT - unless you are editing documentation as per:
+// https://code.google.com/p/dart/wiki/ContributingHTMLDocumentation
// Auto-generated dart:svg library.
library dart.dom.indexed_db;
diff --git a/tools/dom/templates/html/dart2js/svg_dart2js.darttemplate b/tools/dom/templates/html/dart2js/svg_dart2js.darttemplate
index d907998..eedcc3c 100644
--- a/tools/dom/templates/html/dart2js/svg_dart2js.darttemplate
+++ b/tools/dom/templates/html/dart2js/svg_dart2js.darttemplate
@@ -1,4 +1,5 @@
-// DO NOT EDIT
+// DO NOT EDIT - unless you are editing documentation as per:
+// https://code.google.com/p/dart/wiki/ContributingHTMLDocumentation
// Auto-generated dart:svg library.
library dart.dom.svg;
diff --git a/tools/dom/templates/html/dart2js/web_audio_dart2js.darttemplate b/tools/dom/templates/html/dart2js/web_audio_dart2js.darttemplate
index 17c774a..8cc027d 100644
--- a/tools/dom/templates/html/dart2js/web_audio_dart2js.darttemplate
+++ b/tools/dom/templates/html/dart2js/web_audio_dart2js.darttemplate
@@ -1,4 +1,5 @@
-// DO NOT EDIT
+// DO NOT EDIT - unless you are editing documentation as per:
+// https://code.google.com/p/dart/wiki/ContributingHTMLDocumentation
// Auto-generated dart:audio library.
library dart.dom.web_audio;
diff --git a/tools/dom/templates/html/dart2js/web_sql_dart2js.darttemplate b/tools/dom/templates/html/dart2js/web_sql_dart2js.darttemplate
new file mode 100644
index 0000000..2c6fec6
--- /dev/null
+++ b/tools/dom/templates/html/dart2js/web_sql_dart2js.darttemplate
@@ -0,0 +1,14 @@
+// DO NOT EDIT - unless you are editing documentation as per:
+// https://code.google.com/p/dart/wiki/ContributingHTMLDocumentation
+// Auto-generated dart:audio library.
+
+library dart.dom.web_sql;
+
+import 'dart:async';
+import 'dart:collection';
+import 'dart:html';
+import 'dart:html_common';
+import 'dart:_js_helper' show convertDartClosureToJS, Creates, JavaScriptIndexingBehavior, JSName;
+import 'dart:_foreign_helper' show JS;
+
+$!GENERATED_DART_FILES
diff --git a/tools/dom/templates/html/dartium/html_dartium.darttemplate b/tools/dom/templates/html/dartium/html_dartium.darttemplate
index 825b6c8..4e2b5e7 100644
--- a/tools/dom/templates/html/dartium/html_dartium.darttemplate
+++ b/tools/dom/templates/html/dartium/html_dartium.darttemplate
@@ -14,6 +14,7 @@
import 'dart:isolate';
import 'dart:json' as json;
import 'dart:nativewrappers';
+import 'dart:web_sql';
// Not actually used, but imported since dart:html can generate these objects.
import 'dart:svg' as svg;
import 'dart:web_audio' as web_audio;
diff --git a/tools/dom/templates/html/dartium/web_sql_dartium.darttemplate b/tools/dom/templates/html/dartium/web_sql_dartium.darttemplate
new file mode 100644
index 0000000..0556070
--- /dev/null
+++ b/tools/dom/templates/html/dartium/web_sql_dartium.darttemplate
@@ -0,0 +1,13 @@
+// DO NOT EDIT - unless you are editing documentation as per:
+// https://code.google.com/p/dart/wiki/ContributingHTMLDocumentation
+// Auto-generated dart:audio library.
+
+library dart.dom.web_sql;
+
+import 'dart:async';
+import 'dart:collection';
+import 'dart:html';
+import 'dart:html_common';
+import 'dart:nativewrappers';
+
+$!GENERATED_DART_FILES
diff --git a/tools/dom/templates/html/impl/impl_Element.darttemplate b/tools/dom/templates/html/impl/impl_Element.darttemplate
index 605731c..3e0080a 100644
--- a/tools/dom/templates/html/impl/impl_Element.darttemplate
+++ b/tools/dom/templates/html/impl/impl_Element.darttemplate
@@ -495,7 +495,7 @@
_ElementCssClassSet(this._element);
Set<String> readClasses() {
- var s = new Set<String>();
+ var s = new LinkedHashSet<String>();
var classname = _element.$dom_className;
for (String name in classname.split(' ')) {
diff --git a/tools/dom/templates/html/impl/impl_Geolocation.darttemplate b/tools/dom/templates/html/impl/impl_Geolocation.darttemplate
new file mode 100644
index 0000000..52dac0c
--- /dev/null
+++ b/tools/dom/templates/html/impl/impl_Geolocation.darttemplate
@@ -0,0 +1,103 @@
+// 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.
+
+part of $LIBRARYNAME;
+
+@DocsEditable
+$(ANNOTATIONS)class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC {
+
+ @DomName('Geolocation.getCurrentPosition')
+ Future<Geoposition> getCurrentPosition({bool enableHighAccuracy,
+ Duration timeout, Duration maximumAge}) {
+ var options = {};
+ if (enableHighAccuracy != null) {
+ options['enableHighAccuracy'] = enableHighAccuracy;
+ }
+ if (timeout != null) {
+ options['timeout'] = timeout.inMilliseconds;
+ }
+ if (maximumAge != null) {
+ options['maximumAge'] = maximumAge.inMilliseconds;
+ }
+ var completer = new Completer<Geoposition>();
+ try {
+ $dom_getCurrentPosition(
+ (position) {
+ completer.complete(_ensurePosition(position));
+ },
+ (error) {
+ completer.completeError(error);
+ },
+ options);
+ } catch (e, stacktrace) {
+ completer.completeError(e, stacktrace);
+ }
+ return completer.future;
+ }
+
+ @DomName('Geolocation.watchPosition')
+ Stream<Geoposition> watchPosition({bool enableHighAccuracy,
+ Duration timeout, Duration maximumAge}) {
+
+ var options = {};
+ if (enableHighAccuracy != null) {
+ options['enableHighAccuracy'] = enableHighAccuracy;
+ }
+ if (timeout != null) {
+ options['timeout'] = timeout.inMilliseconds;
+ }
+ if (maximumAge != null) {
+ options['maximumAge'] = maximumAge.inMilliseconds;
+ }
+
+ int watchId;
+ var controller;
+ controller = new StreamController<Geoposition>(
+ onSubscriptionStateChange: () {
+ if (controller.hasSubscribers) {
+ assert(watchId == null);
+ watchId = $dom_watchPosition(
+ (position) {
+ controller.add(_ensurePosition(position));
+ },
+ (error) {
+ controller.signalError(error);
+ },
+ options);
+ } else {
+ assert(watchId != null);
+ $dom_clearWatch(watchId);
+ }
+ });
+
+ return controller.stream;
+ }
+
+ Geoposition _ensurePosition(domPosition) {
+ try {
+ // Firefox may throw on this.
+ if (domPosition is Geoposition) {
+ return domPosition;
+ }
+ } catch(e) {}
+ return new _GeopositionWrapper(domPosition);
+ }
+$!MEMBERS}
+
+$if DART2JS
+/**
+ * Wrapper for Firefox- it returns an object which we cannot map correctly.
+ * Basically Firefox was returning a [xpconnect wrapped nsIDOMGeoPosition] but
+ * which has further oddities.
+ */
+class _GeopositionWrapper implements Geoposition {
+ var _ptr;
+ _GeopositionWrapper(this._ptr);
+
+ Coordinates get coords => JS('Coordinates', '#.coords', _ptr);
+ int get timestamp => JS('int', '#.timestamp', _ptr);
+}
+$endif
+
+
diff --git a/tools/dom/templates/html/impl/impl_SVGElement.darttemplate b/tools/dom/templates/html/impl/impl_SVGElement.darttemplate
index 61cf7f6..9ab3886 100644
--- a/tools/dom/templates/html/impl/impl_SVGElement.darttemplate
+++ b/tools/dom/templates/html/impl/impl_SVGElement.darttemplate
@@ -12,7 +12,7 @@
Set<String> readClasses() {
var classname = _element.attributes['class'];
- Set<String> s = new Set<String>();
+ Set<String> s = new LinkedHashSet<String>();
if (classname == null) {
return s;
}
diff --git a/tools/dom/templates/html/impl/impl_WheelEvent.darttemplate b/tools/dom/templates/html/impl/impl_WheelEvent.darttemplate
index a3cf320..c49d8f6 100644
--- a/tools/dom/templates/html/impl/impl_WheelEvent.darttemplate
+++ b/tools/dom/templates/html/impl/impl_WheelEvent.darttemplate
@@ -134,12 +134,13 @@
'deltaX is not supported');
}
+ @DomName('WheelEvent.deltaMode')
int get deltaMode {
- if (JS('bool', '!!#.deltaMode', this)) {
- // If not available then we're poly-filling and doing pixel scroll.
- return 0;
+ if (JS('bool', '!!(#.deltaMode)', this)) {
+ return JS('int', '#.deltaMode', this);
}
- return this._deltaMode;
+ // If not available then we're poly-filling and doing pixel scroll.
+ return 0;
}
num get _deltaY => JS('num', '#.deltaY', this);
@@ -147,7 +148,6 @@
num get _wheelDelta => JS('num', '#.wheelDelta', this);
num get _wheelDeltaX => JS('num', '#.wheelDeltaX', this);
num get _detail => JS('num', '#.detail', this);
- int get _deltaMode => JS('int', '#.deltaMode', this);
bool get _hasInitMouseScrollEvent =>
JS('bool', '!!(#.initMouseScrollEvent)', this);
@@ -197,8 +197,5 @@
num get deltaX => $dom_wheelDeltaX;
@DomName('WheelEvent.deltaY')
num get deltaY => $dom_wheelDeltaY;
- @DomName('WheelEvent.deltaMode')
- int get deltaMode => 0;
-
$endif
}
diff --git a/tools/gyp/all.gypi b/tools/gyp/all.gypi
index 236d48e..d75e339 100644
--- a/tools/gyp/all.gypi
+++ b/tools/gyp/all.gypi
@@ -30,6 +30,5 @@
'xcode.gypi',
'msvs.gypi',
'configurations.gypi',
- 'source_filter.gypi',
],
}
diff --git a/tools/gyp/common.gypi b/tools/gyp/common.gypi
index 861efe0..adfc1db 100644
--- a/tools/gyp/common.gypi
+++ b/tools/gyp/common.gypi
@@ -33,6 +33,5 @@
'xcode.gypi',
'msvs.gypi',
'configurations.gypi',
- 'source_filter.gypi',
],
}
diff --git a/tools/line_doc_comments.dart b/tools/line_doc_comments.dart
index aa7627c..a51d84d 100755
--- a/tools/line_doc_comments.dart
+++ b/tools/line_doc_comments.dart
@@ -22,11 +22,14 @@
}
var dir = new Directory(args[0]);
- var lister = dir.list(recursive: true);
- lister.onFile = (file) {
- if (path.extension(file) != '.dart') return;
- fixFile(file);
- };
+ dir.list(recursive: true).listen(
+ (entity) {
+ if (entity is File) {
+ var file = entity.name;
+ if (path.extension(file) != '.dart') return;
+ fixFile(file);
+ }
+ });
}
void fixFile(String path) {
diff --git a/tools/test-runtime.dart b/tools/test-runtime.dart
index 867d39e..32e4e07 100755
--- a/tools/test-runtime.dart
+++ b/tools/test-runtime.dart
@@ -73,6 +73,8 @@
}
var testSuites = new List<TestSuite>();
+ TestingServerRunner.setBuildDir(firstConf);
+ TestingServerRunner.setPackageRootDir(firstConf);
for (var conf in configurations) {
if (selectors.containsKey('co19')) {
testSuites.add(new Co19TestSuite(conf));
diff --git a/tools/test.dart b/tools/test.dart
index 1f88fd7..398230d 100755
--- a/tools/test.dart
+++ b/tools/test.dart
@@ -126,6 +126,10 @@
}
var testSuites = new List<TestSuite>();
+ // FIXME(kustermann,ricow): This is broken and should be fixed ASAP.
+ // Issue: 8366
+ TestingServerRunner.setBuildDir(firstConf);
+ TestingServerRunner.setPackageRootDir(firstConf);
var maxBrowserProcesses = maxProcesses;
for (var conf in configurations) {
// There should not be more than one InternetExplorerDriver instance
@@ -134,7 +138,6 @@
if (conf['runtime'].startsWith('ie')) {
maxBrowserProcesses = 1;
}
- TestingServerRunner.setPackageRootDir(conf);
for (String key in selectors.keys) {
if (key == 'co19') {
testSuites.add(new Co19TestSuite(conf));
diff --git a/tools/testing/dart/browser_test.dart b/tools/testing/dart/browser_test.dart
index 6bfc93a..5e7c715 100644
--- a/tools/testing/dart/browser_test.dart
+++ b/tools/testing/dart/browser_test.dart
@@ -5,8 +5,6 @@
part of test_suite;
String getHtmlContents(String title,
- Path controllerScript,
- Path dartJsScript,
String scriptType,
Path sourceScript) =>
"""
@@ -24,10 +22,13 @@
</head>
<body>
<h1> Running $title </h1>
- <script type="text/javascript" src="$controllerScript"></script>
+ <script type="text/javascript"
+ src="/root_dart/pkg/unittest/lib/test_controller.js">
+ </script>
<script type="$scriptType" src="$sourceScript" onerror="externalError(null)">
</script>
- <script type="text/javascript" src="$dartJsScript"></script>
+ <script type="text/javascript"
+ src="/root_dart/pkg/browser/lib/dart.js"></script>
</body>
</html>
""";
@@ -48,37 +49,27 @@
</html>
""";
-String wrapDartTestInLibrary(Path test, String testPath) =>
+String wrapDartTestInLibrary(Path testRelativeToDart) =>
"""
library libraryWrapper;
-part '${pathLib.relative(test.toNativePath(),
- from: pathLib.dirname(testPath)).replaceAll('\\', '\\\\')}';
+part '/$testRelativeToDart';
""";
-String dartTestWrapper(Path dartHome, String testPath, Path library) {
- var testPathDir = pathLib.dirname(testPath);
- var dartHomePath = dartHome.toNativePath();
- // TODO(efortuna): Unify path libraries used in test.dart.
- var unitTest = pathLib.relative(pathLib.join(dartHomePath,
- 'pkg/unittest/lib'), from: testPathDir).replaceAll('\\', '\\\\');
-
- var libString = library.toNativePath();
- if (!pathLib.isAbsolute(libString)) {
- libString = pathLib.join(dartHome.toNativePath(), libString);
- }
+String dartTestWrapper(bool usePackageImport, String libraryPathComponent) {
// Tests inside "pkg" import unittest using "package:". All others use a
// relative path. The imports need to agree, so use a matching form here.
- if (pathLib.split(pathLib.relative(libString,
- from: dartHome.toNativePath())).contains("pkg")) {
+ var unitTest;
+ if (usePackageImport) {
unitTest = 'package:unittest';
+ } else {
+ unitTest = '/root_dart/pkg/unittest/lib';
}
return """
library test;
import '$unitTest/unittest.dart' as unittest;
import '$unitTest/html_config.dart' as config;
-import '${pathLib.relative(libString, from: testPathDir).replaceAll(
- '\\', '\\\\')}' as Test;
+import '$libraryPathComponent' as Test;
main() {
config.useHtmlConfiguration();
diff --git a/tools/testing/dart/http_server.dart b/tools/testing/dart/http_server.dart
index 06b87da..6ab495b 100644
--- a/tools/testing/dart/http_server.dart
+++ b/tools/testing/dart/http_server.dart
@@ -4,6 +4,7 @@
library http_server;
+import 'dart:async';
import 'dart:io';
import 'dart:isolate';
import 'dart:uri';
@@ -12,6 +13,30 @@
// expected number of arguments, so test.dart doesn't rely on the args library?
// See discussion on https://codereview.chromium.org/11931025/.
import 'vendored_pkg/args/args.dart';
+import 'utils.dart';
+
+
+/// Interface of the HTTP server:
+///
+/// /echo: This will stream the data received in the request stream back
+/// to the client.
+/// /root_dart/X: This will serve the corresponding file from the dart
+/// directory (i.e. '$DartDirectory/X').
+/// /root_build/X: This will serve the corresponding file from the build
+/// directory (i.e. '$BuildDirectory/X').
+/// /FOO/packages/BAR: This will serve the corresponding file from the packages
+/// directory (i.e. '$BuildDirectory/packages/BAR')
+///
+/// In case a path does not refer to a file but rather to a directory, a
+/// directory listing will be displayed.
+
+const PREFIX_BUILDDIR = 'root_build';
+const PREFIX_DARTDIR = 'root_dart';
+
+// TODO(kustermann,ricow): We could change this to the following scheme:
+// http://host:port/root_packages/X -> $BuildDir/packages/X
+// Issue: 8368
+
main() {
/** Convenience method for local testing. */
@@ -43,7 +68,11 @@
.join(new Path('../../test.dart'))
.canonicalize()
.toNativePath();
+ // Note: args['package-root'] is always the build directory. We have the
+ // implicit assumption that it contains the 'packages' subdirectory.
+ // TODO: We should probably rename 'package-root' to 'build-directory'.
TestingServerRunner._packageRootDir = new Path(args['package-root']);
+ TestingServerRunner._buildDirectory = new Path(args['package-root']);
var network = args['network'];
TestingServerRunner.startHttpServer(network,
port: int.parse(args['port']));
@@ -63,98 +92,129 @@
class TestingServerRunner {
static List serverList = [];
static Path _packageRootDir = null;
+ static Path _buildDirectory = null;
// Added as a getter so that the function will be called again each time the
// default request handler closure is executed.
static Path get packageRootDir => _packageRootDir;
+ static Path get buildDirectory => _buildDirectory;
static setPackageRootDir(Map configuration) {
- _packageRootDir = TestUtils.currentWorkingDirectory.join(
+ _packageRootDir = TestUtils.absolutePath(
+ new Path(TestUtils.buildDir(configuration)));
+ }
+
+ static setBuildDir(Map configuration) {
+ _buildDirectory = TestUtils.absolutePath(
new Path(TestUtils.buildDir(configuration)));
}
static startHttpServer(String host, {int allowedPort:-1, int port: 0}) {
- var basePath = TestUtils.dartDir();
var httpServer = new HttpServer();
- var packagesDirName = 'packages';
httpServer.onError = (e) {
- // TODO(ricow): Once we have a debug log we should write this out there.
- print('Test http server error: $e');
+ DebugLogger.error('HttpServer: an error occured: $e');
};
- httpServer.defaultRequestHandler = (request, resp) {
- var requestPath = new Path(request.path.substring(1)).canonicalize();
- var path = basePath.join(requestPath);
- var file = new File(path.toNativePath());
-
- if (requestPath.segments().contains(packagesDirName)) {
- // Essentially implement the packages path rewriting, so we don't have
- // to pass environment variables to the browsers.
- var requestPathStr = requestPath.toNativePath().substring(
- requestPath.toNativePath().indexOf(packagesDirName));
- path = packageRootDir.append(requestPathStr);
- file = new File(path.toNativePath());
- }
- file.exists().then((exists) {
- if (exists) {
- if (allowedPort != -1) {
- if (request.headers.value('Origin') != null) {
- var origin = new Uri(request.headers.value('Origin'));
- // Allow loading from http://*:$allowedPort in browsers.
- var allowedOrigin =
- '${origin.scheme}://${origin.domain}:${allowedPort}';
- resp.headers.set("Access-Control-Allow-Origin", allowedOrigin);
- resp.headers.set('Access-Control-Allow-Credentials', 'true');
- }
- } else {
- // No allowedPort specified. Allow from anywhere (but cross-origin
- // requests *with credentials* will fail because you can't use "*").
- resp.headers.set("Access-Control-Allow-Origin", "*");
- }
- if (path.toNativePath().endsWith('.html')) {
- resp.headers.set('Content-Type', 'text/html');
- } else if (path.toNativePath().endsWith('.js')) {
- resp.headers.set('Content-Type', 'application/javascript');
- } else if (path.toNativePath().endsWith('.dart')) {
- resp.headers.set('Content-Type', 'application/dart');
- }
- file.openInputStream().pipe(resp.outputStream);
- } else {
- var directory = new Directory.fromPath(path);
- directory.exists().then((exists) {
- if (!exists) {
- sendNotFound(resp);
- } else {
- sendDirectoryListing(directory, request, resp);
- }
- });
- }
- });
+ httpServer.defaultRequestHandler = (request, response) {
+ handleFileOrDirectoryRequest(request, response, allowedPort);
};
-
- // Echos back the contents of the request as the response data.
- httpServer.addRequestHandler((req) => req.path == "/echo", (request, resp) {
- resp.headers.set("Access-Control-Allow-Origin", "*");
-
- request.inputStream.pipe(resp.outputStream);
- });
+ httpServer.addRequestHandler(
+ (req) => req.path == "/echo", handleEchoRequest);
httpServer.listen(host, port);
serverList.add(httpServer);
}
- static void sendNotFound(HttpResponse response) {
- response.statusCode = HttpStatus.NOT_FOUND;
- try {
- response.outputStream.close();
- } catch (e) {
- if (e is StreamException) {
- print('Test http_server error closing the response stream: $e');
+
+ static void handleFileOrDirectoryRequest(HttpRequest request,
+ HttpResponse response,
+ int allowedPort) {
+ var path = getFilePathFromRequestPath(request.path);
+ if (path != null) {
+ var file = new File.fromPath(path);
+ file.exists().then((exists) {
+ if (exists) {
+ sendFileContent(request, response, allowedPort, path, file);
+ } else {
+ var directory = new Directory.fromPath(path);
+ directory.exists().then((exists) {
+ if (exists) {
+ listDirectory(directory).then((entries) {
+ sendDirectoryListing(entries, request, response);
+ });
+ } else {
+ sendNotFound(request, response);
+ }
+ });
+ }
+ });
+ } else {
+ if (request.path == '/') {
+ var entries = [new _Entry('root_dart', 'root_dart/'),
+ new _Entry('root_build', 'root_build/'),
+ new _Entry('echo', 'echo')];
+ sendDirectoryListing(entries, request, response);
} else {
- throw e;
+ sendNotFound(request, response);
}
}
}
+ static void handleEchoRequest(HttpRequest request, HttpResponse response) {
+ response.headers.set("Access-Control-Allow-Origin", "*");
+ request.inputStream.pipe(response.outputStream);
+ }
+
+ static Path getFilePathFromRequestPath(String urlRequestPath) {
+ // Go to the top of the file to see an explanation of the URL path scheme.
+ var requestPath = new Path(urlRequestPath.substring(1)).canonicalize();
+ var pathSegments = requestPath.segments();
+ if (pathSegments.length > 0) {
+ var basePath;
+ var relativePath;
+ if (pathSegments[0] == PREFIX_BUILDDIR) {
+ basePath = _buildDirectory;
+ relativePath = new Path(
+ pathSegments.getRange(1, pathSegments.length - 1).join('/'));
+ } else if (pathSegments[0] == PREFIX_DARTDIR) {
+ basePath = TestUtils.dartDir();
+ relativePath = new Path(
+ pathSegments.getRange(1, pathSegments.length - 1).join('/'));
+ }
+ var packagesDirName = 'packages';
+ var packagesIndex = pathSegments.indexOf(packagesDirName);
+ if (packagesIndex != -1) {
+ var start = packagesIndex + 1;
+ var length = pathSegments.length - start;
+ basePath = _packageRootDir.append(packagesDirName);
+ relativePath = new Path(
+ pathSegments.getRange(start, length).join('/'));
+ }
+ if (basePath != null && relativePath != null) {
+ return basePath.join(relativePath);
+ }
+ }
+ return null;
+ }
+
+ static Future<List<_Entry>> listDirectory(Directory directory) {
+ var completer = new Completer();
+ var entries = [];
+
+ directory.list()
+ ..onFile = (filepath) {
+ var filename = new Path(filepath).filename;
+ entries.add(new _Entry(filename, filename));
+ }
+ ..onDir = (dirpath) {
+ var filename = new Path(dirpath).filename;
+ entries.add(new _Entry(filename, '$filename/'));
+ }
+ ..onDone = (_) {
+ completer.complete(entries);
+ };
+ return completer.future;
+ }
+
/**
* Sends a simple listing of all the files and sub-directories within
* directory.
@@ -162,8 +222,9 @@
* This is intended to make it easier to browse tests when manually running
* tests against this test server.
*/
- static void sendDirectoryListing(Directory directory, HttpRequest request,
- HttpResponse response) {
+ static void sendDirectoryListing(entries,
+ HttpRequest request,
+ HttpResponse response) {
response.headers.set('Content-Type', 'text/html');
var header = '''<!DOCTYPE html>
<html>
@@ -181,30 +242,62 @@
</body>
</html>''';
- var entries = [];
- directory.list()
- ..onFile = (filepath) {
- var filename = new Path(filepath).filename;
- entries.add(new _Entry(filename, filename));
- }
- ..onDir = (dirpath) {
- var filename = new Path(dirpath).filename;
- entries.add(new _Entry(filename, '$filename/'));
- }
- ..onDone = (_) {
- var requestPath = new Path.raw(request.path);
- entries.sort();
+ entries.sort();
+ response.outputStream.writeString(header);
+ for (var entry in entries) {
+ response.outputStream.writeString(
+ '<li><a href="${new Path(request.path).append(entry.name)}">'
+ '${entry.displayName}</a></li>');
+ }
+ response.outputStream.writeString(footer);
+ response.outputStream.close();
+ }
- response.outputStream.writeString(header);
- for (var entry in entries) {
- response.outputStream.writeString(
- '<li><a href="${requestPath.append(entry.name)}">'
- '${entry.displayName}</a></li>');
- }
- response.outputStream.writeString(footer);
- response.outputStream.close();
- };
+ static void sendFileContent(HttpRequest request,
+ HttpResponse response,
+ int allowedPort,
+ Path path,
+ File file) {
+ if (allowedPort != -1) {
+ var origin = new Uri(request.headers.value('Origin'));
+ // Allow loading from http://*:$allowedPort in browsers.
+ var allowedOrigin =
+ '${origin.scheme}://${origin.domain}:${allowedPort}';
+ response.headers.set("Access-Control-Allow-Origin", allowedOrigin);
+ response.headers.set('Access-Control-Allow-Credentials', 'true');
+ } else {
+ // No allowedPort specified. Allow from anywhere (but cross-origin
+ // requests *with credentials* will fail because you can't use "*").
+ response.headers.set("Access-Control-Allow-Origin", "*");
+ }
+ if (path.filename.endsWith('.html')) {
+ response.headers.set('Content-Type', 'text/html');
+ } else if (path.filename.endsWith('.js')) {
+ response.headers.set('Content-Type', 'application/javascript');
+ } else if (path.filename.endsWith('.dart')) {
+ response.headers.set('Content-Type', 'application/dart');
+ }
+ file.openInputStream().pipe(response.outputStream);
+ }
+
+ static void sendNotFound(HttpRequest request, HttpResponse response) {
+ // NOTE: Since some tests deliberately try to access non-existent files.
+ // We might want to remove this warning (otherwise it will show
+ // up in the debug.log every time).
+ DebugLogger.warning('HttpServer: could not find file for request path: '
+ '"${request.path}"');
+ response.statusCode = HttpStatus.NOT_FOUND;
+ try {
+ response.outputStream.close();
+ } catch (e) {
+ if (e is StreamException) {
+ DebugLogger.warning('HttpServer: error while closing the response '
+ 'stream: $e');
+ } else {
+ throw e;
+ }
+ }
}
static terminateHttpServers() {
diff --git a/tools/testing/dart/test_runner.dart b/tools/testing/dart/test_runner.dart
index 9833dd4..6ebd60c 100644
--- a/tools/testing/dart/test_runner.dart
+++ b/tools/testing/dart/test_runner.dart
@@ -1594,7 +1594,9 @@
new Timer(100, (_) => _tryRunTest()); // Don't lose a process.
return;
}
- if (_verbose) {
+ // Before running any commands, we print out all commands if '--verbose'
+ // was specified.
+ if (_verbose && test.commandOutputs.length == 0) {
int i = 1;
if (test is BrowserTestCase) {
// Additional command for rerunning the steps locally after the fact.
diff --git a/tools/testing/dart/test_suite.dart b/tools/testing/dart/test_suite.dart
index f8b59d8..f6ff12c 100644
--- a/tools/testing/dart/test_suite.dart
+++ b/tools/testing/dart/test_suite.dart
@@ -23,9 +23,7 @@
import "status_file_parser.dart";
import "test_runner.dart";
import "utils.dart";
-
-// TODO(efortuna,whess): Remove this import.
-import 'vendored_pkg/path/path.dart' as pathLib;
+import "http_server.dart" show PREFIX_BUILDDIR, PREFIX_DARTDIR;
part "browser_test.dart";
@@ -812,7 +810,77 @@
};
}
- /**
+
+ /**
+ * _createUrlPathFromFile takes a [file], which is either located in the dart
+ * or in the build directory, and will return a String representing
+ * the relative path to either the dart or the build directory.
+ * Thus, the returned [String] will be the path component of the URL
+ * corresponding to [file] (the http server serves files relative to the
+ * dart/build directories).
+ */
+ String _createUrlPathFromFile(Path file) {
+ file = TestUtils.absolutePath(file);
+
+ var relativeBuildDir = new Path(TestUtils.buildDir(configuration));
+ var buildDir = TestUtils.absolutePath(relativeBuildDir);
+ var dartDir = TestUtils.absolutePath(TestUtils.dartDir());
+
+ var fileString = file.toString();
+ if (fileString.startsWith(buildDir.toString())) {
+ var fileRelativeToBuildDir = file.relativeTo(buildDir);
+ return "/$PREFIX_BUILDDIR/$fileRelativeToBuildDir";
+ } else if (fileString.startsWith(dartDir.toString())) {
+ var fileRelativeToDartDir = file.relativeTo(dartDir);
+ return "/$PREFIX_DARTDIR/$fileRelativeToDartDir";
+ }
+ // Unreachable
+ Except.fail('This should be unreachable.');
+ }
+
+ void _getUriForBrowserTest(TestInformation info,
+ String pathComponent,
+ subtestNames,
+ subtestIndex) {
+ // Note: If we run test.py with the "--list" option, no http servers
+ // will be started. Therefore serverList is an empty list in this
+ // case. So we use PORT/CROSS_ORIGIN_PORT instead of real ports.
+ var serverPort = "PORT";
+ var crossOriginPort = "CROSS_ORIGIN_PORT";
+ if (!configuration['list']) {
+ serverPort = serverList[0].port.toString();
+ crossOriginPort = serverList[1].port.toString();
+ }
+
+ var url= 'http://127.0.0.1:$serverPort$pathComponent'
+ '?crossOriginPort=$crossOriginPort';
+ if (info.optionsFromFile['isMultiHtmlTest'] && subtestNames.length > 0) {
+ url= '${url}&group=${subtestNames[subtestIndex]}';
+ }
+ return url;
+ }
+
+ void _createWrapperFile(String dartWrapperFilename, dartLibraryFilename) {
+ File file = new File(dartWrapperFilename);
+ RandomAccessFile dartWrapper = file.openSync(FileMode.WRITE);
+
+ var usePackageImport = dartLibraryFilename.segments().contains("pkg");
+ var libraryPathComponent = _createUrlPathFromFile(dartLibraryFilename);
+ dartWrapper.writeStringSync(dartTestWrapper(usePackageImport,
+ libraryPathComponent));
+ dartWrapper.closeSync();
+ }
+
+ void _createLibraryWrapperFile(Path dartLibraryFilename, filePath) {
+ File file = new File(dartLibraryFilename.toNativePath());
+ RandomAccessFile dartLibrary = file.openSync(FileMode.WRITE);
+ var requestPath = new Path(PREFIX_DARTDIR)
+ .join(filePath.relativeTo(TestUtils.dartDir()));
+ dartLibrary.writeStringSync(wrapDartTestInLibrary(requestPath));
+ dartLibrary.closeSync();
+ }
+
+ /**
* The [StandardTestSuite] has support for tests that
* compile a test from Dart to JavaScript, and then run the resulting
* JavaScript. This function creates a working directory to hold the
@@ -867,18 +935,9 @@
if (!isLibraryDefinition) {
dartLibraryFilename = new Path(tempDir).append(
'test_as_library.dart');
- File file = new File(dartLibraryFilename.toNativePath());
- RandomAccessFile dartLibrary = file.openSync(FileMode.WRITE);
- dartLibrary.writeStringSync(
- wrapDartTestInLibrary(filePath, file.name));
- dartLibrary.closeSync();
+ _createLibraryWrapperFile(dartLibraryFilename, filePath);
}
-
- File file = new File(dartWrapperFilename);
- RandomAccessFile dartWrapper = file.openSync(FileMode.WRITE);
- dartWrapper.writeStringSync(
- dartTestWrapper(dartDir, file.name, dartLibraryFilename));
- dartWrapper.closeSync();
+ _createWrapperFile(dartWrapperFilename, dartLibraryFilename);
} else {
dartWrapperFilename = filename;
// TODO(whesse): Once test.py is retired, adjust the relative path in
@@ -895,8 +954,10 @@
}
htmlPath = '$tempDir/../$htmlFilename';
}
- final String scriptPath = (compiler == 'none') ?
+ String scriptPath = (compiler == 'none') ?
dartWrapperFilename : compiledDartWrapperFilename;
+ scriptPath = _createUrlPathFromFile(new Path(scriptPath));
+
// Create the HTML file for the test.
RandomAccessFile htmlTest = new File(htmlPath).openSync(FileMode.WRITE);
String content = null;
@@ -907,22 +968,13 @@
Path expectedOutput = null;
if (new File.fromPath(pngPath).existsSync()) {
expectedOutput = pngPath;
- // TODO(efortuna): Unify path libraries in test.dart.
- content = getHtmlLayoutContents(scriptType, pathLib.relative(scriptPath,
- from: pathLib.dirname(htmlPath)));
+ content = getHtmlLayoutContents(scriptType, new Path("$scriptPath"));
} else if (new File.fromPath(txtPath).existsSync()) {
expectedOutput = txtPath;
- content = getHtmlLayoutContents(scriptType, pathLib.relative(scriptPath,
- from: pathLib.dirname(htmlPath)));
+ content = getHtmlLayoutContents(scriptType, new Path("$scriptPath"));
} else {
- final htmlLocation = new Path(htmlPath);
- content = getHtmlContents(
- filename,
- dartDir.append('pkg/unittest/lib/test_controller.js')
- .relativeTo(htmlLocation),
- dartDir.append('pkg/browser/lib/dart.js').relativeTo(htmlLocation),
- scriptType,
- new Path(scriptPath).relativeTo(htmlLocation));
+ content = getHtmlContents(filename, scriptType,
+ new Path("$scriptPath"));
}
htmlTest.writeStringSync(content);
htmlTest.closeSync();
@@ -962,35 +1014,11 @@
commandSet = [];
}
- List<String> args = <String>[];
- var basePath = TestUtils.dartDir().toString();
- if (!htmlPath.startsWith('/') && !htmlPath.startsWith('http')) {
- htmlPath = '/$htmlPath';
- }
- htmlPath = htmlPath.startsWith(basePath) ?
- htmlPath.substring(basePath.length) : htmlPath;
- String fullHtmlPath = htmlPath;
- var searchStr = '?';
- if (!htmlPath.startsWith('http')) {
- // Note: If we run test.py with the "--list" option, no http servers
- // will be started. Therefore serverList is an empty list in this
- // case. So we use PORT/CROSS_ORIGIN_PORT instead of real ports.
- var serverPort = "PORT";
- var crossOriginPort = "CROSS_ORIGIN_PORT";
- if (!configuration['list']) {
- serverPort = serverList[0].port.toString();
- crossOriginPort = serverList[1].port.toString();
- }
- fullHtmlPath = 'http://127.0.0.1:$serverPort$htmlPath${searchStr}'
- 'crossOriginPort=$crossOriginPort';
- searchStr = '&';
- }
- if (info.optionsFromFile['isMultiHtmlTest']
- && subtestNames.length > 0) {
- fullHtmlPath = '${fullHtmlPath}${searchStr}group='
- '${subtestNames[subtestIndex]}';
- }
+ var htmlPath_subtest = _createUrlPathFromFile(new Path(htmlPath));
+ var fullHtmlPath = _getUriForBrowserTest(info, htmlPath_subtest,
+ subtestNames, subtestIndex);
+ List<String> args = <String>[];
if (TestUtils.usesWebDriver(runtime)) {
args = [
dartDir.append('tools/testing/run_selenium.py').toNativePath(),
@@ -1125,8 +1153,7 @@
var minified = configuration['minified'] ? '-minified' : '';
var dirName = "${configuration['compiler']}-${configuration['runtime']}"
"$checked$minified";
- Path generatedTestPath = new Path(dartDir.toNativePath())
- .append(buildDir)
+ Path generatedTestPath = new Path(buildDir)
.append('generated_tests')
.append(dirName)
.append(testUniqueName);
@@ -1816,6 +1843,10 @@
const ['d8', 'jsshell'].contains(runtime);
static String buildDir(Map configuration) {
+ // FIXME(kustermann,ricow): Our code assumes that the returned 'buildDir'
+ // is relative to the current working directory.
+ // Thus, if we pass in an absolute path (e.g. '--build-directory=/tmp/out')
+ // we get into trouble.
if (configuration['build_directory'] != '') {
return configuration['build_directory'];
}
diff --git a/tools/testing/dart/vendored_pkg/path/path.dart b/tools/testing/dart/vendored_pkg/path/path.dart
deleted file mode 100644
index f27d558..0000000
--- a/tools/testing/dart/vendored_pkg/path/path.dart
+++ /dev/null
@@ -1,689 +0,0 @@
-// 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.
-
-/// A comprehensive, cross-platform path manipulation library.
-library path;
-
-import 'dart:io' as io;
-
-/// An internal builder for the current OS so we can provide a straight
-/// functional interface and not require users to create one.
-final _builder = new Builder();
-
-/// Gets the path to the current working directory.
-String get current => new io.Directory.current().path;
-
-/// Gets the path separator for the current platform. On Mac and Linux, this
-/// is `/`. On Windows, it's `\`.
-String get separator => _builder.separator;
-
-/// Converts [path] to an absolute path by resolving it relative to the current
-/// working directory. If [path] is already an absolute path, just returns it.
-///
-/// path.absolute('foo/bar.txt'); // -> /your/current/dir/foo/bar.txt
-String absolute(String path) => join(current, path);
-
-/// Gets the part of [path] after the last separator.
-///
-/// path.basename('path/to/foo.dart'); // -> 'foo.dart'
-/// path.basename('path/to'); // -> 'to'
-///
-/// Trailing separators are ignored.
-///
-/// builder.dirname('path/to/'); // -> 'to'
-String basename(String path) => _builder.basename(path);
-
-/// Gets the part of [path] after the last separator, and without any trailing
-/// file extension.
-///
-/// path.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo'
-///
-/// Trailing separators are ignored.
-///
-/// builder.dirname('path/to/foo.dart/'); // -> 'foo'
-String basenameWithoutExtension(String path) =>
- _builder.basenameWithoutExtension(path);
-
-/// Gets the part of [path] before the last separator.
-///
-/// path.dirname('path/to/foo.dart'); // -> 'path/to'
-/// path.dirname('path/to'); // -> 'to'
-///
-/// Trailing separators are ignored.
-///
-/// builder.dirname('path/to/'); // -> 'path'
-String dirname(String path) => _builder.dirname(path);
-
-/// Gets the file extension of [path]: the portion of [basename] from the last
-/// `.` to the end (including the `.` itself).
-///
-/// path.extension('path/to/foo.dart'); // -> '.dart'
-/// path.extension('path/to/foo'); // -> ''
-/// path.extension('path.to/foo'); // -> ''
-/// path.extension('path/to/foo.dart.js'); // -> '.js'
-///
-/// If the file name starts with a `.`, then that is not considered the
-/// extension:
-///
-/// path.extension('~/.bashrc'); // -> ''
-/// path.extension('~/.notes.txt'); // -> '.txt'
-String extension(String path) => _builder.extension(path);
-
-// TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed.
-/// Returns the root of [path], if it's absolute, or the empty string if it's
-/// relative.
-///
-/// // Unix
-/// path.rootPrefix('path/to/foo'); // -> ''
-/// path.rootPrefix('/path/to/foo'); // -> '/'
-///
-/// // Windows
-/// path.rootPrefix(r'path\to\foo'); // -> ''
-/// path.rootPrefix(r'C:\path\to\foo'); // -> r'C:\'
-String rootPrefix(String path) => _builder.rootPrefix(path);
-
-/// Returns `true` if [path] is an absolute path and `false` if it is a
-/// relative path. On POSIX systems, absolute paths start with a `/` (forward
-/// slash). On Windows, an absolute path starts with `\\`, or a drive letter
-/// followed by `:/` or `:\`.
-bool isAbsolute(String path) => _builder.isAbsolute(path);
-
-/// Returns `true` if [path] is a relative path and `false` if it is absolute.
-/// On POSIX systems, absolute paths start with a `/` (forward slash). On
-/// Windows, an absolute path starts with `\\`, or a drive letter followed by
-/// `:/` or `:\`.
-bool isRelative(String path) => _builder.isRelative(path);
-
-/// Joins the given path parts into a single path using the current platform's
-/// [separator]. Example:
-///
-/// path.join('path', 'to', 'foo'); // -> 'path/to/foo'
-///
-/// If any part ends in a path separator, then a redundant separator will not
-/// be added:
-///
-/// path.join('path/', 'to', 'foo'); // -> 'path/to/foo
-///
-/// If a part is an absolute path, then anything before that will be ignored:
-///
-/// path.join('path', '/to', 'foo'); // -> '/to/foo'
-String join(String part1, [String part2, String part3, String part4,
- String part5, String part6, String part7, String part8]) =>
- _builder.join(part1, part2, part3, part4, part5, part6, part7, part8);
-
-// TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed.
-/// Splits [path] into its components using the current platform's [separator].
-///
-/// path.split('path/to/foo'); // -> ['path', 'to', 'foo']
-///
-/// The path will *not* be normalized before splitting.
-///
-/// path.split('path/../foo'); // -> ['path', '..', 'foo']
-///
-/// If [path] is absolute, the root directory will be the first element in the
-/// array. Example:
-///
-/// // Unix
-/// path.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo']
-///
-/// // Windows
-/// path.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo']
-List<String> split(String path) => _builder.split(path);
-
-/// Normalizes [path], simplifying it by handling `..`, and `.`, and
-/// removing redundant path separators whenever possible.
-///
-/// path.normalize('path/./to/..//file.text'); // -> 'path/file.txt'
-String normalize(String path) => _builder.normalize(path);
-
-/// Attempts to convert [path] to an equivalent relative path from the current
-/// directory.
-///
-/// // Given current directory is /root/path:
-/// path.relative('/root/path/a/b.dart'); // -> 'a/b.dart'
-/// path.relative('/root/other.dart'); // -> '../other.dart'
-///
-/// If the [from] argument is passed, [path] is made relative to that instead.
-///
-/// path.relative('/root/path/a/b.dart',
-/// from: '/root/path'); // -> 'a/b.dart'
-/// path.relative('/root/other.dart',
-/// from: '/root/path'); // -> '../other.dart'
-///
-/// Since there is no relative path from one drive letter to another on Windows,
-/// this will return an absolute path in that case.
-///
-/// path.relative(r'D:\other', from: r'C:\home'); // -> 'D:\other'
-String relative(String path, {String from}) =>
- _builder.relative(path, from: from);
-
-/// Removes a trailing extension from the last part of [path].
-///
-/// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo'
-String withoutExtension(String path) => _builder.withoutExtension(path);
-
-/// An instantiable class for manipulating paths. Unlike the top-level
-/// functions, this lets you explicitly select what platform the paths will use.
-class Builder {
- /// Creates a new path builder for the given style and root directory.
- ///
- /// If [style] is omitted, it uses the host operating system's path style. If
- /// [root] is omitted, it defaults to the current working directory. If [root]
- /// is relative, it is considered relative to the current working directory.
- factory Builder({Style style, String root}) {
- if (style == null) {
- if (io.Platform.operatingSystem == 'windows') {
- style = Style.windows;
- } else {
- style = Style.posix;
- }
- }
-
- if (root == null) root = current;
-
- return new Builder._(style, root);
- }
-
- Builder._(this.style, this.root);
-
- /// The style of path that this builder works with.
- final Style style;
-
- /// The root directory that relative paths will be relative to.
- final String root;
-
- /// Gets the path separator for the builder's [style]. On Mac and Linux,
- /// this is `/`. On Windows, it's `\`.
- String get separator => style.separator;
-
- /// Gets the part of [path] after the last separator on the builder's
- /// platform.
- ///
- /// builder.basename('path/to/foo.dart'); // -> 'foo.dart'
- /// builder.basename('path/to'); // -> 'to'
- ///
- /// Trailing separators are ignored.
- ///
- /// builder.dirname('path/to/'); // -> 'to'
- String basename(String path) => _parse(path).basename;
-
- /// Gets the part of [path] after the last separator on the builder's
- /// platform, and without any trailing file extension.
- ///
- /// builder.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo'
- ///
- /// Trailing separators are ignored.
- ///
- /// builder.dirname('path/to/foo.dart/'); // -> 'foo'
- String basenameWithoutExtension(String path) =>
- _parse(path).basenameWithoutExtension;
-
- /// Gets the part of [path] before the last separator.
- ///
- /// builder.dirname('path/to/foo.dart'); // -> 'path/to'
- /// builder.dirname('path/to'); // -> 'path'
- ///
- /// Trailing separators are ignored.
- ///
- /// builder.dirname('path/to/'); // -> 'path'
- String dirname(String path) {
- var parsed = _parse(path);
- parsed.removeTrailingSeparators();
- if (parsed.parts.isEmpty) return parsed.root == null ? '.' : parsed.root;
- if (parsed.parts.length == 1) {
- return parsed.root == null ? '.' : parsed.root;
- }
- parsed.parts.removeLast();
- parsed.separators.removeLast();
- parsed.removeTrailingSeparators();
- return parsed.toString();
- }
-
- /// Gets the file extension of [path]: the portion of [basename] from the last
- /// `.` to the end (including the `.` itself).
- ///
- /// builder.extension('path/to/foo.dart'); // -> '.dart'
- /// builder.extension('path/to/foo'); // -> ''
- /// builder.extension('path.to/foo'); // -> ''
- /// builder.extension('path/to/foo.dart.js'); // -> '.js'
- ///
- /// If the file name starts with a `.`, then it is not considered an
- /// extension:
- ///
- /// builder.extension('~/.bashrc'); // -> ''
- /// builder.extension('~/.notes.txt'); // -> '.txt'
- String extension(String path) => _parse(path).extension;
-
- // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed.
- /// Returns the root of [path], if it's absolute, or an empty string if it's
- /// relative.
- ///
- /// // Unix
- /// builder.rootPrefix('path/to/foo'); // -> ''
- /// builder.rootPrefix('/path/to/foo'); // -> '/'
- ///
- /// // Windows
- /// builder.rootPrefix(r'path\to\foo'); // -> ''
- /// builder.rootPrefix(r'C:\path\to\foo'); // -> r'C:\'
- String rootPrefix(String path) {
- var root = _parse(path).root;
- return root == null ? '' : root;
- }
-
- /// Returns `true` if [path] is an absolute path and `false` if it is a
- /// relative path. On POSIX systems, absolute paths start with a `/` (forward
- /// slash). On Windows, an absolute path starts with `\\`, or a drive letter
- /// followed by `:/` or `:\`.
- bool isAbsolute(String path) => _parse(path).isAbsolute;
-
- /// Returns `true` if [path] is a relative path and `false` if it is absolute.
- /// On POSIX systems, absolute paths start with a `/` (forward slash). On
- /// Windows, an absolute path starts with `\\`, or a drive letter followed by
- /// `:/` or `:\`.
- bool isRelative(String path) => !isAbsolute(path);
-
- /// Joins the given path parts into a single path. Example:
- ///
- /// builder.join('path', 'to', 'foo'); // -> 'path/to/foo'
- ///
- /// If any part ends in a path separator, then a redundant separator will not
- /// be added:
- ///
- /// builder.join('path/', 'to', 'foo'); // -> 'path/to/foo
- ///
- /// If a part is an absolute path, then anything before that will be ignored:
- ///
- /// builder.join('path', '/to', 'foo'); // -> '/to/foo'
- ///
- String join(String part1, [String part2, String part3, String part4,
- String part5, String part6, String part7, String part8]) {
- var buffer = new StringBuffer();
- var needsSeparator = false;
-
- var parts = [part1, part2, part3, part4, part5, part6, part7, part8];
- for (var i = 1; i < parts.length; i++) {
- if (parts[i] != null && parts[i - 1] == null) {
- throw new ArgumentError("join(): part ${i - 1} was null, but part $i "
- "was not.");
- }
- }
-
- for (var part in parts) {
- if (part == null) continue;
-
- if (this.isAbsolute(part)) {
- // An absolute path discards everything before it.
- buffer.clear();
- buffer.add(part);
- } else {
- if (part.length > 0 && part[0].contains(style.separatorPattern)) {
- // The part starts with a separator, so we don't need to add one.
- } else if (needsSeparator) {
- buffer.add(separator);
- }
-
- buffer.add(part);
- }
-
- // Unless this part ends with a separator, we'll need to add one before
- // the next part.
- needsSeparator = part.length > 0 &&
- !part[part.length - 1].contains(style.separatorPattern);
- }
-
- return buffer.toString();
- }
-
- // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed.
- /// Splits [path] into its components using the current platform's
- /// [separator]. Example:
- ///
- /// builder.split('path/to/foo'); // -> ['path', 'to', 'foo']
- ///
- /// The path will *not* be normalized before splitting.
- ///
- /// builder.split('path/../foo'); // -> ['path', '..', 'foo']
- ///
- /// If [path] is absolute, the root directory will be the first element in the
- /// array. Example:
- ///
- /// // Unix
- /// builder.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo']
- ///
- /// // Windows
- /// builder.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo']
- List<String> split(String path) {
- var parsed = _parse(path);
- // Filter out empty parts that exist due to multiple separators in a row.
- parsed.parts = parsed.parts.where((part) => !part.isEmpty).toList();
- if (parsed.root != null) parsed.parts.insertRange(0, 1, parsed.root);
- return parsed.parts;
- }
-
- /// Normalizes [path], simplifying it by handling `..`, and `.`, and
- /// removing redundant path separators whenever possible.
- ///
- /// builder.normalize('path/./to/..//file.text'); // -> 'path/file.txt'
- String normalize(String path) {
- if (path == '') return path;
-
- var parsed = _parse(path);
- parsed.normalize();
- return parsed.toString();
- }
-
- /// Creates a new path by appending the given path parts to the [root].
- /// Equivalent to [join()] with [root] as the first argument. Example:
- ///
- /// var builder = new Builder(root: 'root');
- /// builder.resolve('path', 'to', 'foo'); // -> 'root/path/to/foo'
- String resolve(String part1, [String part2, String part3, String part4,
- String part5, String part6, String part7]) {
- if (!?part2) return join(root, part1);
- if (!?part3) return join(root, part1, part2);
- if (!?part4) return join(root, part1, part2, part3);
- if (!?part5) return join(root, part1, part2, part3, part4);
- if (!?part6) return join(root, part1, part2, part3, part4, part5);
- if (!?part7) return join(root, part1, part2, part3, part4, part5, part6);
- return join(root, part1, part2, part3, part4, part5, part6, part7);
- }
-
- /// Attempts to convert [path] to an equivalent relative path relative to
- /// [root].
- ///
- /// var builder = new Builder(root: '/root/path');
- /// builder.relative('/root/path/a/b.dart'); // -> 'a/b.dart'
- /// builder.relative('/root/other.dart'); // -> '../other.dart'
- ///
- /// If the [from] argument is passed, [path] is made relative to that instead.
- ///
- /// builder.relative('/root/path/a/b.dart',
- /// from: '/root/path'); // -> 'a/b.dart'
- /// builder.relative('/root/other.dart',
- /// from: '/root/path'); // -> '../other.dart'
- ///
- /// Since there is no relative path from one drive letter to another on
- /// Windows, this will return an absolute path in that case.
- ///
- /// builder.relative(r'D:\other', from: r'C:\other'); // -> 'D:\other'
- ///
- /// This will also return an absolute path if an absolute [path] is passed to
- /// a builder with a relative [root].
- ///
- /// var builder = new Builder(r'some/relative/path');
- /// builder.relative(r'/absolute/path'); // -> '/absolute/path'
- String relative(String path, {String from}) {
- if (path == '') return '.';
-
- from = from == null ? root : this.join(root, from);
-
- // We can't determine the path from a relative path to an absolute path.
- if (this.isRelative(from) && this.isAbsolute(path)) {
- return this.normalize(path);
- }
-
- // If the given path is relative, resolve it relative to the root of the
- // builder.
- if (this.isRelative(path)) path = this.resolve(path);
-
- // If the path is still relative and `from` is absolute, we're unable to
- // find a path from `from` to `path`.
- if (this.isRelative(path) && this.isAbsolute(from)) {
- throw new ArgumentError('Unable to find a path to "$path" from "$from".');
- }
-
- var fromParsed = _parse(from)..normalize();
- var pathParsed = _parse(path)..normalize();
-
- // If the root prefixes don't match (for example, different drive letters
- // on Windows), then there is no relative path, so just return the absolute
- // one. In Windows, drive letters are case-insenstive and we allow
- // calculation of relative paths, even if a path has not been normalized.
- if (fromParsed.root != pathParsed.root &&
- ((fromParsed.root == null || pathParsed.root == null) ||
- fromParsed.root.toLowerCase().replaceAll('/', '\\') !=
- pathParsed.root.toLowerCase().replaceAll('/', '\\'))) {
- return pathParsed.toString();
- }
-
- // Strip off their common prefix.
- while (fromParsed.parts.length > 0 && pathParsed.parts.length > 0 &&
- fromParsed.parts[0] == pathParsed.parts[0]) {
- fromParsed.parts.removeAt(0);
- fromParsed.separators.removeAt(0);
- pathParsed.parts.removeAt(0);
- pathParsed.separators.removeAt(0);
- }
-
- // If there are any directories left in the root path, we need to walk up
- // out of them.
- pathParsed.parts.insertRange(0, fromParsed.parts.length, '..');
- pathParsed.separators.insertRange(0, fromParsed.parts.length,
- style.separator);
-
- // Corner case: the paths completely collapsed.
- if (pathParsed.parts.length == 0) return '.';
-
- // Make it relative.
- pathParsed.root = '';
- pathParsed.removeTrailingSeparators();
-
- return pathParsed.toString();
- }
-
- /// Removes a trailing extension from the last part of [path].
- ///
- /// builder.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo'
- String withoutExtension(String path) {
- var parsed = _parse(path);
-
- for (var i = parsed.parts.length - 1; i >= 0; i--) {
- if (!parsed.parts[i].isEmpty) {
- parsed.parts[i] = parsed.basenameWithoutExtension;
- break;
- }
- }
-
- return parsed.toString();
- }
-
- _ParsedPath _parse(String path) {
- var before = path;
-
- // Remove the root prefix, if any.
- var root = style.getRoot(path);
- if (root != null) path = path.substring(root.length);
-
- // Split the parts on path separators.
- var parts = [];
- var separators = [];
- var start = 0;
- for (var match in style.separatorPattern.allMatches(path)) {
- parts.add(path.substring(start, match.start));
- separators.add(match[0]);
- start = match.end;
- }
-
- // Add the final part, if any.
- if (start < path.length) {
- parts.add(path.substring(start));
- separators.add('');
- }
-
- return new _ParsedPath(style, root, parts, separators);
- }
-}
-
-/// An enum type describing a "flavor" of path.
-class Style {
- /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths
- /// start with "/". Used by UNIX, Linux, Mac OS X, and others.
- static final posix = new Style._('posix', '/', '/', '/');
-
- /// Windows paths use "\" (backslash) as separators. Absolute paths start with
- /// a drive letter followed by a colon (example, "C:") or two backslashes
- /// ("\\") for UNC paths.
- // TODO(rnystrom): The UNC root prefix should include the drive name too, not
- // just the "\\".
- static final windows = new Style._('windows', '\\', r'[/\\]',
- r'\\\\|[a-zA-Z]:[/\\]');
-
- Style._(this.name, this.separator, String separatorPattern,
- String rootPattern)
- : separatorPattern = new RegExp(separatorPattern),
- _rootPattern = new RegExp('^$rootPattern');
-
- /// The name of this path style. Will be "posix" or "windows".
- final String name;
-
- /// The path separator for this style. On POSIX, this is `/`. On Windows,
- /// it's `\`.
- final String separator;
-
- /// The [Pattern] that can be used to match a separator for a path in this
- /// style. Windows allows both "/" and "\" as path separators even though
- /// "\" is the canonical one.
- final Pattern separatorPattern;
-
- // TODO(nweiz): make this a Pattern when issue 7080 is fixed.
- /// The [RegExp] that can be used to match the root prefix of an absolute
- /// path in this style.
- final RegExp _rootPattern;
-
- /// Gets the root prefix of [path] if path is absolute. If [path] is relative,
- /// returns `null`.
- String getRoot(String path) {
- var match = _rootPattern.firstMatch(path);
- if (match == null) return null;
- return match[0];
- }
-
- String toString() => name;
-}
-
-// TODO(rnystrom): Make this public?
-class _ParsedPath {
- /// The [Style] that was used to parse this path.
- Style style;
-
- /// The absolute root portion of the path, or `null` if the path is relative.
- /// On POSIX systems, this will be `null` or "/". On Windows, it can be
- /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive
- /// letters.
- String root;
-
- /// The path-separated parts of the path. All but the last will be
- /// directories.
- List<String> parts;
-
- /// The path separators following each part. The last one will be an empty
- /// string unless the path ends with a trailing separator.
- List<String> separators;
-
- /// The file extension of the last part, or "" if it doesn't have one.
- String get extension => _splitExtension()[1];
-
- /// `true` if this is an absolute path.
- bool get isAbsolute => root != null;
-
- _ParsedPath(this.style, this.root, this.parts, this.separators);
-
- String get basename {
- var copy = this.clone();
- copy.removeTrailingSeparators();
- if (copy.parts.isEmpty) return root == null ? '' : root;
- return copy.parts.last;
- }
-
- String get basenameWithoutExtension {
- var copy = this.clone();
- copy.removeTrailingSeparators();
- if (copy.parts.isEmpty) return root == null ? '' : root;
- return copy._splitExtension()[0];
- }
-
- void removeTrailingSeparators() {
- while (!parts.isEmpty && parts.last == '') {
- parts.removeLast();
- separators.removeLast();
- }
- if (separators.length > 0) separators[separators.length - 1] = '';
- }
-
- void normalize() {
- // Handle '.', '..', and empty parts.
- var leadingDoubles = 0;
- var newParts = [];
- for (var part in parts) {
- if (part == '.' || part == '') {
- // Do nothing. Ignore it.
- } else if (part == '..') {
- // Pop the last part off.
- if (newParts.length > 0) {
- newParts.removeLast();
- } else {
- // Backed out past the beginning, so preserve the "..".
- leadingDoubles++;
- }
- } else {
- newParts.add(part);
- }
- }
-
- // A relative path can back out from the start directory.
- if (!isAbsolute) {
- newParts.insertRange(0, leadingDoubles, '..');
- }
-
- // If we collapsed down to nothing, do ".".
- if (newParts.length == 0 && !isAbsolute) {
- newParts.add('.');
- }
-
- // Canonicalize separators.
- var newSeparators = [];
- newSeparators.insertRange(0, newParts.length, style.separator);
-
- parts = newParts;
- separators = newSeparators;
-
- // Normalize the Windows root if needed.
- if (root != null && style == Style.windows) {
- root = root.replaceAll('/', '\\');
- }
- removeTrailingSeparators();
- }
-
- String toString() {
- var builder = new StringBuffer();
- if (root != null) builder.add(root);
- for (var i = 0; i < parts.length; i++) {
- builder.add(parts[i]);
- builder.add(separators[i]);
- }
-
- return builder.toString();
- }
-
- /// Splits the last part of the path into a two-element list. The first is
- /// the name of the file without any extension. The second is the extension
- /// or "" if it has none.
- List<String> _splitExtension() {
- if (parts.isEmpty) return ['', ''];
-
- var file = parts.last;
- if (file == '..') return ['..', ''];
-
- var lastDot = file.lastIndexOf('.');
-
- // If there is no dot, or it's the first character, like '.bashrc', it
- // doesn't count.
- if (lastDot <= 0) return [file, ''];
-
- return [file.substring(0, lastDot), file.substring(lastDot)];
- }
-
- _ParsedPath clone() => new _ParsedPath(
- style, root, new List.from(parts), new List.from(separators));
-}
diff --git a/tools/utils.py b/tools/utils.py
index c1305b5..2245516 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -60,6 +60,78 @@
return int(win_cpu_count)
return int(os.getenv("DART_NUMBER_OF_CORES", 2))
+def GetWindowsRegistryKeyName(name):
+ import win32process
+ # Check if python process is 64-bit or if it's 32-bit running in 64-bit OS.
+ # We need to know whether host is 64-bit so that we are looking in right
+ # registry for Visual Studio path.
+ if sys.maxsize > 2**32 or win32process.IsWow64Process():
+ wow6432Node = 'Wow6432Node\\'
+ else:
+ wow6432Node = ''
+ return r'SOFTWARE\%s%s' % (wow6432Node, name)
+
+# Try to guess Visual Studio location when buiding on Windows.
+def GuessVisualStudioPath():
+ defaultPath = r"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7" \
+ r"\IDE"
+ defaultExecutable = "devenv.com"
+
+ if not IsWindows():
+ return (defaultPath, defaultExecutable)
+
+ keyNamesAndExecutables = [
+ # Pair for non-Express editions.
+ (GetWindowsRegistryKeyName(r'Microsoft\VisualStudio'), 'devenv.com'),
+ # Pair for 2012 Express edition.
+ (GetWindowsRegistryKeyName(r'Microsoft\VSWinExpress'), 'VSWinExpress.exe'),
+ # Pair for pre-2012 Express editions.
+ (GetWindowsRegistryKeyName(r'Microsoft\VCExpress'), 'VCExpress.exe')]
+
+ bestGuess = (0.0, (defaultPath, defaultExecutable))
+
+ import _winreg
+ for (keyName, executable) in keyNamesAndExecutables:
+ try:
+ key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, keyName)
+ except WindowsError:
+ # Can't find this key - moving on the next one.
+ continue
+
+ try:
+ subkeyCounter = 0
+ while True:
+ try:
+ subkeyName = _winreg.EnumKey(key, subkeyCounter)
+ subkeyCounter = subkeyCounter + 1
+ except WindowsError:
+ # Reached end of enumeration. Moving on the next key.
+ break
+
+ match = re.match(r'^\d+\.\d+$', subkeyName)
+ if match:
+ with _winreg.OpenKey(key, subkeyName) as subkey:
+ try:
+ (installDir, registrytype) = _winreg.QueryValueEx(subkey,
+ 'InstallDir')
+ except WindowsError:
+ # Can't find value under the key - continue to the next key.
+ continue
+ isExpress = executable != 'devenv.com'
+ if not isExpress and subkeyName == '10.0':
+ # Stop search since if we found non-Express VS2010 version
+ # installed, which is preferred version.
+ return (installDir, executable)
+ else:
+ version = float(subkeyName)
+ # We prefer higher version of Visual Studio and given equal
+ # version numbers we prefer non-Express edition.
+ if version > bestGuess[0]:
+ bestGuess = (version, (installDir, executable))
+ finally:
+ _winreg.CloseKey(key)
+ return bestGuess[1]
+
# Returns true if we're running under Windows.
def IsWindows():
@@ -271,6 +343,7 @@
print "GuessArchitecture() -> ", GuessArchitecture()
print "GuessCpus() -> ", GuessCpus()
print "IsWindows() -> ", IsWindows()
+ print "GuessVisualStudioPath() -> ", GuessVisualStudioPath()
class Error(Exception):
diff --git a/utils/apidoc/apidoc.dart b/utils/apidoc/apidoc.dart
index 4caf0d5..fcaf106 100644
--- a/utils/apidoc/apidoc.dart
+++ b/utils/apidoc/apidoc.dart
@@ -110,46 +110,48 @@
});
var lister = new Directory.fromPath(doc.scriptDir.append('../../pkg')).list();
- lister.onDir = (dirPath) {
- var path = new Path(dirPath);
- var libName = path.filename;
+ lister.listen(
+ (entity) {
+ if (entity is Directory) {
+ var path = new Path(entity.path);
+ var libName = path.filename;
- // TODO(rnystrom): Get rid of oldStylePath support when all packages are
- // using new layout. See #5106.
- var oldStylePath = path.append('${libName}.dart');
- var newStylePath = path.append('lib/${libName}.dart');
+ // TODO(rnystrom): Get rid of oldStylePath support when all
+ // packages are using new layout. See #5106.
+ var oldStylePath = path.append('${libName}.dart');
+ var newStylePath = path.append('lib/${libName}.dart');
- if (new File.fromPath(oldStylePath).existsSync()) {
- apidocLibraries.add(oldStylePath);
- includedLibraries.add(libName);
- } else if (new File.fromPath(newStylePath).existsSync()) {
- apidocLibraries.add(newStylePath);
- includedLibraries.add(libName);
- } else {
- print('Warning: could not find package at $path');
- }
- };
+ if (new File.fromPath(oldStylePath).existsSync()) {
+ apidocLibraries.add(oldStylePath);
+ includedLibraries.add(libName);
+ } else if (new File.fromPath(newStylePath).existsSync()) {
+ apidocLibraries.add(newStylePath);
+ includedLibraries.add(libName);
+ } else {
+ print('Warning: could not find package at $path');
+ }
+ }
+ },
+ onDone: () {
+ print('Generating docs...');
+ final apidoc = new Apidoc(mdn, outputDir, mode, generateAppCache,
+ excludedLibraries, version);
+ apidoc.dartdocPath =
+ doc.scriptDir.append('../../sdk/lib/_internal/dartdoc/');
+ // Select the libraries to include in the produced documentation:
+ apidoc.includeApi = true;
+ apidoc.includedLibraries = includedLibraries;
- lister.onDone = (success) {
- print('Generating docs...');
- final apidoc = new Apidoc(mdn, outputDir, mode, generateAppCache,
- excludedLibraries, version);
- apidoc.dartdocPath =
- doc.scriptDir.append('../../sdk/lib/_internal/dartdoc/');
- // Select the libraries to include in the produced documentation:
- apidoc.includeApi = true;
- apidoc.includedLibraries = includedLibraries;
+ Future.wait([copiedStatic, copiedApiDocStatic]).then((_) {
+ apidoc.documentLibraries(apidocLibraries, libPath, pkgPath);
- Future.wait([copiedStatic, copiedApiDocStatic]).then((_) {
- apidoc.documentLibraries(apidocLibraries, libPath, pkgPath);
+ final compiled = doc.compileScript(mode, outputDir, libPath);
- final compiled = doc.compileScript(mode, outputDir, libPath);
-
- Future.wait([compiled, copiedStatic, copiedApiDocStatic]).then((_) {
- apidoc.cleanup();
+ Future.wait([compiled, copiedStatic, copiedApiDocStatic]).then((_) {
+ apidoc.cleanup();
+ });
+ });
});
- });
- };
}
class Apidoc extends doc.Dartdoc {
diff --git a/utils/css/source.dart b/utils/css/source.dart
index 3875c7b..a5e981b 100644
--- a/utils/css/source.dart
+++ b/utils/css/source.dart
@@ -6,7 +6,7 @@
/**
* Represents a file of source code.
*/
-class SourceFile implements Comparable {
+class SourceFile implements Comparable<SourceFile> {
// TODO(terry): This filename for in memory buffer. May need to rework if
// filename is used for more than informational.
static String IN_MEMORY_FILE = '<buffer>';
@@ -132,7 +132,7 @@
* work.
*/
// TODO(jmesserly): Rename to Span - but first write cool refactoring tool
-class SourceSpan implements Comparable {
+class SourceSpan implements Comparable<SourceSpan> {
/** The [SourceFile] that contains this span. */
final SourceFile file;
diff --git a/utils/pub/entrypoint.dart b/utils/pub/entrypoint.dart
index b2dc9b4..ab65950 100644
--- a/utils/pub/entrypoint.dart
+++ b/utils/pub/entrypoint.dart
@@ -232,7 +232,7 @@
if (entryExists(linkPath)) return;
ensureDir(packagesDir);
return createPackageSymlink(root.name, root.dir, linkPath,
- isSelfLink: true);
+ isSelfLink: true, relative: true);
});
}
@@ -296,7 +296,7 @@
return defer(() {
var symlink = path.join(dir, 'packages');
if (entryExists(symlink)) return;
- return createSymlink(packagesDir, symlink);
+ return createSymlink(packagesDir, symlink, relative: true);
});
}
}
diff --git a/utils/pub/hosted_source.dart b/utils/pub/hosted_source.dart
index 6fdc8e9..44f7195 100644
--- a/utils/pub/hosted_source.dart
+++ b/utils/pub/hosted_source.dart
@@ -29,21 +29,20 @@
final name = "hosted";
final shouldCache = true;
- /// The URL of the default package repository.
- static final defaultUrl = "https://pub.dartlang.org";
-
/// Downloads a list of all versions of a package that are available from the
/// site.
Future<List<Version>> getVersions(String name, description) {
- var parsed = _parseDescription(description);
- var fullUrl = "${parsed.last}/packages/${parsed.first}.json";
+ var url = _makeUrl(description,
+ (server, package) => "$server/packages/$package.json");
- return httpClient.read(fullUrl).then((body) {
+ log.io("Get versions from $url.");
+ return httpClient.read(url).then((body) {
var doc = json.parse(body);
return doc['versions']
.map((version) => new Version.parse(version))
.toList();
}).catchError((ex) {
+ var parsed = _parseDescription(description);
_throwFriendlyError(ex, parsed.first, parsed.last);
});
}
@@ -51,13 +50,14 @@
/// Downloads and parses the pubspec for a specific version of a package that
/// is available from the site.
Future<Pubspec> describe(PackageId id) {
- var parsed = _parseDescription(id.description);
- var fullUrl = "${parsed.last}/packages/${parsed.first}/versions/"
- "${id.version}.yaml";
+ var url = _makeVersionUrl(id, (server, package, version) =>
+ "$server/packages/$package/versions/$version.yaml");
- return httpClient.read(fullUrl).then((yaml) {
+ log.io("Describe package at $url.");
+ return httpClient.read(url).then((yaml) {
return new Pubspec.parse(null, yaml, systemCache.sources);
}).catchError((ex) {
+ var parsed = _parseDescription(id.description);
_throwFriendlyError(ex, id, parsed.last);
});
}
@@ -65,21 +65,19 @@
/// Downloads a package from the site and unpacks it.
Future<bool> install(PackageId id, String destPath) {
return defer(() {
- var parsedDescription = _parseDescription(id.description);
- var name = parsedDescription.first;
- var url = parsedDescription.last;
-
- var fullUrl = "$url/packages/$name/versions/${id.version}.tar.gz";
+ var url = _makeVersionUrl(id, (server, package, version) =>
+ "$server/packages/$package/versions/$version.tar.gz");
+ log.io("Install package from $url.");
log.message('Downloading $id...');
// Download and extract the archive to a temp directory.
var tempDir = systemCache.createTempDir();
- return httpClient.send(new http.Request("GET", Uri.parse(fullUrl)))
+ return httpClient.send(new http.Request("GET", url))
.then((response) => response.stream)
.then((stream) {
return timeout(extractTarGz(stream, tempDir), HTTP_TIMEOUT,
- 'fetching URL "$fullUrl"');
+ 'fetching URL "$url"');
}).then((_) {
// Now that the install has succeeded, move it to the real location in
// the cache. This ensures that we don't leave half-busted ghost
@@ -142,31 +140,57 @@
throw asyncError;
}
- /// Parses the description for a package.
- ///
- /// If the package parses correctly, this returns a (name, url) pair. If not,
- /// this throws a descriptive FormatException.
- Pair<String, String> _parseDescription(description) {
- if (description is String) {
- return new Pair<String, String>(description, defaultUrl);
- }
+}
- if (description is! Map) {
- throw new FormatException(
- "The description must be a package name or map.");
- }
+/// The URL of the default package repository.
+final _defaultUrl = "https://pub.dartlang.org";
- if (!description.containsKey("name")) {
- throw new FormatException(
- "The description map must contain a 'name' key.");
- }
+/// 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.
+Uri _makeUrl(description, String pattern(String server, String package)) {
+ var parsed = _parseDescription(description);
+ var server = parsed.last;
+ var package = encodeUriComponent(parsed.first);
+ return new Uri(pattern(server, package));
+}
- var name = description["name"];
- if (name is! String) {
- throw new FormatException("The 'name' key must have a string value.");
- }
+/// Parses [id] into its server, package name, and version components, then
+/// converts that to a Uri given [pattern]. Ensures the package name is
+/// properly URL encoded.
+Uri _makeVersionUrl(PackageId id,
+ String pattern(String server, String package, String version)) {
+ var parsed = _parseDescription(id.description);
+ var server = parsed.last;
+ var package = encodeUriComponent(parsed.first);
+ var version = encodeUriComponent(id.version.toString());
+ return new Uri(pattern(server, package, version));
+}
- var url = description.containsKey("url") ? description["url"] : defaultUrl;
- return new Pair<String, String>(name, url);
+/// Parses the description for a package.
+///
+/// If the package parses correctly, this returns a (name, url) pair. If not,
+/// this throws a descriptive FormatException.
+Pair<String, String> _parseDescription(description) {
+ if (description is String) {
+ return new Pair<String, String>(description, _defaultUrl);
}
+
+ if (description is! Map) {
+ throw new FormatException(
+ "The description must be a package name or map.");
+ }
+
+ if (!description.containsKey("name")) {
+ throw new FormatException(
+ "The description map must contain a 'name' key.");
+ }
+
+ var name = description["name"];
+ if (name is! String) {
+ throw new FormatException("The 'name' key must have a string value.");
+ }
+
+ var url = description.containsKey("url") ? description["url"] : _defaultUrl;
+ return new Pair<String, String>(name, url);
}
diff --git a/utils/pub/http.dart b/utils/pub/http.dart
index f8cb699..fe80834 100644
--- a/utils/pub/http.dart
+++ b/utils/pub/http.dart
@@ -60,6 +60,7 @@
if (asyncError.error.osError.errorCode == 8 ||
asyncError.error.osError.errorCode == -2 ||
asyncError.error.osError.errorCode == -5 ||
+ asyncError.error.osError.errorCode == 11001 ||
asyncError.error.osError.errorCode == 11004) {
throw 'Could not resolve URL "${request.url.origin}".';
} else if (asyncError.error.osError.errorCode == -12276) {
diff --git a/utils/pub/io.dart b/utils/pub/io.dart
index a031da2..2a4be97 100644
--- a/utils/pub/io.dart
+++ b/utils/pub/io.dart
@@ -81,8 +81,7 @@
Future<String> createFileFromStream(Stream<List<int>> stream, String file) {
log.io("Creating $file from stream.");
- var outputStream = new File(file).openOutputStream();
- return stream.pipe(wrapOutputStream(outputStream)).then((_) {
+ return stream.pipe(new File(file).openWrite()).then((_) {
log.fine("Created $file from stream.");
return file;
});
@@ -158,41 +157,38 @@
log.io("Listing directory $dir.");
var lister = new Directory(dir).list();
- lister.onDone = (done) {
- // TODO(rnystrom): May need to sort here if it turns out onDir and onFile
- // aren't guaranteed to be called in a certain order. So far, they seem to.
- if (done) {
- log.fine("Listed directory $dir:\n${contents.join('\n')}");
- completer.complete(contents);
- }
- };
-
- // TODO(nweiz): remove this when issue 4061 is fixed.
- var stackTrace;
- try {
- throw "";
- } catch (_, localStackTrace) {
- stackTrace = localStackTrace;
- }
-
var children = [];
- lister.onError = (error) => completer.completeError(error, stackTrace);
- lister.onDir = (file) {
- if (!includeHiddenFiles && path.basename(file).startsWith('.')) return;
- file = path.join(dir, path.basename(file));
- contents.add(file);
- // TODO(nweiz): don't manually recurse once issue 7358 is fixed. Note that
- // once we remove the manual recursion, we'll need to explicitly filter
- // out files in hidden directories.
- if (recursive) {
- children.add(doList(file, listedDirectories));
- }
- };
-
- lister.onFile = (file) {
- if (!includeHiddenFiles && path.basename(file).startsWith('.')) return;
- contents.add(path.join(dir, path.basename(file)));
- };
+ lister.listen(
+ (entity) {
+ if (entity is File) {
+ var file = entity.name;
+ if (!includeHiddenFiles && path.basename(file).startsWith('.')) {
+ return;
+ }
+ contents.add(path.join(dir, path.basename(file)));
+ } else if (entity is Directory) {
+ var file = entity.path;
+ if (!includeHiddenFiles && path.basename(file).startsWith('.')) {
+ return;
+ }
+ file = path.join(dir, path.basename(file));
+ contents.add(file);
+ // TODO(nweiz): don't manually recurse once issue 7358 is fixed.
+ // Note that once we remove the manual recursion, we'll need to
+ // explicitly filter out files in hidden directories.
+ if (recursive) {
+ children.add(doList(file, listedDirectories));
+ }
+ }
+ },
+ onDone: () {
+ // TODO(rnystrom): May need to sort here if it turns out
+ // onDir and onFile aren't guaranteed to be called in a
+ // certain order. So far, they seem to.
+ log.fine("Listed directory $dir:\n${contents.join('\n')}");
+ completer.complete(contents);
+ },
+ onError: (error) => completer.completeError(error, stackTrace));
return completer.future.then((contents) {
return Future.wait(children).then((childContents) {
@@ -364,8 +360,8 @@
/// Wrap the standard output or error [stream] in a [StreamSink]. Any errors are
/// logged, and then the program is terminated. [name] is used for debugging.
-StreamSink<List<int>> _wrapStdio(OutputStream stream, String name) {
- var pair = consumerToSink(wrapOutputStream(stream));
+StreamSink<List<int>> _wrapStdio(IOSink sink, String name) {
+ var pair = consumerToSink(sink);
pair.last.catchError((e) {
// This log may or may not work, depending on how the stream failed. Not
// much we can do about that.
@@ -376,8 +372,8 @@
}
/// A line-by-line stream of standard input.
-final Stream<String> stdinLines =
- streamToLines(wrapInputStream(stdin).toStringStream());
+final Stream<String> stdinLines = streamToLines(
+ new ByteStream(stdin).toStringStream());
/// Displays a message and reads a yes/no confirmation from the user. Returns
/// a [Future] that completes to `true` if the user confirms or `false` if they
@@ -392,82 +388,10 @@
.then((line) => new RegExp(r"^[yY]").hasMatch(line));
}
-/// Reads and discards all output from [inputStream]. Returns a [Future] that
+/// Reads and discards all output from [stream]. Returns a [Future] that
/// completes when the stream is closed.
-Future drainInputStream(InputStream inputStream) {
- var completer = new Completer();
- if (inputStream.closed) {
- completer.complete();
- return completer.future;
- }
-
- inputStream.onClosed = () => completer.complete();
- inputStream.onData = inputStream.read;
- inputStream.onError = (error) => completer.completeError(error);
- return completer.future;
-}
-
-/// Wraps [stream] in a single-subscription [Stream] that emits the same data.
-ByteStream wrapInputStream(InputStream stream) {
- var controller = new StreamController();
- if (stream.closed) {
- controller.close();
- return new ByteStream(controller.stream);
- }
-
- stream.onClosed = controller.close;
- stream.onData = () => controller.add(stream.read());
- stream.onError = (e) => controller.signalError(new AsyncError(e));
- return new ByteStream(controller.stream);
-}
-
-/// Wraps [stream] in a [StreamConsumer] so that [Stream]s can by piped into it
-/// using [Stream.pipe]. Errors piped to the returned [StreamConsumer] will be
-/// forwarded to the [Future] returned by [Stream.pipe].
-StreamConsumer<List<int>, dynamic> wrapOutputStream(OutputStream stream) =>
- new _OutputStreamConsumer(stream);
-
-/// A [StreamConsumer] that pipes data into an [OutputStream].
-class _OutputStreamConsumer implements StreamConsumer<List<int>, dynamic> {
- final OutputStream _outputStream;
-
- _OutputStreamConsumer(this._outputStream);
-
- Future consume(Stream<List<int>> stream) {
- // TODO(nweiz): we have to manually keep track of whether or not the
- // completer has completed since the output stream could signal an error
- // after close() has been called but before it has shut down internally. See
- // the following TODO.
- var completed = false;
- var completer = new Completer();
- stream.listen((data) {
- // Writing empty data to a closed stream can cause errors.
- if (data.isEmpty) return;
-
- // TODO(nweiz): remove this try/catch when issue 7836 is fixed.
- try {
- _outputStream.write(data);
- } catch (e, stack) {
- if (!completed) completer.completeError(e, stack);
- completed = true;
- }
- }, onError: (e) {
- if (!completed) completer.completeError(e.error, e.stackTrace);
- completed = true;
- }, onDone: () => _outputStream.close());
-
- _outputStream.onError = (e) {
- if (!completed) completer.completeError(e);
- completed = true;
- };
-
- _outputStream.onClosed = () {
- if (!completed) completer.complete();
- completed = true;
- };
-
- return completer.future;
- }
+Future drainStream(Stream stream) {
+ return stream.reduce(null, (x, y) {});
}
/// Returns a [StreamSink] that pipes all data to [consumer] and a [Future] that
@@ -609,18 +533,18 @@
: _process = process {
var errorGroup = new ErrorGroup();
- var pair = consumerToSink(wrapOutputStream(process.stdin));
+ var pair = consumerToSink(process.stdin);
_stdin = pair.first;
_stdinClosed = errorGroup.registerFuture(pair.last);
_stdout = new ByteStream(
- errorGroup.registerStream(wrapInputStream(process.stdout)));
+ errorGroup.registerStream(process.stdout));
_stderr = new ByteStream(
- errorGroup.registerStream(wrapInputStream(process.stderr)));
+ errorGroup.registerStream(process.stderr));
var exitCodeCompleter = new Completer();
_exitCode = errorGroup.registerFuture(exitCodeCompleter.future);
- _process.onExit = (code) => exitCodeCompleter.complete(code);
+ _process.exitCode.then((code) => exitCodeCompleter.complete(code));
}
/// Sends [signal] to the underlying process.
@@ -796,8 +720,6 @@
contents.forEach((file) => buffer.add('$file\n'));
log.fine(buffer.toString());
- // TODO(nweiz): Propagate errors to the returned stream (including non-zero
- // exit codes). See issue 3657.
var controller = new StreamController<List<int>>();
if (baseDir == null) baseDir = path.current;
diff --git a/utils/pub/oauth2.dart b/utils/pub/oauth2.dart
index 276fb25..f2ec1b5 100644
--- a/utils/pub/oauth2.dart
+++ b/utils/pub/oauth2.dart
@@ -170,35 +170,37 @@
// Spin up a one-shot HTTP server to receive the authorization code from the
// Google OAuth2 server via redirect. This server will close itself as soon as
// the code is received.
- var completer = new Completer();
- var server = new HttpServer();
- server.addRequestHandler((request) => request.path == "/",
- (request, response) {
- chainToCompleter(defer(() {
- log.message('Authorization received, processing...');
- var queryString = request.queryString;
- if (queryString == null) queryString = '';
- response.statusCode = 302;
- response.headers.set('location', 'http://pub.dartlang.org/authorized');
- response.outputStream.close();
- return grant.handleAuthorizationResponse(queryToMap(queryString));
- }).then((client) {
- server.close();
- return client;
- }), completer);
- });
- server.listen('127.0.0.1', 0);
+ return HttpServer.bind('127.0.0.1', 0).then((server) {
+ var authUrl = grant.getAuthorizationUrl(
+ Uri.parse('http://localhost:${server.port}'), scopes: _scopes);
- var authUrl = grant.getAuthorizationUrl(
- Uri.parse('http://localhost:${server.port}'), scopes: _scopes);
-
- log.message(
- 'Pub needs your authorization to upload packages on your behalf.\n'
- 'In a web browser, go to $authUrl\n'
- 'Then click "Allow access".\n\n'
- 'Waiting for your authorization...');
-
- return completer.future.then((client) {
+ log.message(
+ 'Pub needs your authorization to upload packages on your behalf.\n'
+ 'In a web browser, go to $authUrl\n'
+ 'Then click "Allow access".\n\n'
+ 'Waiting for your authorization...');
+ return server.first.then((request) {
+ var response = request.response;
+ if (request.uri.path == "/") {
+ log.message('Authorization received, processing...');
+ var queryString = request.uri.query;
+ if (queryString == null) queryString = '';
+ response.statusCode = 302;
+ response.headers.set('location',
+ 'http://pub.dartlang.org/authorized');
+ response.close();
+ return grant.handleAuthorizationResponse(queryToMap(queryString))
+ .then((client) {
+ server.close();
+ return client;
+ });
+ } else {
+ response.statusCode = 404;
+ response.close();
+ }
+ });
+ })
+ .then((client) {
log.message('Successfully authorized.\n');
return client;
});
diff --git a/utils/pub/package.dart b/utils/pub/package.dart
index dd988cb..8731655 100644
--- a/utils/pub/package.dart
+++ b/utils/pub/package.dart
@@ -85,7 +85,7 @@
/// different directories that happen to contain identical packages. For
/// example, the same package may be available from multiple sources. As far as
/// Pub is concerned, those packages are different.
-class PackageId implements Comparable {
+class PackageId implements Comparable<PackageId> {
/// The name of the package being identified.
final String name;
@@ -129,9 +129,7 @@
return "$name $version from $source";
}
- int compareTo(Comparable other) {
- if (other is! PackageId) throw new ArgumentError(other);
-
+ int compareTo(PackageId other) {
var sourceComp = source.name.compareTo(other.source.name);
if (sourceComp != 0) return sourceComp;
diff --git a/utils/pub/validator/name.dart b/utils/pub/validator/name.dart
index 2ad4bc9..c183f9c 100644
--- a/utils/pub/validator/name.dart
+++ b/utils/pub/validator/name.dart
@@ -16,9 +16,10 @@
/// Dart reserved words, from the Dart spec.
final _RESERVED_WORDS = [
- "abstract", "as", "dynamic", "export", "external", "factory", "get",
- "implements", "import", "library", "operator", "part", "set", "static",
- "typedef"
+ "assert", "break", "case", "catch", "class", "const", "continue", "default",
+ "do", "else", "extends", "false", "final", "finally", "for", "if", "in", "is",
+ "new", "null", "return", "super", "switch", "this", "throw", "true", "try",
+ "var", "void", "while", "with"
];
/// A validator that validates the name of the package and its libraries.
@@ -27,12 +28,14 @@
: super(entrypoint);
Future validate() {
- _checkName(entrypoint.root.name, 'Package name "${entrypoint.root.name}"');
+ _checkName(entrypoint.root.name, 'Package name "${entrypoint.root.name}"',
+ isPackage: true);
return _libraries.then((libraries) {
for (var library in libraries) {
var libName = path.basenameWithoutExtension(library);
- _checkName(libName, 'The name of "$library", "$libName",');
+ _checkName(libName, 'The name of "$library", "$libName",',
+ isPackage: false);
}
if (libraries.length == 1) {
@@ -61,18 +64,21 @@
});
}
- void _checkName(String name, String description) {
+ void _checkName(String name, String description, {bool isPackage}) {
+ // Packages names are more stringent than libraries.
+ var messages = isPackage ? errors : warnings;
+
if (name == "") {
errors.add("$description may not be empty.");
} else if (!new RegExp(r"^[a-zA-Z0-9_]*$").hasMatch(name)) {
- errors.add("$description may only contain letters, numbers, and "
+ messages.add("$description may only contain letters, numbers, and "
"underscores.\n"
"Using a valid Dart identifier makes the name usable in Dart code.");
} else if (!new RegExp(r"^[a-zA-Z]").hasMatch(name)) {
- errors.add("$description must begin with letter.\n"
+ messages.add("$description must begin with letter.\n"
"Using a valid Dart identifier makes the name usable in Dart code.");
} else if (_RESERVED_WORDS.contains(name.toLowerCase())) {
- errors.add("$description may not be a reserved word in Dart.\n"
+ messages.add("$description may not be a reserved word in Dart.\n"
"Using a valid Dart identifier makes the name usable in Dart code.");
} else if (new RegExp(r"[A-Z]").hasMatch(name)) {
warnings.add('$description should be lower-case. Maybe use '
diff --git a/utils/pub/version.dart b/utils/pub/version.dart
index 06c14fd..62669e4 100644
--- a/utils/pub/version.dart
+++ b/utils/pub/version.dart
@@ -11,18 +11,25 @@
import 'utils.dart';
+
+/// Regex that matches a version number at the beginning of a string.
+final _START_VERSION = new RegExp(
+ r'^' // Start at beginning.
+ r'(\d+).(\d+).(\d+)' // Version number.
+ r'(-([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?' // Pre-release.
+ r'(\+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?'); // Build.
+
+/// Like [_START_VERSION] but matches the entire string.
+final _COMPLETE_VERSION = new RegExp("${_START_VERSION.pattern}\$");
+
+/// Parses a comparison operator ("<", ">", "<=", or ">=") at the beginning of
+/// a string.
+final _START_COMPARISON = new RegExp(r"^[<>]=?");
+
/// A parsed semantic version number.
-class Version implements Comparable, VersionConstraint {
+class Version implements Comparable<Version>, VersionConstraint {
/// No released version: i.e. "0.0.0".
static Version get none => new Version(0, 0, 0);
-
- static final _PARSE_REGEX = new RegExp(
- r'^' // Start at beginning.
- r'(\d+).(\d+).(\d+)' // Version number.
- r'(-([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?' // Pre-release.
- r'(\+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?' // Build.
- r'$'); // Consume entire string.
-
/// The major version number: "1" in "1.2.3".
final int major;
@@ -51,7 +58,7 @@
/// Creates a new [Version] by parsing [text].
factory Version.parse(String text) {
- final match = _PARSE_REGEX.firstMatch(text);
+ final match = _COMPLETE_VERSION.firstMatch(text);
if (match == null) {
throw new FormatException('Could not parse "$text".');
}
@@ -146,9 +153,9 @@
String toString() {
var buffer = new StringBuffer();
- buffer.add('$major.$minor.$patch');
- if (preRelease != null) buffer.add('-$preRelease');
- if (build != null) buffer.add('+$build');
+ buffer.write('$major.$minor.$patch');
+ if (preRelease != null) buffer.write('-$preRelease');
+ if (build != null) buffer.write('+$build');
return buffer.toString();
}
@@ -214,28 +221,92 @@
/// A [VersionConstraint] that allows no versions: i.e. the empty set.
static VersionConstraint empty = const _EmptyVersion();
- /// Parses a version constraint. This string is a space-separated series of
+ /// Parses a version constraint. This string is either "any" or a series of
/// version parts. Each part can be one of:
///
/// * A version string like `1.2.3`. In other words, anything that can be
/// parsed by [Version.parse()].
/// * A comparison operator (`<`, `>`, `<=`, or `>=`) followed by a version
- /// string. There cannot be a space between the operator and the version.
+ /// string.
+ ///
+ /// Whitespace is ignored.
///
/// Examples:
///
+ /// any
/// 1.2.3-alpha
/// <=5.1.4
- /// >2.0.4 <=2.4.6
+ /// >2.0.4 <= 2.4.6
factory VersionConstraint.parse(String text) {
- if (text.trim() == '') {
- throw new FormatException('Cannot parse an empty string.');
+ // Handle the "any" constraint.
+ if (text.trim() == "any") return new VersionRange();
+
+ var originalText = text;
+ var constraints = <VersionConstraint>[];
+
+ void skipWhitespace() {
+ text = text.trim();
}
- // Split it into space-separated parts.
- var constraints = <VersionConstraint>[];
- for (var part in text.split(' ')) {
- constraints.add(_parseSingleConstraint(part));
+ // Try to parse and consume a version number.
+ Version matchVersion() {
+ var version = _START_VERSION.firstMatch(text);
+ if (version == null) return null;
+
+ text = text.substring(version.end);
+ return new Version.parse(version[0]);
+ }
+
+ // Try to parse and consume a comparison operator followed by a version.
+ VersionConstraint matchComparison() {
+ var comparison = _START_COMPARISON.firstMatch(text);
+ if (comparison == null) return null;
+
+ var op = comparison[0];
+ text = text.substring(comparison.end);
+ skipWhitespace();
+
+ var version = matchVersion();
+ if (version == null) {
+ throw new FormatException('Expected version number after "$op" in '
+ '"$originalText", got "$text".');
+ }
+
+ switch (op) {
+ case '<=':
+ return new VersionRange(max: version, includeMax: true);
+ case '<':
+ return new VersionRange(max: version, includeMax: false);
+ case '>=':
+ return new VersionRange(min: version, includeMin: true);
+ case '>':
+ return new VersionRange(min: version, includeMin: false);
+ }
+ }
+
+ while (true) {
+ skipWhitespace();
+ if (text.isEmpty) break;
+
+ var version = matchVersion();
+ if (version != null) {
+ constraints.add(version);
+ continue;
+ }
+
+ var comparison = matchComparison();
+ if (comparison != null) {
+ constraints.add(comparison);
+ continue;
+ }
+
+ // If we got here, we couldn't parse the remaining string.
+ throw new FormatException('Could not parse version "$originalText". '
+ 'Unknown text at "$text".');
+ }
+
+ if (constraints.isEmpty) {
+ throw new FormatException('Cannot parse an empty string.');
}
return new VersionConstraint.intersection(constraints);
@@ -266,32 +337,6 @@
/// Creates a new [VersionConstraint] that only allows [Version]s allowed by
/// both this and [other].
VersionConstraint intersect(VersionConstraint other);
-
- static VersionConstraint _parseSingleConstraint(String text) {
- if (text == 'any') {
- return new VersionRange();
- }
-
- // TODO(rnystrom): Consider other syntaxes for version constraints. This
- // one is whitespace sensitive (you can't do "< 1.2.3") and "<" is
- // unfortunately meaningful in YAML, requiring it to be quoted in a
- // pubspec.
- // See if it's a comparison operator followed by a version, like ">1.2.3".
- var match = new RegExp(r"^([<>]=?)?(.*)$").firstMatch(text);
- if (match != null) {
- var comparison = match[1];
- var version = new Version.parse(match[2]);
- switch (match[1]) {
- case '<=': return new VersionRange(max: version, includeMax: true);
- case '<': return new VersionRange(max: version, includeMax: false);
- case '>=': return new VersionRange(min: version, includeMin: true);
- case '>': return new VersionRange(min: version, includeMin: false);
- }
- }
-
- // Otherwise, it must be an explicit version.
- return new Version.parse(text);
- }
}
/// Constrains versions to a fall within a given range. If there is a minimum,
@@ -402,17 +447,17 @@
var buffer = new StringBuffer();
if (min != null) {
- buffer.add(includeMin ? '>=' : '>');
- buffer.add(min);
+ buffer.write(includeMin ? '>=' : '>');
+ buffer.write(min);
}
if (max != null) {
- if (min != null) buffer.add(' ');
- buffer.add(includeMax ? '<=' : '<');
- buffer.add(max);
+ if (min != null) buffer.write(' ');
+ buffer.write(includeMax ? '<=' : '<');
+ buffer.write(max);
}
- if (min == null && max == null) buffer.add('any');
+ if (min == null && max == null) buffer.write('any');
return buffer.toString();
}
}
diff --git a/utils/template/source.dart b/utils/template/source.dart
index 3875c7b..a5e981b 100644
--- a/utils/template/source.dart
+++ b/utils/template/source.dart
@@ -6,7 +6,7 @@
/**
* Represents a file of source code.
*/
-class SourceFile implements Comparable {
+class SourceFile implements Comparable<SourceFile> {
// TODO(terry): This filename for in memory buffer. May need to rework if
// filename is used for more than informational.
static String IN_MEMORY_FILE = '<buffer>';
@@ -132,7 +132,7 @@
* work.
*/
// TODO(jmesserly): Rename to Span - but first write cool refactoring tool
-class SourceSpan implements Comparable {
+class SourceSpan implements Comparable<SourceSpan> {
/** The [SourceFile] that contains this span. */
final SourceFile file;
diff --git a/utils/testrunner/utils.dart b/utils/testrunner/utils.dart
index e8f81d4..3bd7efb 100644
--- a/utils/testrunner/utils.dart
+++ b/utils/testrunner/utils.dart
@@ -70,21 +70,24 @@
Directory d = new Directory(path);
if (d.existsSync()) {
++dirCount;
- var lister = d.list(recursive: recurse);
- lister.onFile = (file) {
- if (filePat.hasMatch(file)) {
- if (excludePat == null || !excludePat.hasMatch(file)) {
- if (includeSymLinks || file.startsWith(path)) {
- files.add(file);
+ d.list(recursive: recurse).listen(
+ (entity) {
+ if (entity is File) {
+ var file = entity.name;
+ if (filePat.hasMatch(file)) {
+ if (excludePat == null || !excludePat.hasMatch(file)) {
+ if (includeSymLinks || file.startsWith(path)) {
+ files.add(file);
+ }
+ }
+ }
}
- }
- }
- };
- lister.onDone = (complete) {
- if (complete && --dirCount == 0) {
- onComplete(files);
- }
- };
+ },
+ onDone: () {
+ if (complete && --dirCount == 0) {
+ onComplete(files);
+ }
+ });
} else { // Does not exist.
print('$path does not exist.');
}
diff --git a/utils/tests/pub/install/hosted/install_test.dart b/utils/tests/pub/install/hosted/install_test.dart
index 1ed4f40..5bc120c 100644
--- a/utils/tests/pub/install/hosted/install_test.dart
+++ b/utils/tests/pub/install/hosted/install_test.dart
@@ -9,6 +9,7 @@
import '../../test_pub.dart';
main() {
+ initConfig();
integration('installs a package from a pub server', () {
servePackages([package("foo", "1.2.3")]);
@@ -20,4 +21,15 @@
cacheDir({"foo": "1.2.3"}).scheduleValidate();
packagesDir({"foo": "1.2.3"}).scheduleValidate();
});
+
+ integration('URL encodes the package name', () {
+ servePackages([]);
+
+ appDir([dependency("bad name!", "1.2.3")]).scheduleCreate();
+
+ schedulePub(args: ['install'],
+ error: new RegExp('Could not find package "bad name!" at '
+ 'http://localhost:'),
+ exitCode: 1);
+ });
}
diff --git a/utils/tests/pub/install/pub_install_test.dart b/utils/tests/pub/install/pub_install_test.dart
index 233b7c2..b82565a 100644
--- a/utils/tests/pub/install/pub_install_test.dart
+++ b/utils/tests/pub/install/pub_install_test.dart
@@ -10,6 +10,8 @@
import '../test_pub.dart';
main() {
+ initConfig();
+
group('requires', () {
integration('a pubspec', () {
dir(appPath, []).scheduleCreate();
diff --git a/utils/tests/pub/install/relative_symlink_test.dart b/utils/tests/pub/install/relative_symlink_test.dart
new file mode 100644
index 0000000..166a7ca
--- /dev/null
+++ b/utils/tests/pub/install/relative_symlink_test.dart
@@ -0,0 +1,62 @@
+// 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.
+
+library pub_tests;
+
+import 'dart:io';
+
+import '../../../../pkg/unittest/lib/unittest.dart';
+import '../test_pub.dart';
+
+main() {
+ // Pub uses NTFS junction points to create links in the packages directory.
+ // These (unlike the symlinks that are supported in Vista and later) do not
+ // support relative paths. So this test, by design, will not pass on Windows.
+ // So just skip it.
+ if (Platform.operatingSystem == "windows") return;
+
+ initConfig();
+ integration('uses a relative symlink for the self link', () {
+ dir(appPath, [
+ appPubspec([]),
+ libDir('foo')
+ ]).scheduleCreate();
+
+ schedulePub(args: ['install'],
+ output: new RegExp(r"Dependencies installed!$"));
+
+ scheduleRename(appPath, "moved");
+
+ dir("moved", [
+ dir("packages", [
+ dir("myapp", [
+ file('foo.dart', 'main() => "foo";')
+ ])
+ ])
+ ]).scheduleValidate();
+ });
+
+ integration('uses a relative symlink for secondary packages directory', () {
+ dir(appPath, [
+ appPubspec([]),
+ libDir('foo'),
+ dir("bin")
+ ]).scheduleCreate();
+
+ schedulePub(args: ['install'],
+ output: new RegExp(r"Dependencies installed!$"));
+
+ scheduleRename(appPath, "moved");
+
+ dir("moved", [
+ dir("bin", [
+ dir("packages", [
+ dir("myapp", [
+ file('foo.dart', 'main() => "foo";')
+ ])
+ ])
+ ])
+ ]).scheduleValidate();
+ });
+}
diff --git a/utils/tests/pub/oauth2_test.dart b/utils/tests/pub/oauth2_test.dart
index 62d4f91..c4d0873 100644
--- a/utils/tests/pub/oauth2_test.dart
+++ b/utils/tests/pub/oauth2_test.dart
@@ -28,7 +28,7 @@
expect(request.headers.value('authorization'),
equals('Bearer access token'));
- response.outputStream.close();
+ response.close();
});
// After we give pub an invalid response, it should crash. We wait for it to
@@ -48,7 +48,7 @@
expect(request.headers.value('authorization'),
equals('Bearer access token'));
- response.outputStream.close();
+ response.close();
});
pub.kill();
@@ -66,17 +66,17 @@
confirmPublish(pub);
server.handle('POST', '/token', (request, response) {
- return wrapInputStream(request.inputStream).toBytes().then((bytes) {
+ return new ByteStream(request).toBytes().then((bytes) {
var body = new String.fromCharCodes(bytes);
expect(body, matches(
new RegExp(r'(^|&)refresh_token=refresh\+token(&|$)')));
response.headers.contentType = new ContentType("application", "json");
- response.outputStream.writeString(json.stringify({
+ response.addString(json.stringify({
"access_token": "new access token",
"token_type": "bearer"
}));
- response.outputStream.close();
+ response.close();
});
});
@@ -84,7 +84,7 @@
expect(request.headers.value('authorization'),
equals('Bearer new access token'));
- response.outputStream.close();
+ response.close();
});
pub.shouldExit();
@@ -111,7 +111,7 @@
expect(request.headers.value('authorization'),
equals('Bearer new access token'));
- response.outputStream.close();
+ response.close();
});
// After we give pub an invalid response, it should crash. We wait for it to
@@ -136,7 +136,7 @@
expect(request.headers.value('authorization'),
equals('Bearer new access token'));
- response.outputStream.close();
+ response.close();
});
@@ -159,10 +159,10 @@
response.statusCode = 401;
response.headers.set('www-authenticate', 'Bearer error="invalid_token",'
' error_description="your token sucks"');
- response.outputStream.writeString(json.stringify({
+ response.addString(json.stringify({
'error': {'message': 'your token sucks'}
}));
- response.outputStream.close();
+ response.close();
});
expectLater(pub.nextErrLine(), equals('OAuth2 authorization failed (your '
@@ -205,16 +205,16 @@
void handleAccessTokenRequest(ScheduledServer server, String accessToken) {
server.handle('POST', '/token', (request, response) {
- return wrapInputStream(request.inputStream).toBytes().then((bytes) {
+ return new ByteStream(request).toBytes().then((bytes) {
var body = new String.fromCharCodes(bytes);
expect(body, matches(new RegExp(r'(^|&)code=access\+code(&|$)')));
response.headers.contentType = new ContentType("application", "json");
- response.outputStream.writeString(json.stringify({
+ response.addString(json.stringify({
"access_token": accessToken,
"token_type": "bearer"
}));
- response.outputStream.close();
+ response.close();
});
});
}
diff --git a/utils/tests/pub/pub_lish_test.dart b/utils/tests/pub/pub_lish_test.dart
index 05b8ff5..62fcdf8 100644
--- a/utils/tests/pub/pub_lish_test.dart
+++ b/utils/tests/pub/pub_lish_test.dart
@@ -29,8 +29,8 @@
}
response.headers.contentType = new ContentType("application", "json");
- response.outputStream.writeString(json.stringify(body));
- response.outputStream.close();
+ response.addString(json.stringify(body));
+ response.close();
});
});
}
@@ -39,12 +39,12 @@
server.handle('POST', '/upload', (request, response) {
// TODO(nweiz): Once a multipart/form-data parser in Dart exists, validate
// that the request body is correctly formatted. See issue 6952.
- return drainInputStream(request.inputStream).then((_) {
+ return drainStream(request).then((_) {
return server.url;
}).then((url) {
response.statusCode = 302;
response.headers.set('location', url.resolve('/create').toString());
- response.outputStream.close();
+ response.close();
});
});
}
@@ -63,10 +63,10 @@
handleUpload(server);
server.handle('GET', '/create', (request, response) {
- response.outputStream.writeString(json.stringify({
+ response.addString(json.stringify({
'success': {'message': 'Package test_pkg 1.0.0 uploaded!'}
}));
- response.outputStream.close();
+ response.close();
});
// TODO(rnystrom): The confirm line is run together with this one because
@@ -150,10 +150,10 @@
handleUpload(server);
server.handle('GET', '/create', (request, response) {
- response.outputStream.writeString(json.stringify({
+ response.addString(json.stringify({
'success': {'message': 'Package test_pkg 1.0.0 uploaded!'}
}));
- response.outputStream.close();
+ response.close();
});
pub.shouldExit(0);
@@ -170,10 +170,10 @@
server.handle('GET', '/packages/versions/new.json', (request, response) {
response.statusCode = 400;
- response.outputStream.writeString(json.stringify({
+ response.addString(json.stringify({
'error': {'message': 'your request sucked'}
}));
- response.outputStream.close();
+ response.close();
});
expectLater(pub.nextErrLine(), equals('your request sucked'));
@@ -188,8 +188,8 @@
confirmPublish(pub);
server.handle('GET', '/packages/versions/new.json', (request, response) {
- response.outputStream.writeString('{not json');
- response.outputStream.close();
+ response.addString('{not json');
+ response.close();
});
expectLater(pub.nextErrLine(), equals('Invalid server response:'));
@@ -292,12 +292,12 @@
handleUploadForm(server);
server.handle('POST', '/upload', (request, response) {
- return drainInputStream(request.inputStream).then((_) {
+ return drainStream(request).then((_) {
response.statusCode = 400;
response.headers.contentType = new ContentType('application', 'xml');
- response.outputStream.writeString('<Error><Message>Your request sucked.'
+ response.addString('<Error><Message>Your request sucked.'
'</Message></Error>');
- response.outputStream.close();
+ response.close();
});
});
@@ -316,9 +316,9 @@
handleUploadForm(server);
server.handle('POST', '/upload', (request, response) {
- return drainInputStream(request.inputStream).then((_) {
+ return drainStream(request).then((_) {
// Don't set the location header.
- response.outputStream.close();
+ response.close();
});
});
@@ -337,10 +337,10 @@
server.handle('GET', '/create', (request, response) {
response.statusCode = 400;
- response.outputStream.writeString(json.stringify({
+ response.addString(json.stringify({
'error': {'message': 'Your package was too boring.'}
}));
- response.outputStream.close();
+ response.close();
});
expectLater(pub.nextErrLine(), equals('Your package was too boring.'));
@@ -357,8 +357,8 @@
handleUpload(server);
server.handle('GET', '/create', (request, response) {
- response.outputStream.writeString('{not json');
- response.outputStream.close();
+ response.addString('{not json');
+ response.close();
});
expectLater(pub.nextErrLine(), equals('Invalid server response:'));
@@ -378,8 +378,8 @@
var body = {'error': 'Your package was too boring.'};
server.handle('GET', '/create', (request, response) {
response.statusCode = 400;
- response.outputStream.writeString(json.stringify(body));
- response.outputStream.close();
+ response.addString(json.stringify(body));
+ response.close();
});
expectLater(pub.nextErrLine(), equals('Invalid server response:'));
@@ -398,8 +398,8 @@
var body = {'success': 'Your package was awesome.'};
server.handle('GET', '/create', (request, response) {
- response.outputStream.writeString(json.stringify(body));
- response.outputStream.close();
+ response.addString(json.stringify(body));
+ response.close();
});
expectLater(pub.nextErrLine(), equals('Invalid server response:'));
@@ -425,10 +425,10 @@
handleUpload(server);
server.handle('GET', '/create', (request, response) {
- response.outputStream.writeString(json.stringify({
+ response.addString(json.stringify({
'success': {'message': 'Package test_pkg 1.0.0 uploaded!'}
}));
- response.outputStream.close();
+ response.close();
});
pub.shouldExit(0);
@@ -449,10 +449,10 @@
handleUpload(server);
server.handle('GET', '/create', (request, response) {
- response.outputStream.writeString(json.stringify({
+ response.addString(json.stringify({
'success': {'message': 'Package test_pkg 1.0.0 uploaded!'}
}));
- response.outputStream.close();
+ response.close();
});
pub.shouldExit(0);
diff --git a/utils/tests/pub/pub_uploader_test.dart b/utils/tests/pub/pub_uploader_test.dart
index 2ac8722..d8f8721 100644
--- a/utils/tests/pub/pub_uploader_test.dart
+++ b/utils/tests/pub/pub_uploader_test.dart
@@ -54,14 +54,14 @@
var pub = startPubUploader(server, ['--package', 'pkg', 'add', 'email']);
server.handle('POST', '/packages/pkg/uploaders.json', (request, response) {
- expect(wrapInputStream(request.inputStream).toBytes().then((bodyBytes) {
+ expect(new ByteStream(request).toBytes().then((bodyBytes) {
expect(new String.fromCharCodes(bodyBytes), equals('email=email'));
response.headers.contentType = new ContentType("application", "json");
- response.outputStream.writeString(json.stringify({
+ response.addString(json.stringify({
'success': {'message': 'Good job!'}
}));
- response.outputStream.close();
+ response.close();
}), completes);
});
@@ -77,10 +77,10 @@
server.handle('DELETE', '/packages/pkg/uploaders/email.json',
(request, response) {
response.headers.contentType = new ContentType("application", "json");
- response.outputStream.writeString(json.stringify({
+ response.addString(json.stringify({
'success': {'message': 'Good job!'}
}));
- response.outputStream.close();
+ response.close();
});
expectLater(pub.nextLine(), equals('Good job!'));
@@ -97,10 +97,10 @@
server.handle('POST', '/packages/test_pkg/uploaders.json',
(request, response) {
response.headers.contentType = new ContentType("application", "json");
- response.outputStream.writeString(json.stringify({
+ response.addString(json.stringify({
'success': {'message': 'Good job!'}
}));
- response.outputStream.close();
+ response.close();
});
expectLater(pub.nextLine(), equals('Good job!'));
@@ -115,10 +115,10 @@
server.handle('POST', '/packages/pkg/uploaders.json', (request, response) {
response.statusCode = 400;
response.headers.contentType = new ContentType("application", "json");
- response.outputStream.writeString(json.stringify({
+ response.addString(json.stringify({
'error': {'message': 'Bad job!'}
}));
- response.outputStream.close();
+ response.close();
});
expectLater(pub.nextErrLine(), equals('Bad job!'));
@@ -135,10 +135,10 @@
(request, response) {
response.statusCode = 400;
response.headers.contentType = new ContentType("application", "json");
- response.outputStream.writeString(json.stringify({
+ response.addString(json.stringify({
'error': {'message': 'Bad job!'}
}));
- response.outputStream.close();
+ response.close();
});
expectLater(pub.nextErrLine(), equals('Bad job!'));
@@ -151,8 +151,8 @@
var pub = startPubUploader(server, ['--package', 'pkg', 'add', 'email']);
server.handle('POST', '/packages/pkg/uploaders.json', (request, response) {
- response.outputStream.writeString("{not json");
- response.outputStream.close();
+ response.addString("{not json");
+ response.close();
});
expectLater(pub.nextErrLine(), equals('Invalid server response:'));
@@ -167,8 +167,8 @@
server.handle('DELETE', '/packages/pkg/uploaders/email.json',
(request, response) {
- response.outputStream.writeString("{not json");
- response.outputStream.close();
+ response.addString("{not json");
+ response.close();
});
expectLater(pub.nextErrLine(), equals('Invalid server response:'));
diff --git a/utils/tests/pub/test_pub.dart b/utils/tests/pub/test_pub.dart
index 2a148f2..64ca423 100644
--- a/utils/tests/pub/test_pub.dart
+++ b/utils/tests/pub/test_pub.dart
@@ -102,36 +102,38 @@
_schedule((_) {
return _closeServer().then((_) {
- _server = new HttpServer();
- _server.defaultRequestHandler = (request, response) {
- var path = request.uri.replaceFirst("/", "").split("/");
- response.persistentConnection = false;
- var stream;
- try {
- stream = baseDir.load(path);
- } catch (e) {
- response.statusCode = 404;
- response.contentLength = 0;
- response.outputStream.close();
- return;
- }
+ return HttpServer.bind("127.0.0.1", 0).then((server) {
+ _server = server;
+ server.listen((request) {
+ var response = request.response;
+ var path = request.uri.path.replaceFirst("/", "").split("/");
+ response.persistentConnection = false;
+ var stream;
+ try {
+ stream = baseDir.load(path);
+ } catch (e) {
+ response.statusCode = 404;
+ response.contentLength = 0;
+ response.close();
+ return;
+ }
- stream.toBytes().then((data) {
- response.statusCode = 200;
- response.contentLength = data.length;
- response.outputStream.write(data);
- response.outputStream.close();
- }).catchError((e) {
- print("Exception while handling ${request.uri}: $e");
- response.statusCode = 500;
- response.reasonPhrase = e.message;
- response.outputStream.close();
+ stream.toBytes().then((data) {
+ response.statusCode = 200;
+ response.contentLength = data.length;
+ response.add(data);
+ response.close();
+ }).catchError((e) {
+ print("Exception while handling ${request.uri}: $e");
+ response.statusCode = 500;
+ response.reasonPhrase = e.message;
+ response.close();
+ });
});
- };
- _server.listen("127.0.0.1", 0);
- _portCompleter.complete(_server.port);
- _scheduleCleanup((_) => _closeServer());
- return null;
+ _portCompleter.complete(_server.port);
+ _scheduleCleanup((_) => _closeServer());
+ return null;
+ });
});
});
}
@@ -467,15 +469,15 @@
typedef Future _ScheduledEvent(String parentDir);
/// The list of events that are scheduled to run as part of the test case.
-List<_ScheduledEvent> _scheduled;
+Queue<_ScheduledEvent> _scheduled;
/// The list of events that are scheduled to run after the test case, even if
/// it failed.
-List<_ScheduledEvent> _scheduledCleanup;
+Queue<_ScheduledEvent> _scheduledCleanup;
/// The list of events that are scheduled to run after the test case only if it
/// failed.
-List<_ScheduledEvent> _scheduledOnException;
+Queue<_ScheduledEvent> _scheduledOnException;
/// Set to true when the current batch of scheduled events should be aborted.
bool _abortScheduled = false;
@@ -704,18 +706,16 @@
});
}
-Future _runScheduled(List<_ScheduledEvent> scheduled) {
+Future _runScheduled(Queue<_ScheduledEvent> scheduled) {
if (scheduled == null) return new Future.immediate(null);
- var iterator = scheduled.iterator;
Future runNextEvent(_) {
- if (_abortScheduled || !iterator.moveNext()) {
+ if (_abortScheduled || scheduled.isEmpty) {
_abortScheduled = false;
- scheduled.clear();
return new Future.immediate(null);
}
- var future = iterator.current(_sandboxDir);
+ var future = scheduled.removeFirst()(_sandboxDir);
if (future != null) {
return future.then(runNextEvent);
} else {
@@ -1155,8 +1155,8 @@
// TODO(nweiz): propagate any errors to the return value. See issue 3657.
withTempDir((tempDir) {
return create(tempDir).then((tar) {
- var sourceStream = new File(tar).openInputStream();
- return store(wrapInputStream(sourceStream), controller);
+ var sourceStream = new File(tar).openRead();
+ return store(sourceStream, controller);
});
});
return new ByteStream(controller.stream);
@@ -1500,11 +1500,11 @@
factory ScheduledServer() {
var scheduledServer;
scheduledServer = new ScheduledServer._(_scheduleValue((_) {
- var server = new HttpServer();
- server.defaultRequestHandler = scheduledServer._awaitHandle;
- server.listen("127.0.0.1", 0);
- _scheduleCleanup((_) => server.close());
- return new Future.immediate(server);
+ return HttpServer.bind("127.0.0.1", 0).then((server) {
+ server.listen(scheduledServer._awaitHandle);
+ _scheduleCleanup((_) => server.close());
+ return server;
+ });
}));
return scheduledServer;
}
@@ -1526,8 +1526,7 @@
var requestCompleteCompleter = new Completer();
handlerCompleter.complete((request, response) {
expect(request.method, equals(method));
- // TODO(nweiz): Use request.path once issue 7464 is fixed.
- expect(Uri.parse(request.uri).path, equals(path));
+ expect(request.uri.path, equals(path));
var future = handler(request, response);
if (future == null) future = new Future.immediate(null);
@@ -1545,17 +1544,18 @@
_ignored.add(new Pair(method, path));
/// Raises an error complaining of an unexpected request.
- void _awaitHandle(HttpRequest request, HttpResponse response) {
- if (_ignored.contains(new Pair(request.method, request.path))) return;
+ void _awaitHandle(HttpRequest request) {
+ HttpResponse response = request.response;
+ if (_ignored.contains(new Pair(request.method, request.uri.path))) return;
var future = timeout(defer(() {
if (_handlers.isEmpty) {
- fail('Unexpected ${request.method} request to ${request.path}.');
+ fail('Unexpected ${request.method} request to ${request.uri.path}.');
}
return _handlers.removeFirst();
}).then((handler) {
handler(request, response);
}), _SCHEDULE_TIMEOUT, "waiting for a handler for ${request.method} "
- "${request.path}");
+ "${request.uri.path}");
expect(future, completes);
}
}
@@ -1587,8 +1587,8 @@
/// Schedules a callback to be called as part of the test case.
void _schedule(_ScheduledEvent event) {
- if (_scheduled == null) _scheduled = [];
- _scheduled.add(event);
+ if (_scheduled == null) _scheduled = new Queue();
+ _scheduled.addLast(event);
}
/// Like [_schedule], but pipes the return value of [event] to a returned
@@ -1605,15 +1605,15 @@
/// Schedules a callback to be called after the test case has completed, even
/// if it failed.
void _scheduleCleanup(_ScheduledEvent event) {
- if (_scheduledCleanup == null) _scheduledCleanup = [];
- _scheduledCleanup.add(event);
+ if (_scheduledCleanup == null) _scheduledCleanup = new Queue();
+ _scheduledCleanup.addLast(event);
}
/// Schedules a callback to be called after the test case has completed, but
/// only if it failed.
void _scheduleOnException(_ScheduledEvent event) {
- if (_scheduledOnException == null) _scheduledOnException = [];
- _scheduledOnException.add(event);
+ if (_scheduledOnException == null) _scheduledOnException = new Queue();
+ _scheduledOnException.addLast(event);
}
/// Like [expect], but for [Future]s that complete as part of the scheduled
diff --git a/utils/tests/pub/validator_test.dart b/utils/tests/pub/validator_test.dart
index 3dbb5ad..4f59bba 100644
--- a/utils/tests/pub/validator_test.dart
+++ b/utils/tests/pub/validator_test.dart
@@ -298,7 +298,7 @@
});
integration('has a package name that is a Dart reserved word', () {
- dir(appPath, [libPubspec("operator", "1.0.0")]).scheduleCreate();
+ dir(appPath, [libPubspec("final", "1.0.0")]).scheduleCreate();
expectValidationError(name);
});
@@ -307,7 +307,7 @@
libPubspec("test_pkg", "1.0.0"),
dir("lib", [file("test-pkg.dart", "int i = 0;")])
]).scheduleCreate();
- expectValidationError(name);
+ expectValidationWarning(name);
});
integration('has a library name that begins with a number', () {
@@ -315,7 +315,7 @@
libPubspec("test_pkg", "1.0.0"),
dir("lib", [file("8ball.dart", "int i = 0;")])
]).scheduleCreate();
- expectValidationError(name);
+ expectValidationWarning(name);
});
integration('has a library name that contains upper-case letters', () {
@@ -329,9 +329,9 @@
integration('has a library name that is a Dart reserved word', () {
dir(appPath, [
libPubspec("test_pkg", "1.0.0"),
- dir("lib", [file("operator.dart", "int i = 0;")])
+ dir("lib", [file("for.dart", "int i = 0;")])
]).scheduleCreate();
- expectValidationError(name);
+ expectValidationWarning(name);
});
integration('has a single library named differently than the package', () {
diff --git a/utils/tests/pub/version_test.dart b/utils/tests/pub/version_test.dart
index d5b1849..7543298 100644
--- a/utils/tests/pub/version_test.dart
+++ b/utils/tests/pub/version_test.dart
@@ -10,6 +10,8 @@
import '../../pub/version.dart';
main() {
+ initConfig();
+
final v123 = new Version.parse('1.2.3');
final v114 = new Version.parse('1.1.4');
final v124 = new Version.parse('1.2.4');
@@ -386,12 +388,38 @@
new Version.parse('3.4.5')]));
});
- test('throws FormatException on a bad string', () {
- expect(() => new VersionConstraint.parse(''), throwsFormatException);
- expect(() => new VersionConstraint.parse(' '), throwsFormatException);
- expect(() => new VersionConstraint.parse('not a version'),
+ test('ignores whitespace around operators', () {
+ var constraint = new VersionConstraint.parse(' >1.0.0>=1.2.3 < 1.3.0');
+ expect(constraint, allows([
+ new Version.parse('1.2.3'),
+ new Version.parse('1.2.5')]));
+ expect(constraint, doesNotAllow([
+ new Version.parse('1.2.3-pre'),
+ new Version.parse('1.3.0'),
+ new Version.parse('3.4.5')]));
+ });
+
+ test('does not allow "any" to be mixed with other constraints', () {
+ expect(() => new VersionConstraint.parse('any 1.0.0'),
throwsFormatException);
});
+
+ test('throws FormatException on a bad string', () {
+ var bad = [
+ "", " ", // Empty string.
+ "foo", // Bad text.
+ ">foo", // Bad text after operator.
+ "1.0.0 foo", "1.0.0foo", // Bad text after version.
+ "anything", // Bad text after "any".
+ "<>1.0.0", // Multiple operators.
+ "1.0.0<" // Trailing operator.
+ ];
+
+ for (var text in bad) {
+ expect(() => new VersionConstraint.parse(text),
+ throwsFormatException);
+ }
+ });
});
});
}