Version 1.13.0-dev.2.0
Merge commit 'c99e5c02fa68a6c583c746b34768e9f6882185b8' into dev
diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html
index 7170448..901200f 100644
--- a/pkg/analysis_server/doc/api.html
+++ b/pkg/analysis_server/doc/api.html
@@ -1511,6 +1511,7 @@
"params": {
"<b>file</b>": <a href="#type_FilePath">FilePath</a>
"<b>offset</b>": int
+ "<b>superOnly</b>": <span style="color:#999999">optional</span> bool
}
}</pre><br><pre>response: {
"id": String
@@ -1536,6 +1537,12 @@
<p>
The offset of the name of the type within the file.
</p>
+ </dd><dt class="field"><b><i>superOnly ( <span style="color:#999999">optional</span> bool )</i></b></dt><dd>
+
+ <p>
+ True if the client is only requesting superclasses and
+ interfaces hierarchy.
+ </p>
</dd></dl><h4>Returns</h4><dl><dt class="field"><b><i>hierarchyItems ( <span style="color:#999999">optional</span> List<<a href="#type_TypeHierarchyItem">TypeHierarchyItem</a>> )</i></b></dt><dd>
<p>
diff --git a/pkg/analysis_server/lib/analysis/analysis_domain.dart b/pkg/analysis_server/lib/analysis/analysis_domain.dart
new file mode 100644
index 0000000..27e8678
--- /dev/null
+++ b/pkg/analysis_server/lib/analysis/analysis_domain.dart
@@ -0,0 +1,53 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/**
+ * Support for client code that extends the analysis aspect of analysis server.
+ */
+library analysis_server.analysis;
+
+import 'dart:async';
+
+import 'package:analysis_server/src/plugin/server_plugin.dart';
+import 'package:analysis_server/src/protocol.dart' show AnalysisService;
+import 'package:analyzer/src/generated/engine.dart'
+ show AnalysisContext, ComputedResult;
+import 'package:analyzer/src/generated/source.dart' show Source;
+import 'package:analyzer/task/model.dart' show ResultDescriptor;
+import 'package:plugin/plugin.dart';
+
+/**
+ * The identifier of the extension point that allows plugins to get access to
+ * `AnalysisSite`. The object used as an extension must be
+ * a [SetAnalysisDomain].
+ */
+final String SET_ANALYSIS_DOMAIN_EXTENSION_POINT_ID = Plugin.join(
+ ServerPlugin.UNIQUE_IDENTIFIER,
+ ServerPlugin.SET_ANALISYS_DOMAIN_EXTENSION_POINT);
+
+/**
+ * A function that is invoked on the `analysis` domain creation.
+ */
+typedef void SetAnalysisDomain(AnalysisDomain site);
+
+/**
+ * An object that gives [SetAnalysisDomain]s access to the `analysis` domain
+ * of the analysis server.
+ *
+ * Clients are not expected to subtype this class.
+ */
+abstract class AnalysisDomain {
+ /**
+ * Return the stream that is notified when a new value for the given
+ * [descriptor] is computed.
+ */
+ Stream<ComputedResult> onResultComputed(ResultDescriptor descriptor);
+
+ /**
+ * Schedule sending the given [service] notifications for the given [source]
+ * in the given [context].
+ */
+ void scheduleNotification(
+ AnalysisContext context, Source source, AnalysisService service);
+}
diff --git a/pkg/analysis_server/lib/src/domain_analysis.dart b/pkg/analysis_server/lib/src/domain_analysis.dart
index 655ca2f..5dd9feb 100644
--- a/pkg/analysis_server/lib/src/domain_analysis.dart
+++ b/pkg/analysis_server/lib/src/domain_analysis.dart
@@ -7,16 +7,23 @@
import 'dart:async';
import 'dart:core' hide Resource;
+import 'package:analysis_server/analysis/analysis_domain.dart';
import 'package:analysis_server/src/analysis_server.dart';
import 'package:analysis_server/src/computer/computer_hover.dart';
import 'package:analysis_server/src/constants.dart';
+import 'package:analysis_server/src/context_manager.dart';
import 'package:analysis_server/src/domains/analysis/navigation.dart';
+import 'package:analysis_server/src/operation/operation_analysis.dart'
+ show sendAnalysisNotificationNavigation;
import 'package:analysis_server/src/protocol_server.dart';
import 'package:analysis_server/src/services/dependencies/library_dependencies.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
import 'package:analyzer/src/generated/engine.dart' as engine;
+import 'package:analyzer/src/generated/java_engine.dart' show CaughtException;
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/task/model.dart' show ResultDescriptor;
/**
* Instances of the class [AnalysisDomainHandler] implement a [RequestHandler]
@@ -31,7 +38,9 @@
/**
* Initialize a newly created handler to handle requests for the given [server].
*/
- AnalysisDomainHandler(this.server);
+ AnalysisDomainHandler(this.server) {
+ _callAnalysisDomainReceivers();
+ }
/**
* Implement the `analysis.getErrors` request.
@@ -290,4 +299,72 @@
server.updateOptions(updaters);
return new AnalysisUpdateOptionsResult().toResponse(request.id);
}
+
+ /**
+ * Call all the registered [SetAnalysisDomain] functions.
+ */
+ void _callAnalysisDomainReceivers() {
+ AnalysisDomain analysisDomain = new AnalysisDomainImpl(server);
+ for (SetAnalysisDomain function
+ in server.serverPlugin.setAnalysisDomainFunctions) {
+ try {
+ function(analysisDomain);
+ } catch (exception, stackTrace) {
+ engine.AnalysisEngine.instance.logger.logError(
+ 'Exception from analysis domain receiver: ${function.runtimeType}',
+ new CaughtException(exception, stackTrace));
+ }
+ }
+ }
+}
+
+/**
+ * An implementation of [AnalysisDomain] for [AnalysisServer].
+ */
+class AnalysisDomainImpl implements AnalysisDomain {
+ final AnalysisServer server;
+
+ final Map<ResultDescriptor,
+ StreamController<engine.ComputedResult>> controllers =
+ <ResultDescriptor, StreamController<engine.ComputedResult>>{};
+
+ AnalysisDomainImpl(this.server) {
+ server.onContextsChanged.listen((ContextsChangedEvent event) {
+ event.added.forEach(_subscribeForContext);
+ });
+ }
+
+ @override
+ Stream<engine.ComputedResult> onResultComputed(ResultDescriptor descriptor) {
+ Stream<engine.ComputedResult> stream = controllers
+ .putIfAbsent(descriptor,
+ () => new StreamController<engine.ComputedResult>.broadcast())
+ .stream;
+ server.getAnalysisContexts().forEach(_subscribeForContext);
+ return stream;
+ }
+
+ @override
+ void scheduleNotification(
+ engine.AnalysisContext context, Source source, AnalysisService service) {
+ // TODO(scheglov) schedule, don't do it right now
+ String file = source.fullName;
+ if (server.hasAnalysisSubscription(service, file)) {
+ if (service == AnalysisService.NAVIGATION) {
+ sendAnalysisNotificationNavigation(server, context, source);
+ }
+ }
+ }
+
+ void _subscribeForContext(engine.AnalysisContext context) {
+ for (ResultDescriptor descriptor in controllers.keys) {
+ context.onResultComputed(descriptor).listen((result) {
+ StreamController<engine.ComputedResult> controller =
+ controllers[result.descriptor];
+ if (controller != null) {
+ controller.add(result);
+ }
+ });
+ }
+ }
}
diff --git a/pkg/analysis_server/lib/src/domains/analysis/navigation.dart b/pkg/analysis_server/lib/src/domains/analysis/navigation.dart
index 8cb08ae..859cd7c 100644
--- a/pkg/analysis_server/lib/src/domains/analysis/navigation.dart
+++ b/pkg/analysis_server/lib/src/domains/analysis/navigation.dart
@@ -32,6 +32,7 @@
new CaughtException(exception, stackTrace));
}
}
+ holder.sortRegions();
return holder;
}
@@ -66,6 +67,12 @@
regions.add(region);
}
+ void sortRegions() {
+ regions.sort((a, b) {
+ return a.offset - b.offset;
+ });
+ }
+
int _addFile(String file) {
int index = fileMap[file];
if (index == null) {
diff --git a/pkg/analysis_server/lib/src/generated_protocol.dart b/pkg/analysis_server/lib/src/generated_protocol.dart
index b6fe193..4ee4157 100644
--- a/pkg/analysis_server/lib/src/generated_protocol.dart
+++ b/pkg/analysis_server/lib/src/generated_protocol.dart
@@ -4339,6 +4339,7 @@
* {
* "file": FilePath
* "offset": int
+ * "superOnly": optional bool
* }
*/
class SearchGetTypeHierarchyParams implements HasToJson {
@@ -4346,6 +4347,8 @@
int _offset;
+ bool _superOnly;
+
/**
* The file containing the declaration or reference to the type for which a
* hierarchy is being requested.
@@ -4374,9 +4377,24 @@
this._offset = value;
}
- SearchGetTypeHierarchyParams(String file, int offset) {
+ /**
+ * True if the client is only requesting superclasses and interfaces
+ * hierarchy.
+ */
+ bool get superOnly => _superOnly;
+
+ /**
+ * True if the client is only requesting superclasses and interfaces
+ * hierarchy.
+ */
+ void set superOnly(bool value) {
+ this._superOnly = value;
+ }
+
+ SearchGetTypeHierarchyParams(String file, int offset, {bool superOnly}) {
this.file = file;
this.offset = offset;
+ this.superOnly = superOnly;
}
factory SearchGetTypeHierarchyParams.fromJson(JsonDecoder jsonDecoder, String jsonPath, Object json) {
@@ -4396,7 +4414,11 @@
} else {
throw jsonDecoder.missingKey(jsonPath, "offset");
}
- return new SearchGetTypeHierarchyParams(file, offset);
+ bool superOnly;
+ if (json.containsKey("superOnly")) {
+ superOnly = jsonDecoder._decodeBool(jsonPath + ".superOnly", json["superOnly"]);
+ }
+ return new SearchGetTypeHierarchyParams(file, offset, superOnly: superOnly);
} else {
throw jsonDecoder.mismatch(jsonPath, "search.getTypeHierarchy params", json);
}
@@ -4411,6 +4433,9 @@
Map<String, dynamic> result = {};
result["file"] = file;
result["offset"] = offset;
+ if (superOnly != null) {
+ result["superOnly"] = superOnly;
+ }
return result;
}
@@ -4425,7 +4450,8 @@
bool operator==(other) {
if (other is SearchGetTypeHierarchyParams) {
return file == other.file &&
- offset == other.offset;
+ offset == other.offset &&
+ superOnly == other.superOnly;
}
return false;
}
@@ -4435,6 +4461,7 @@
int hash = 0;
hash = _JenkinsSmiHash.combine(hash, file.hashCode);
hash = _JenkinsSmiHash.combine(hash, offset.hashCode);
+ hash = _JenkinsSmiHash.combine(hash, superOnly.hashCode);
return _JenkinsSmiHash.finish(hash);
}
}
diff --git a/pkg/analysis_server/lib/src/plugin/server_plugin.dart b/pkg/analysis_server/lib/src/plugin/server_plugin.dart
index 6139725..c1deee0 100644
--- a/pkg/analysis_server/lib/src/plugin/server_plugin.dart
+++ b/pkg/analysis_server/lib/src/plugin/server_plugin.dart
@@ -4,6 +4,7 @@
library analysis_server.src.plugin.server_plugin;
+import 'package:analysis_server/analysis/analysis_domain.dart';
import 'package:analysis_server/analysis/index/index_core.dart';
import 'package:analysis_server/analysis/navigation/navigation_core.dart';
import 'package:analysis_server/completion/completion_core.dart';
@@ -84,6 +85,12 @@
'navigationContributor';
/**
+ * The simple identifier of the extension point that allows plugins to
+ * register analysis result listeners.
+ */
+ static const String SET_ANALISYS_DOMAIN_EXTENSION_POINT = 'setAnalysisDomain';
+
+ /**
* The unique identifier of this plugin.
*/
static const String UNIQUE_IDENTIFIER = 'analysis_server.core';
@@ -128,6 +135,12 @@
ExtensionPoint navigationContributorExtensionPoint;
/**
+ * The extension point that allows plugins to get access to the `analysis`
+ * domain.
+ */
+ ExtensionPoint setAnalysisDomainExtensionPoint;
+
+ /**
* Initialize a newly created plugin.
*/
ServerPlugin();
@@ -173,6 +186,13 @@
List<NavigationContributor> get navigationContributors =>
navigationContributorExtensionPoint.extensions;
+ /**
+ * Return a list containing all of the receivers of the `analysis` domain
+ * instance.
+ */
+ List<SetAnalysisDomain> get setAnalysisDomainFunctions =>
+ setAnalysisDomainExtensionPoint.extensions;
+
@override
String get uniqueIdentifier => UNIQUE_IDENTIFIER;
@@ -191,6 +211,9 @@
@override
void registerExtensionPoints(RegisterExtensionPoint registerExtensionPoint) {
+ setAnalysisDomainExtensionPoint = registerExtensionPoint(
+ SET_ANALISYS_DOMAIN_EXTENSION_POINT,
+ _validateSetAnalysisDomainFunction);
analyzeFileExtensionPoint = registerExtensionPoint(
ANALYZE_FILE_EXTENSION_POINT, _validateAnalyzeFileExtension);
assistContributorExtensionPoint = registerExtensionPoint(
@@ -270,7 +293,7 @@
if (extension is! ShouldAnalyzeFile) {
String id = analyzeFileExtensionPoint.uniqueIdentifier;
throw new ExtensionError(
- 'Extensions to $id must be an ShouldAnalyzeFile function');
+ 'Extensions to $id must be a ShouldAnalyzeFile function');
}
}
@@ -343,4 +366,16 @@
'Extensions to $id must be an NavigationContributor');
}
}
+
+ /**
+ * Validate the given extension by throwing an [ExtensionError] if it is not a
+ * valid analysis domain receiver.
+ */
+ void _validateSetAnalysisDomainFunction(Object extension) {
+ if (extension is! SetAnalysisDomain) {
+ String id = setAnalysisDomainExtensionPoint.uniqueIdentifier;
+ throw new ExtensionError(
+ 'Extensions to $id must be a SetAnalysisDomain function');
+ }
+ }
}
diff --git a/pkg/analysis_server/lib/src/search/search_domain.dart b/pkg/analysis_server/lib/src/search/search_domain.dart
index 7fba8d8..668077b 100644
--- a/pkg/analysis_server/lib/src/search/search_domain.dart
+++ b/pkg/analysis_server/lib/src/search/search_domain.dart
@@ -127,20 +127,34 @@
*/
Future getTypeHierarchy(protocol.Request request) async {
var params = new protocol.SearchGetTypeHierarchyParams.fromRequest(request);
- await server.onAnalysisComplete;
+ String file = params.file;
+ // wait for analysis
+ if (params.superOnly == true) {
+ await server.onFileAnalysisComplete(file);
+ } else {
+ await server.onAnalysisComplete;
+ }
// prepare element
- List<Element> elements =
- server.getElementsAtOffset(params.file, params.offset);
+ List<Element> elements = server.getElementsAtOffset(file, params.offset);
if (elements.isEmpty) {
- protocol.Response response =
- new protocol.SearchGetTypeHierarchyResult().toResponse(request.id);
- server.sendResponse(response);
+ _sendTypeHierarchyNull(request);
return;
}
Element element = elements.first;
+ // maybe supertype hierarchy only
+ if (params.superOnly == true) {
+ TypeHierarchyComputer computer =
+ new TypeHierarchyComputer(searchEngine, element);
+ List<protocol.TypeHierarchyItem> items = computer.computeSuper();
+ protocol.Response response = new protocol.SearchGetTypeHierarchyResult(
+ hierarchyItems: items).toResponse(request.id);
+ server.sendResponse(response);
+ return;
+ }
// prepare type hierarchy
- TypeHierarchyComputer computer = new TypeHierarchyComputer(searchEngine);
- List<protocol.TypeHierarchyItem> items = await computer.compute(element);
+ TypeHierarchyComputer computer =
+ new TypeHierarchyComputer(searchEngine, element);
+ List<protocol.TypeHierarchyItem> items = await computer.compute();
protocol.Response response = new protocol.SearchGetTypeHierarchyResult(
hierarchyItems: items).toResponse(request.id);
server.sendResponse(response);
@@ -190,6 +204,12 @@
server.sendResponse(response);
}
+ void _sendTypeHierarchyNull(protocol.Request request) {
+ protocol.Response response =
+ new protocol.SearchGetTypeHierarchyResult().toResponse(request.id);
+ server.sendResponse(response);
+ }
+
static protocol.SearchResult toResult(SearchMatch match) {
return protocol.newSearchResult_fromMatch(match);
}
diff --git a/pkg/analysis_server/lib/src/search/type_hierarchy.dart b/pkg/analysis_server/lib/src/search/type_hierarchy.dart
index c0c38ed..e9c3772 100644
--- a/pkg/analysis_server/lib/src/search/type_hierarchy.dart
+++ b/pkg/analysis_server/lib/src/search/type_hierarchy.dart
@@ -19,35 +19,42 @@
class TypeHierarchyComputer {
final SearchEngine _searchEngine;
+ Element _pivotElement;
LibraryElement _pivotLibrary;
ElementKind _pivotKind;
- bool _pivotFieldFinal;
String _pivotName;
+ bool _pivotFieldFinal;
+ ClassElement _pivotClass;
final List<TypeHierarchyItem> _items = <TypeHierarchyItem>[];
final List<ClassElement> _itemClassElements = <ClassElement>[];
final Map<Element, TypeHierarchyItem> _elementItemMap =
new HashMap<Element, TypeHierarchyItem>();
- TypeHierarchyComputer(this._searchEngine);
+ TypeHierarchyComputer(this._searchEngine, this._pivotElement) {
+ _pivotLibrary = _pivotElement.library;
+ _pivotKind = _pivotElement.kind;
+ _pivotName = _pivotElement.name;
+ // try to find enclosing ClassElement
+ Element element = _pivotElement;
+ if (_pivotElement is FieldElement) {
+ _pivotFieldFinal = (_pivotElement as FieldElement).isFinal;
+ element = _pivotElement.enclosingElement;
+ }
+ if (_pivotElement is ExecutableElement) {
+ element = _pivotElement.enclosingElement;
+ }
+ if (element is ClassElement) {
+ _pivotClass = element;
+ }
+ }
/**
* Returns the computed type hierarchy, maybe `null`.
*/
- Future<List<TypeHierarchyItem>> compute(Element element) {
- _pivotLibrary = element.library;
- _pivotKind = element.kind;
- _pivotName = element.name;
- if (element is FieldElement) {
- _pivotFieldFinal = (element as FieldElement).isFinal;
- element = element.enclosingElement;
- }
- if (element is ExecutableElement &&
- element.enclosingElement is ClassElement) {
- element = element.enclosingElement;
- }
- if (element is ClassElement) {
- InterfaceType type = element.type;
+ Future<List<TypeHierarchyItem>> compute() {
+ if (_pivotClass != null) {
+ InterfaceType type = _pivotClass.type;
_createSuperItem(type);
return _createSubclasses(_items[0], 0, type).then((_) {
return new Future.value(_items);
@@ -56,6 +63,18 @@
return new Future.value(null);
}
+ /**
+ * Returns the computed super type only type hierarchy, maybe `null`.
+ */
+ List<TypeHierarchyItem> computeSuper() {
+ if (_pivotClass != null) {
+ InterfaceType type = _pivotClass.type;
+ _createSuperItem(type);
+ return _items;
+ }
+ return null;
+ }
+
Future _createSubclasses(
TypeHierarchyItem item, int itemId, InterfaceType type) {
var future = getDirectSubClasses(_searchEngine, type.element);
diff --git a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
index cf87d27..7b6f75a 100644
--- a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
@@ -394,7 +394,7 @@
void _addProposal_convertToBlockFunctionBody() {
FunctionBody body = getEnclosingFunctionBody();
// prepare expression body
- if (body is! ExpressionFunctionBody) {
+ if (body is! ExpressionFunctionBody || body.isGenerator) {
_coverageMarker();
return;
}
@@ -405,10 +405,16 @@
String prefix = utils.getNodePrefix(body.parent);
String indent = utils.getIndent(1);
// add change
- String statementCode =
- (returnValueType.isVoid ? '' : 'return ') + returnValueCode;
SourceBuilder sb = new SourceBuilder(file, body.offset);
- sb.append('{$eol$prefix$indent$statementCode;');
+ if (body.isAsynchronous) {
+ sb.append('async ');
+ }
+ sb.append('{$eol$prefix$indent');
+ if (!returnValueType.isVoid) {
+ sb.append('return ');
+ }
+ sb.append(returnValueCode);
+ sb.append(';');
sb.setExitOffset();
sb.append('$eol$prefix}');
_insertBuilder(sb, body.length);
@@ -419,7 +425,7 @@
void _addProposal_convertToExpressionFunctionBody() {
// prepare current body
FunctionBody body = getEnclosingFunctionBody();
- if (body is! BlockFunctionBody) {
+ if (body is! BlockFunctionBody || body.isGenerator) {
_coverageMarker();
return;
}
@@ -442,12 +448,17 @@
return;
}
// add change
- String newBodySource = '=> ${_getNodeText(returnExpression)}';
+ SourceBuilder sb = new SourceBuilder(file, body.offset);
+ if (body.isAsynchronous) {
+ sb.append('async ');
+ }
+ sb.append('=> ');
+ sb.append(_getNodeText(returnExpression));
if (body.parent is! FunctionExpression ||
body.parent.parent is FunctionDeclaration) {
- newBodySource += ';';
+ sb.append(';');
}
- _addReplaceEdit(rangeNode(body), newBodySource);
+ _insertBuilder(sb, body.length);
// add proposal
_addAssist(DartAssistKind.CONVERT_INTO_EXPRESSION_BODY, []);
}
diff --git a/pkg/analysis_server/test/analysis_abstract.dart b/pkg/analysis_server/test/analysis_abstract.dart
index 539f718..88567a1 100644
--- a/pkg/analysis_server/test/analysis_abstract.dart
+++ b/pkg/analysis_server/test/analysis_abstract.dart
@@ -16,6 +16,7 @@
import 'package:analyzer/file_system/memory_file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
import 'package:plugin/manager.dart';
+import 'package:plugin/plugin.dart';
import 'package:unittest/unittest.dart';
import 'mock_sdk.dart';
@@ -83,6 +84,8 @@
handleSuccessfulRequest(request);
}
+ void addServerPlugins(List<Plugin> plugins) {}
+
String addTestFile(String content) {
addFile(testFile, content);
this.testCode = content;
@@ -90,12 +93,22 @@
}
AnalysisServer createAnalysisServer(Index index) {
- ExtensionManager manager = new ExtensionManager();
ServerPlugin serverPlugin = new ServerPlugin();
- manager.processPlugins([serverPlugin]);
- return new AnalysisServer(serverChannel, resourceProvider,
- packageMapProvider, index, serverPlugin, new AnalysisServerOptions(),
- new MockSdk(), InstrumentationService.NULL_SERVICE);
+ List<Plugin> plugins = <Plugin>[serverPlugin];
+ addServerPlugins(plugins);
+ // process plugins
+ ExtensionManager manager = new ExtensionManager();
+ manager.processPlugins(plugins);
+ // create server
+ return new AnalysisServer(
+ serverChannel,
+ resourceProvider,
+ packageMapProvider,
+ index,
+ serverPlugin,
+ new AnalysisServerOptions(),
+ new MockSdk(),
+ InstrumentationService.NULL_SERVICE);
}
Index createIndex() {
@@ -169,7 +182,8 @@
packageMapProvider = new MockPackageMapProvider();
Index index = createIndex();
server = createAnalysisServer(index);
- handler = new AnalysisDomainHandler(server);
+ handler = server.handlers
+ .singleWhere((handler) => handler is AnalysisDomainHandler);
// listen for notifications
Stream<Notification> notificationStream =
serverChannel.notificationController.stream;
diff --git a/pkg/analysis_server/test/integration/integration_test_methods.dart b/pkg/analysis_server/test/integration/integration_test_methods.dart
index 8f3915e..f6aa68d 100644
--- a/pkg/analysis_server/test/integration/integration_test_methods.dart
+++ b/pkg/analysis_server/test/integration/integration_test_methods.dart
@@ -1062,6 +1062,11 @@
*
* The offset of the name of the type within the file.
*
+ * superOnly ( optional bool )
+ *
+ * True if the client is only requesting superclasses and interfaces
+ * hierarchy.
+ *
* Returns
*
* hierarchyItems ( optional List<TypeHierarchyItem> )
@@ -1076,8 +1081,8 @@
* not represent a type, or if the file has not been sufficiently analyzed
* to allow a type hierarchy to be produced.
*/
- Future<SearchGetTypeHierarchyResult> sendSearchGetTypeHierarchy(String file, int offset) {
- var params = new SearchGetTypeHierarchyParams(file, offset).toJson();
+ Future<SearchGetTypeHierarchyResult> sendSearchGetTypeHierarchy(String file, int offset, {bool superOnly}) {
+ var params = new SearchGetTypeHierarchyParams(file, offset, superOnly: superOnly).toJson();
return server.send("search.getTypeHierarchy", params)
.then((result) {
ResponseDecoder decoder = new ResponseDecoder(null);
diff --git a/pkg/analysis_server/test/integration/protocol_matchers.dart b/pkg/analysis_server/test/integration/protocol_matchers.dart
index 349e3f2..dfef91e 100644
--- a/pkg/analysis_server/test/integration/protocol_matchers.dart
+++ b/pkg/analysis_server/test/integration/protocol_matchers.dart
@@ -629,12 +629,15 @@
* {
* "file": FilePath
* "offset": int
+ * "superOnly": optional bool
* }
*/
final Matcher isSearchGetTypeHierarchyParams = new LazyMatcher(() => new MatchesJsonObject(
"search.getTypeHierarchy params", {
"file": isFilePath,
"offset": isInt
+ }, optionalFields: {
+ "superOnly": isBool
}));
/**
diff --git a/pkg/analysis_server/test/plugin/set_analysis_domain_test.dart b/pkg/analysis_server/test/plugin/set_analysis_domain_test.dart
new file mode 100644
index 0000000..38953fa0
--- /dev/null
+++ b/pkg/analysis_server/test/plugin/set_analysis_domain_test.dart
@@ -0,0 +1,146 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library test.plugin.analysis_contributor;
+
+import 'dart:async';
+
+import 'package:analysis_server/analysis/analysis_domain.dart';
+import 'package:analysis_server/analysis/navigation/navigation_core.dart';
+import 'package:analysis_server/plugin/navigation.dart';
+import 'package:analysis_server/src/constants.dart';
+import 'package:analysis_server/src/protocol.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/task/dart.dart';
+import 'package:plugin/plugin.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+import 'package:unittest/unittest.dart';
+
+import '../analysis_abstract.dart';
+import '../utils.dart';
+
+main() {
+ initializeTestEnvironment();
+ if (AnalysisEngine.instance.useTaskModel) {
+ defineReflectiveTests(SetAnalysisDomainTest);
+ }
+}
+
+/**
+ * This test uses [SET_ANALYSIS_DOMAIN_EXTENSION_POINT_ID] and
+ * [NAVIGATION_CONTRIBUTOR_EXTENSION_POINT_ID] extension points to validate
+ * that plugins can listen for analysis and force sending navigation
+ * notifications.
+ */
+@reflectiveTest
+class SetAnalysisDomainTest extends AbstractAnalysisTest {
+ final Set<String> parsedUnitFiles = new Set<String>();
+ bool contributorMayAddRegion = false;
+
+ List<NavigationRegion> regions;
+ List<NavigationTarget> targets;
+ List<String> targetFiles;
+
+ @override
+ void addServerPlugins(List<Plugin> plugins) {
+ var plugin = new TestSetAnalysisDomainPlugin(this);
+ plugins.add(plugin);
+ }
+
+ @override
+ void processNotification(Notification notification) {
+ if (notification.event == ANALYSIS_NAVIGATION) {
+ var params = new AnalysisNavigationParams.fromNotification(notification);
+ // TODO(scheglov) we check for "params.regions.isNotEmpty" because
+ // normal, Dart only, navigation notifications are scheduled as
+ // operations, but plugins use "notificationSite.scheduleNavigation"
+ // which is not scheduled yet. So, it comes *before* the Dart one, and
+ // gets lost.
+ if (params.file == testFile && params.regions.isNotEmpty) {
+ regions = params.regions;
+ targets = params.targets;
+ targetFiles = params.files;
+ }
+ }
+ }
+
+ Future test_contributorIsInvoked() async {
+ createProject();
+ addAnalysisSubscription(AnalysisService.NAVIGATION, testFile);
+ addTestFile('// usually no navigation');
+ await server.onAnalysisComplete;
+ // we have PARSED_UNIT
+ expect(parsedUnitFiles, contains(testFile));
+ // we have an additional navigation region/target
+ expect(regions, hasLength(1));
+ {
+ NavigationRegion region = regions.single;
+ expect(region.offset, 1);
+ expect(region.length, 5);
+ expect(region.targets.single, 0);
+ }
+ {
+ NavigationTarget target = targets.single;
+ expect(target.fileIndex, 0);
+ expect(target.offset, 1);
+ expect(target.length, 2);
+ expect(target.startLine, 3);
+ expect(target.startColumn, 4);
+ }
+ expect(targetFiles.single, '/testLocation.dart');
+ }
+}
+
+class TestNavigationContributor implements NavigationContributor {
+ final SetAnalysisDomainTest test;
+
+ TestNavigationContributor(this.test);
+
+ @override
+ void computeNavigation(NavigationHolder holder, AnalysisContext context,
+ Source source, int offset, int length) {
+ if (test.contributorMayAddRegion) {
+ holder.addRegion(1, 5, ElementKind.CLASS,
+ new Location('/testLocation.dart', 1, 2, 3, 4));
+ }
+ }
+}
+
+class TestSetAnalysisDomainPlugin implements Plugin {
+ final SetAnalysisDomainTest test;
+
+ TestSetAnalysisDomainPlugin(this.test);
+
+ @override
+ String get uniqueIdentifier => 'test';
+
+ @override
+ void registerExtensionPoints(RegisterExtensionPoint register) {}
+
+ @override
+ void registerExtensions(RegisterExtension register) {
+ register(SET_ANALYSIS_DOMAIN_EXTENSION_POINT_ID, _setAnalysisDomain);
+ register(NAVIGATION_CONTRIBUTOR_EXTENSION_POINT_ID,
+ new TestNavigationContributor(test));
+ }
+
+ void _setAnalysisDomain(AnalysisDomain domain) {
+ domain.onResultComputed(PARSED_UNIT).listen((result) {
+ expect(result.context, isNotNull);
+ expect(result.target, isNotNull);
+ expect(result.value, isNotNull);
+ Source source = result.target.source;
+ test.parsedUnitFiles.add(source.fullName);
+ // let the navigation contributor to work
+ test.contributorMayAddRegion = true;
+ try {
+ domain.scheduleNotification(
+ result.context, source, AnalysisService.NAVIGATION);
+ } finally {
+ test.contributorMayAddRegion = false;
+ }
+ });
+ }
+}
diff --git a/pkg/analysis_server/test/plugin/test_all.dart b/pkg/analysis_server/test/plugin/test_all.dart
new file mode 100644
index 0000000..0dbf7d6
--- /dev/null
+++ b/pkg/analysis_server/test/plugin/test_all.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+library test.plugin.analysis_contributor;
+
+import 'package:unittest/unittest.dart';
+
+import '../utils.dart';
+import 'set_analysis_domain_test.dart' as set_analysis_domain_test;
+
+/**
+ * Utility for manually running all tests.
+ */
+main() {
+ initializeTestEnvironment();
+ group('plugin', () {
+ set_analysis_domain_test.main();
+ });
+}
diff --git a/pkg/analysis_server/test/search/type_hierarchy_test.dart b/pkg/analysis_server/test/search/type_hierarchy_test.dart
index 3dda479..67445aa 100644
--- a/pkg/analysis_server/test/search/type_hierarchy_test.dart
+++ b/pkg/analysis_server/test/search/type_hierarchy_test.dart
@@ -791,14 +791,86 @@
itemD.memberElement.location.offset, findOffset('test(x) {} // in D'));
}
- Request _createGetTypeHierarchyRequest(String search) {
- return new SearchGetTypeHierarchyParams(testFile, findOffset(search))
- .toRequest(requestId);
+ test_superOnly() async {
+ addTestFile('''
+class A {}
+class B {}
+class C extends A implements B {}
+class D extends C {}
+''');
+ List<TypeHierarchyItem> items =
+ await _getTypeHierarchy('C extends', superOnly: true);
+ expect(_toJson(items), [
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
+ 'name': 'C',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'superclass': 1,
+ 'interfaces': [3],
+ 'mixins': [],
+ 'subclasses': []
+ },
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
+ 'name': 'A',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'superclass': 2,
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': []
+ },
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
+ 'name': 'Object',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': []
+ },
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
+ 'name': 'B',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'superclass': 2,
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': []
+ }
+ ]);
}
- Future<List<TypeHierarchyItem>> _getTypeHierarchy(String search) async {
+ test_superOnly_fileDoesNotExist() async {
+ Request request = new SearchGetTypeHierarchyParams(
+ '/does/not/exist.dart', 0,
+ superOnly: true).toRequest(requestId);
+ Response response = await serverChannel.sendRequest(request);
+ List<TypeHierarchyItem> items =
+ new SearchGetTypeHierarchyResult.fromResponse(response).hierarchyItems;
+ expect(items, isNull);
+ }
+
+ Request _createGetTypeHierarchyRequest(String search, {bool superOnly}) {
+ return new SearchGetTypeHierarchyParams(testFile, findOffset(search),
+ superOnly: superOnly).toRequest(requestId);
+ }
+
+ Future<List<TypeHierarchyItem>> _getTypeHierarchy(String search,
+ {bool superOnly}) async {
await waitForTasksFinished();
- Request request = _createGetTypeHierarchyRequest(search);
+ Request request =
+ _createGetTypeHierarchyRequest(search, superOnly: superOnly);
Response response = await serverChannel.sendRequest(request);
expect(serverErrors, isEmpty);
return new SearchGetTypeHierarchyResult.fromResponse(response)
diff --git a/pkg/analysis_server/test/services/correction/assist_test.dart b/pkg/analysis_server/test/services/correction/assist_test.dart
index 4d20cc0..9a256a2 100644
--- a/pkg/analysis_server/test/services/correction/assist_test.dart
+++ b/pkg/analysis_server/test/services/correction/assist_test.dart
@@ -823,6 +823,24 @@
assertNoAssistAt('f();', DartAssistKind.ASSIGN_TO_LOCAL_VARIABLE);
}
+ void test_convertToBlockBody_OK_async() {
+ resolveTestUnit('''
+class A {
+ mmm() async => 123;
+}
+''');
+ assertHasAssistAt(
+ 'mmm()',
+ DartAssistKind.CONVERT_INTO_BLOCK_BODY,
+ '''
+class A {
+ mmm() async {
+ return 123;
+ }
+}
+''');
+ }
+
void test_convertToBlockBody_OK_closure() {
resolveTestUnit('''
setup(x) {}
@@ -955,6 +973,24 @@
assertNoAssistAt('fff() {', DartAssistKind.CONVERT_INTO_BLOCK_BODY);
}
+ void test_convertToExpressionBody_OK_async() {
+ resolveTestUnit('''
+class A {
+ mmm() async {
+ return 42;
+ }
+}
+''');
+ assertHasAssistAt(
+ 'mmm',
+ DartAssistKind.CONVERT_INTO_EXPRESSION_BODY,
+ '''
+class A {
+ mmm() async => 42;
+}
+''');
+ }
+
void test_convertToExpressionBody_OK_closure() {
resolveTestUnit('''
setup(x) {}
diff --git a/pkg/analysis_server/test/test_all.dart b/pkg/analysis_server/test/test_all.dart
index 102b4c7..a89913d 100644
--- a/pkg/analysis_server/test/test_all.dart
+++ b/pkg/analysis_server/test/test_all.dart
@@ -14,6 +14,7 @@
import 'domain_server_test.dart' as domain_server_test;
import 'edit/test_all.dart' as edit_all;
import 'operation/test_all.dart' as operation_test_all;
+import 'plugin/test_all.dart' as plugin_all;
import 'protocol_server_test.dart' as protocol_server_test;
import 'protocol_test.dart' as protocol_test;
import 'search/test_all.dart' as search_all;
@@ -40,6 +41,7 @@
domain_server_test.main();
edit_all.main();
operation_test_all.main();
+ plugin_all.main();
protocol_server_test.main();
protocol_test.main();
search_all.main();
diff --git a/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java b/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java
index c183dac..0e2176a 100644
--- a/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java
+++ b/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java
@@ -518,8 +518,9 @@
* @param file The file containing the declaration or reference to the type for which a hierarchy
* is being requested.
* @param offset The offset of the name of the type within the file.
+ * @param superOnly True if the client is only requesting superclasses and interfaces hierarchy.
*/
- public void search_getTypeHierarchy(String file, int offset, GetTypeHierarchyConsumer consumer);
+ public void search_getTypeHierarchy(String file, int offset, boolean superOnly, GetTypeHierarchyConsumer consumer);
/**
* {@code server.getVersion}
diff --git a/pkg/analysis_server/tool/spec/spec_input.html b/pkg/analysis_server/tool/spec/spec_input.html
index c8b8c3a..974eb97 100644
--- a/pkg/analysis_server/tool/spec/spec_input.html
+++ b/pkg/analysis_server/tool/spec/spec_input.html
@@ -1308,6 +1308,13 @@
The offset of the name of the type within the file.
</p>
</field>
+ <field name="superOnly" optional="true">
+ <ref>bool</ref>
+ <p>
+ True if the client is only requesting superclasses and
+ interfaces hierarchy.
+ </p>
+ </field>
</params>
<result>
<field name="hierarchyItems" optional="true">
diff --git a/pkg/analyzer/lib/src/context/context.dart b/pkg/analyzer/lib/src/context/context.dart
index f472ed1..d90b091 100644
--- a/pkg/analyzer/lib/src/context/context.dart
+++ b/pkg/analyzer/lib/src/context/context.dart
@@ -1142,7 +1142,11 @@
entry = getCacheEntry(unit);
setValue(HINTS, AnalysisError.NO_ERRORS);
// dartEntry.setValue(LINTS, AnalysisError.NO_ERRORS);
- entry.setState(RESOLVE_REFERENCES_ERRORS, CacheState.FLUSHED);
+ setValue(INFER_STATIC_VARIABLE_TYPES_ERRORS, AnalysisError.NO_ERRORS);
+ setValue(LIBRARY_UNIT_ERRORS, AnalysisError.NO_ERRORS);
+ setValue(PARTIALLY_RESOLVE_REFERENCES_ERRORS, AnalysisError.NO_ERRORS);
+ setValue(RESOLVE_FUNCTION_BODIES_ERRORS, AnalysisError.NO_ERRORS);
+ setValue(RESOLVE_TYPE_NAMES_ERRORS, AnalysisError.NO_ERRORS);
entry.setState(RESOLVED_UNIT, CacheState.FLUSHED);
entry.setState(RESOLVED_UNIT1, CacheState.FLUSHED);
entry.setState(RESOLVED_UNIT2, CacheState.FLUSHED);
@@ -1154,6 +1158,7 @@
entry.setState(RESOLVED_UNIT8, CacheState.FLUSHED);
// USED_IMPORTED_ELEMENTS
// USED_LOCAL_ELEMENTS
+ setValue(VARIABLE_REFERENCE_ERRORS, AnalysisError.NO_ERRORS);
setValue(VERIFY_ERRORS, AnalysisError.NO_ERRORS);
});
diff --git a/pkg/analyzer/lib/src/generated/ast.dart b/pkg/analyzer/lib/src/generated/ast.dart
index 7940662..0c5dfc2 100644
--- a/pkg/analyzer/lib/src/generated/ast.dart
+++ b/pkg/analyzer/lib/src/generated/ast.dart
@@ -3477,7 +3477,12 @@
}
@override
- Token get beginToken => _block.beginToken;
+ Token get beginToken {
+ if (keyword != null) {
+ return keyword;
+ }
+ return _block.beginToken;
+ }
/**
* Return the block representing the body of the function.
diff --git a/pkg/analyzer/lib/src/generated/engine.dart b/pkg/analyzer/lib/src/generated/engine.dart
index 0e8f9c8..5eccbf7 100644
--- a/pkg/analyzer/lib/src/generated/engine.dart
+++ b/pkg/analyzer/lib/src/generated/engine.dart
@@ -556,7 +556,8 @@
* Perform work until the given [result] has been computed for the given
* [target]. Return the computed value.
*/
- Object /*V*/ computeResult(AnalysisTarget target, ResultDescriptor /*<V>*/ result);
+ Object /*V*/ computeResult(
+ AnalysisTarget target, ResultDescriptor /*<V>*/ result);
/**
* Notifies the context that the client is going to stop using this context.
@@ -729,7 +730,8 @@
* If the corresponding [target] does not exist, or the [result] is not
* computed yet, then the default value is returned.
*/
- Object /*V*/ getResult(AnalysisTarget target, ResultDescriptor /*<V>*/ result);
+ Object /*V*/ getResult(
+ AnalysisTarget target, ResultDescriptor /*<V>*/ result);
/**
* Return a list of the sources being analyzed in this context whose full path
@@ -5903,6 +5905,11 @@
bool limitInvalidationInTaskModel = false;
/**
+ * The plugins that are defined outside the `analyzer` package.
+ */
+ List<Plugin> _userDefinedPlugins = <Plugin>[];
+
+ /**
* The task manager used to manage the tasks used to analyze code.
*/
TaskManager _taskManager;
@@ -5951,6 +5958,7 @@
commandLinePlugin,
optionsPlugin
];
+ _supportedPlugins.addAll(_userDefinedPlugins);
}
return _supportedPlugins;
}
@@ -5960,10 +5968,7 @@
*/
TaskManager get taskManager {
if (_taskManager == null) {
- if (enginePlugin.taskExtensionPoint == null) {
- // The plugin wasn't used, so tasks are not registered.
- new ExtensionManager().processPlugins([enginePlugin]);
- }
+ new ExtensionManager().processPlugins(supportedPlugins);
_taskManager = new TaskManager();
_taskManager.addTaskDescriptors(enginePlugin.taskDescriptors);
// TODO(brianwilkerson) Create a way to associate different results with
@@ -5974,6 +5979,18 @@
}
/**
+ * Set plugins that are defined outside the `analyzer` package.
+ */
+ void set userDefinedPlugins(List<Plugin> plugins) {
+ if (plugins == null) {
+ plugins = <Plugin>[];
+ }
+ _userDefinedPlugins = plugins;
+ _supportedPlugins = null;
+ _taskManager = null;
+ }
+
+ /**
* Clear any caches holding on to analysis results so that a full re-analysis
* will be performed the next time an analysis context is created.
*/
diff --git a/pkg/analyzer/lib/src/generated/incremental_resolver.dart b/pkg/analyzer/lib/src/generated/incremental_resolver.dart
index ea1e0fa..5feaaa9 100644
--- a/pkg/analyzer/lib/src/generated/incremental_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/incremental_resolver.dart
@@ -13,8 +13,11 @@
import 'package:analyzer/src/task/dart.dart'
show
HINTS,
+ INFER_STATIC_VARIABLE_TYPES_ERRORS,
+ LIBRARY_UNIT_ERRORS,
PARSE_ERRORS,
- RESOLVE_REFERENCES_ERRORS,
+ PARTIALLY_RESOLVE_REFERENCES_ERRORS,
+ RESOLVE_FUNCTION_BODIES_ERRORS,
RESOLVE_TYPE_NAMES_ERRORS,
SCAN_ERRORS,
USED_IMPORTED_ELEMENTS,
@@ -1125,7 +1128,10 @@
void _shiftEntryErrors_NEW() {
_shiftErrors_NEW(HINTS);
- _shiftErrors_NEW(RESOLVE_REFERENCES_ERRORS);
+ _shiftErrors_NEW(INFER_STATIC_VARIABLE_TYPES_ERRORS);
+ _shiftErrors_NEW(LIBRARY_UNIT_ERRORS);
+ _shiftErrors_NEW(PARTIALLY_RESOLVE_REFERENCES_ERRORS);
+ _shiftErrors_NEW(RESOLVE_FUNCTION_BODIES_ERRORS);
_shiftErrors_NEW(RESOLVE_TYPE_NAMES_ERRORS);
_shiftErrors_NEW(VARIABLE_REFERENCE_ERRORS);
_shiftErrors_NEW(VERIFY_ERRORS);
@@ -1177,7 +1183,10 @@
}
void _updateEntry_NEW() {
- _updateErrors_NEW(RESOLVE_REFERENCES_ERRORS, _resolveErrors);
+ _updateErrors_NEW(INFER_STATIC_VARIABLE_TYPES_ERRORS, _resolveErrors);
+ _updateErrors_NEW(LIBRARY_UNIT_ERRORS, _resolveErrors);
+ _updateErrors_NEW(PARTIALLY_RESOLVE_REFERENCES_ERRORS, _resolveErrors);
+ _updateErrors_NEW(RESOLVE_FUNCTION_BODIES_ERRORS, _resolveErrors);
_updateErrors_NEW(RESOLVE_TYPE_NAMES_ERRORS, []);
_updateErrors_NEW(VARIABLE_REFERENCE_ERRORS, []);
_updateErrors_NEW(VERIFY_ERRORS, _verifyErrors);
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index eb7bacf..4a17411 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -3930,11 +3930,23 @@
bool visitAsExpression(AsExpression node) => _nodeExits(node.expression);
@override
- bool visitAssertStatement(AssertStatement node) => _nodeExits(node.condition);
+ bool visitAssertStatement(AssertStatement node) => false;
@override
- bool visitAssignmentExpression(AssignmentExpression node) =>
- _nodeExits(node.leftHandSide) || _nodeExits(node.rightHandSide);
+ bool visitAssignmentExpression(AssignmentExpression node) {
+ Expression leftHandSide = node.leftHandSide;
+ if (_nodeExits(leftHandSide)) {
+ return true;
+ }
+ if (node.operator.type == sc.TokenType.QUESTION_QUESTION_EQ) {
+ return false;
+ }
+ if (leftHandSide is PropertyAccess &&
+ leftHandSide.operator.type == sc.TokenType.QUESTION_PERIOD) {
+ return false;
+ }
+ return _nodeExits(node.rightHandSide);
+ }
@override
bool visitAwaitExpression(AwaitExpression node) =>
@@ -3943,9 +3955,10 @@
@override
bool visitBinaryExpression(BinaryExpression node) {
Expression lhsExpression = node.leftOperand;
+ Expression rhsExpression = node.rightOperand;
sc.TokenType operatorType = node.operator.type;
- // If the operator is || and the left hand side is false literal, don't
- // consider the RHS of the binary expression.
+ // If the operator is ||, then only consider the RHS of the binary
+ // expression if the left hand side is the false literal.
// TODO(jwren) Do we want to take constant expressions into account,
// evaluate if(false) {} differently than if(<condition>), when <condition>
// evaluates to a constant false value?
@@ -3953,21 +3966,27 @@
if (lhsExpression is BooleanLiteral) {
BooleanLiteral booleanLiteral = lhsExpression;
if (!booleanLiteral.value) {
- return false;
+ return _nodeExits(rhsExpression);
}
}
+ return _nodeExits(lhsExpression);
}
- // If the operator is && and the left hand side is true literal, don't
- // consider the RHS of the binary expression.
+ // If the operator is &&, then only consider the RHS of the binary
+ // expression if the left hand side is the true literal.
if (operatorType == sc.TokenType.AMPERSAND_AMPERSAND) {
if (lhsExpression is BooleanLiteral) {
BooleanLiteral booleanLiteral = lhsExpression;
if (booleanLiteral.value) {
- return false;
+ return _nodeExits(rhsExpression);
}
}
+ return _nodeExits(lhsExpression);
}
- Expression rhsExpression = node.rightOperand;
+ // If the operator is ??, then don't consider the RHS of the binary
+ // expression.
+ if (operatorType == sc.TokenType.QUESTION_QUESTION) {
+ return _nodeExits(lhsExpression);
+ }
return _nodeExits(lhsExpression) || _nodeExits(rhsExpression);
}
@@ -4162,8 +4181,13 @@
@override
bool visitMethodInvocation(MethodInvocation node) {
Expression target = node.realTarget;
- if (target != null && target.accept(this)) {
- return true;
+ if (target != null) {
+ if (target.accept(this)) {
+ return true;
+ }
+ if (node.operator.type == sc.TokenType.QUESTION_PERIOD) {
+ return false;
+ }
}
return _nodeExits(node.argumentList);
}
diff --git a/pkg/analyzer/lib/src/plugin/engine_plugin.dart b/pkg/analyzer/lib/src/plugin/engine_plugin.dart
index 93f61d1..65ead96 100644
--- a/pkg/analyzer/lib/src/plugin/engine_plugin.dart
+++ b/pkg/analyzer/lib/src/plugin/engine_plugin.dart
@@ -105,7 +105,8 @@
registerExtension(taskId, BuildTypeProviderTask.DESCRIPTOR);
registerExtension(taskId, ComputeConstantDependenciesTask.DESCRIPTOR);
registerExtension(taskId, ComputeConstantValueTask.DESCRIPTOR);
- registerExtension(taskId, ComputeInferableStaticVariableDependenciesTask.DESCRIPTOR);
+ registerExtension(
+ taskId, ComputeInferableStaticVariableDependenciesTask.DESCRIPTOR);
registerExtension(taskId, ContainingLibrariesTask.DESCRIPTOR);
registerExtension(taskId, DartErrorsTask.DESCRIPTOR);
registerExtension(taskId, EvaluateUnitConstantsTask.DESCRIPTOR);
@@ -119,9 +120,9 @@
registerExtension(taskId, LibraryUnitErrorsTask.DESCRIPTOR);
registerExtension(taskId, ParseDartTask.DESCRIPTOR);
registerExtension(taskId, PartiallyResolveUnitReferencesTask.DESCRIPTOR);
+ registerExtension(taskId, ResolveFunctionBodiesInUnitTask.DESCRIPTOR);
registerExtension(taskId, ResolveLibraryReferencesTask.DESCRIPTOR);
registerExtension(taskId, ResolveLibraryTypeNamesTask.DESCRIPTOR);
- registerExtension(taskId, ResolveUnitReferencesTask.DESCRIPTOR);
registerExtension(taskId, ResolveUnitTypeNamesTask.DESCRIPTOR);
registerExtension(taskId, ResolveVariableReferencesTask.DESCRIPTOR);
registerExtension(taskId, ScanDartTask.DESCRIPTOR);
diff --git a/pkg/analyzer/lib/src/task/dart.dart b/pkg/analyzer/lib/src/task/dart.dart
index 65e391f..e81545c 100644
--- a/pkg/analyzer/lib/src/task/dart.dart
+++ b/pkg/analyzer/lib/src/task/dart.dart
@@ -162,6 +162,30 @@
new ListResultDescriptor<Source>('IMPORT_EXPORT_SOURCE_CLOSURE', null);
/**
+ * The errors produced while inferring the type of a static variable.
+ *
+ * The list will be empty if there were no errors, but will not be `null`.
+ *
+ * The result is only available for [VariableElement]s, and only when strong
+ * mode is enabled.
+ */
+final ListResultDescriptor<AnalysisError> INFER_STATIC_VARIABLE_ERRORS =
+ new ListResultDescriptor<AnalysisError>(
+ 'INFER_STATIC_VARIABLE_ERRORS', AnalysisError.NO_ERRORS);
+
+/**
+ * The errors produced while inferring the types of all of the static variables
+ * in a compilation unit.
+ *
+ * The list will be empty if there were no errors, but will not be `null`.
+ *
+ * The result is only available for [LibrarySpecificUnit]s.
+ */
+final ListResultDescriptor<AnalysisError> INFER_STATIC_VARIABLE_TYPES_ERRORS =
+ new ListResultDescriptor<AnalysisError>(
+ 'INFER_STATIC_VARIABLE_TYPES_ERRORS', AnalysisError.NO_ERRORS);
+
+/**
* A list of the [VariableElement]s whose type should be inferred that another
* inferable static variable (the target) depends on.
*
@@ -188,6 +212,9 @@
/**
* An inferrable static variable ([VariableElement]) whose type has been
* inferred.
+ *
+ * The result is only available for [VariableElement]s, and only when strong
+ * mode is enabled.
*/
final ResultDescriptor<VariableElement> INFERRED_STATIC_VARIABLE =
new ResultDescriptor<VariableElement>('INFERRED_STATIC_VARIABLE', null,
@@ -303,15 +330,16 @@
new ResultDescriptor<ReferencedNames>('REFERENCED_NAMES', null);
/**
- * The errors produced while resolving references.
+ * The errors produced while resolving the function bodies within a compilation
+ * unit.
*
* The list will be empty if there were no errors, but will not be `null`.
*
* The result is only available for [LibrarySpecificUnit]s.
*/
-final ListResultDescriptor<AnalysisError> RESOLVE_REFERENCES_ERRORS =
+final ListResultDescriptor<AnalysisError> RESOLVE_FUNCTION_BODIES_ERRORS =
new ListResultDescriptor<AnalysisError>(
- 'RESOLVE_REFERENCES_ERRORS', AnalysisError.NO_ERRORS);
+ 'RESOLVE_FUNCTION_BODIES_ERRORS', AnalysisError.NO_ERRORS);
/**
* The errors produced while resolving type names.
@@ -2534,13 +2562,20 @@
static const String INFERRED_VARIABLES_INPUT = 'INFERRED_VARIABLES_INPUT';
/**
+ * The name of the input whose value is a list of the errors produced while
+ * inferring types for static variables.
+ */
+ static const String INFER_STATIC_VARIABLE_ERRORS_INPUT =
+ 'INFER_STATIC_VARIABLE_ERRORS';
+
+ /**
* The task descriptor describing this kind of task.
*/
static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
'InferStaticVariableTypesInUnitTask',
createTask,
buildInputs,
- <ResultDescriptor>[RESOLVED_UNIT6]);
+ <ResultDescriptor>[INFER_STATIC_VARIABLE_TYPES_ERRORS, RESOLVED_UNIT6]);
/**
* Initialize a newly created task to build a library element for the given
@@ -2559,11 +2594,18 @@
// Prepare inputs.
//
CompilationUnit unit = getRequiredInput(UNIT_INPUT);
+ List<List<AnalysisError>> perVariableErrors =
+ getRequiredInput(INFER_STATIC_VARIABLE_ERRORS_INPUT);
+ List<AnalysisError> errors = <AnalysisError>[];
+ for (List<AnalysisError> variableErrors in perVariableErrors) {
+ errors.addAll(variableErrors);
+ }
//
// Record outputs. There is no additional work to be done at this time
// because the work has implicitly been done by virtue of the task model
// preparing all of the inputs.
//
+ outputs[INFER_STATIC_VARIABLE_TYPES_ERRORS] = errors;
outputs[RESOLVED_UNIT6] = unit;
}
@@ -2578,6 +2620,9 @@
INFERRED_VARIABLES_INPUT: INFERABLE_STATIC_VARIABLES_IN_UNIT
.of(unit)
.toListOf(INFERRED_STATIC_VARIABLE),
+ INFER_STATIC_VARIABLE_ERRORS_INPUT: INFERABLE_STATIC_VARIABLES_IN_UNIT
+ .of(unit)
+ .toListOf(INFER_STATIC_VARIABLE_ERRORS),
UNIT_INPUT: RESOLVED_UNIT5.of(unit)
};
}
@@ -2616,8 +2661,10 @@
static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
'InferStaticVariableTypeTask',
createTask,
- buildInputs,
- <ResultDescriptor>[INFERRED_STATIC_VARIABLE]);
+ buildInputs, <ResultDescriptor>[
+ INFER_STATIC_VARIABLE_ERRORS,
+ INFERRED_STATIC_VARIABLE
+ ]);
InferStaticVariableTypeTask(
InternalAnalysisContext context, VariableElement variable)
@@ -2641,6 +2688,7 @@
VariableElementImpl variable = target;
CompilationUnit unit = getRequiredInput(UNIT_INPUT);
TypeProvider typeProvider = getRequiredInput(TYPE_PROVIDER_INPUT);
+ RecordingErrorListener errorListener = new RecordingErrorListener();
if (dependencyCycle == null) {
//
// Re-resolve the variable's initializer so that the inferred types of other
@@ -2654,7 +2702,6 @@
throw new AnalysisException(
"NodeLocator failed to find a variable's declaration");
}
- RecordingErrorListener errorListener = new RecordingErrorListener();
Expression initializer = declaration.initializer;
ResolutionContext resolutionContext =
ResolutionContextBuilder.contextFor(initializer, errorListener);
@@ -2692,6 +2739,7 @@
// Record outputs.
//
outputs[INFERRED_STATIC_VARIABLE] = variable;
+ outputs[INFER_STATIC_VARIABLE_ERRORS] = errorListener.errors;
}
/**
@@ -2780,10 +2828,22 @@
static const String HINTS_INPUT = 'HINTS';
/**
- * The name of the [RESOLVE_REFERENCES_ERRORS] input.
+ * The name of the [INFER_STATIC_VARIABLE_TYPES_ERRORS] input.
*/
- static const String RESOLVE_REFERENCES_ERRORS_INPUT =
- 'RESOLVE_REFERENCES_ERRORS';
+ static const String INFER_STATIC_VARIABLE_TYPES_ERRORS_INPUT =
+ 'INFER_STATIC_VARIABLE_TYPES_ERRORS';
+
+ /**
+ * The name of the [PARTIALLY_RESOLVE_REFERENCES_ERRORS] input.
+ */
+ static const String PARTIALLY_RESOLVE_REFERENCES_ERRORS_INPUT =
+ 'PARTIALLY_RESOLVE_REFERENCES_ERRORS';
+
+ /**
+ * The name of the [RESOLVE_FUNCTION_BODIES_ERRORS] input.
+ */
+ static const String RESOLVE_FUNCTION_BODIES_ERRORS_INPUT =
+ 'RESOLVE_FUNCTION_BODIES_ERRORS';
/**
* The name of the [RESOLVE_TYPE_NAMES_ERRORS] input.
@@ -2824,7 +2884,9 @@
//
List<List<AnalysisError>> errorLists = <List<AnalysisError>>[];
errorLists.add(getRequiredInput(HINTS_INPUT));
- errorLists.add(getRequiredInput(RESOLVE_REFERENCES_ERRORS_INPUT));
+ errorLists.add(getRequiredInput(INFER_STATIC_VARIABLE_TYPES_ERRORS_INPUT));
+ errorLists.add(getRequiredInput(PARTIALLY_RESOLVE_REFERENCES_ERRORS_INPUT));
+ errorLists.add(getRequiredInput(RESOLVE_FUNCTION_BODIES_ERRORS_INPUT));
errorLists.add(getRequiredInput(RESOLVE_TYPE_NAMES_ERRORS_INPUT));
errorLists.add(getRequiredInput(VARIABLE_REFERENCE_ERRORS_INPUT));
errorLists.add(getRequiredInput(VERIFY_ERRORS_INPUT));
@@ -2843,7 +2905,12 @@
LibrarySpecificUnit unit = target;
return <String, TaskInput>{
HINTS_INPUT: HINTS.of(unit),
- RESOLVE_REFERENCES_ERRORS_INPUT: RESOLVE_REFERENCES_ERRORS.of(unit),
+ INFER_STATIC_VARIABLE_TYPES_ERRORS_INPUT:
+ INFER_STATIC_VARIABLE_TYPES_ERRORS.of(unit),
+ PARTIALLY_RESOLVE_REFERENCES_ERRORS_INPUT:
+ PARTIALLY_RESOLVE_REFERENCES_ERRORS.of(unit),
+ RESOLVE_FUNCTION_BODIES_ERRORS_INPUT:
+ RESOLVE_FUNCTION_BODIES_ERRORS.of(unit),
RESOLVE_TYPE_NAMES_ERRORS_INPUT: RESOLVE_TYPE_NAMES_ERRORS.of(unit),
VARIABLE_REFERENCE_ERRORS_INPUT: VARIABLE_REFERENCE_ERRORS.of(unit),
VERIFY_ERRORS_INPUT: VERIFY_ERRORS.of(unit)
@@ -3295,6 +3362,110 @@
}
/**
+ * A task that resolves the bodies of top-level functions, constructors, and
+ * methods within a single compilation unit.
+ */
+class ResolveFunctionBodiesInUnitTask extends SourceBasedAnalysisTask {
+ /**
+ * The name of the [TYPE_PROVIDER] input.
+ */
+ static const String TYPE_PROVIDER_INPUT = 'TYPE_PROVIDER_INPUT';
+
+ /**
+ * The name of the [RESOLVED_UNIT7] input.
+ */
+ static const String UNIT_INPUT = 'UNIT_INPUT';
+
+ static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+ 'ResolveFunctionBodiesInUnitTask',
+ createTask,
+ buildInputs,
+ <ResultDescriptor>[RESOLVE_FUNCTION_BODIES_ERRORS, RESOLVED_UNIT8]);
+
+ ResolveFunctionBodiesInUnitTask(
+ InternalAnalysisContext context, LibrarySpecificUnit compilationUnit)
+ : super(context, compilationUnit);
+
+ @override
+ TaskDescriptor get descriptor => DESCRIPTOR;
+
+ @override
+ void internalPerform() {
+ //
+ // Prepare inputs.
+ //
+ CompilationUnit unit = getRequiredInput(UNIT_INPUT);
+ TypeProvider typeProvider = getRequiredInput(TYPE_PROVIDER_INPUT);
+ //
+ // Resolve function bodies.
+ //
+ CompilationUnitElement unitElement = unit.element;
+ RecordingErrorListener errorListener = new RecordingErrorListener();
+ for (CompilationUnitMember unitMember in unit.declarations) {
+ if (unitMember is FunctionDeclaration) {
+ _resolveFunctionBody(unitMember.functionExpression.body, unitElement,
+ typeProvider, errorListener);
+ } else if (unitMember is ClassDeclaration) {
+ for (ClassMember classMember in unitMember.members) {
+ if (classMember is ConstructorDeclaration) {
+ _resolveFunctionBody(
+ classMember.body, unitElement, typeProvider, errorListener);
+ } else if (classMember is MethodDeclaration) {
+ _resolveFunctionBody(
+ classMember.body, unitElement, typeProvider, errorListener);
+ }
+ }
+ }
+ }
+ //
+ // Record outputs.
+ //
+ outputs[RESOLVE_FUNCTION_BODIES_ERRORS] = errorListener.errors;
+ outputs[RESOLVED_UNIT8] = unit;
+ }
+
+ void _resolveFunctionBody(
+ FunctionBody functionBody,
+ CompilationUnitElement unitElement,
+ TypeProvider typeProvider,
+ RecordingErrorListener errorListener) {
+ ResolutionContext resolutionContext =
+ ResolutionContextBuilder.contextFor(functionBody, errorListener);
+ ResolverVisitor visitor = new ResolverVisitor(
+ unitElement.library, unitElement.source, typeProvider, errorListener,
+ nameScope: resolutionContext.scope);
+ if (resolutionContext.enclosingClassDeclaration != null) {
+ visitor.prepareToResolveMembersInClass(
+ resolutionContext.enclosingClassDeclaration);
+ }
+ visitor.initForIncrementalResolution();
+ functionBody.accept(visitor);
+ }
+
+ /**
+ * Return a map from the names of the inputs of this kind of task to the task
+ * input descriptors describing those inputs for a task with the given
+ * [target].
+ */
+ static Map<String, TaskInput> buildInputs(AnalysisTarget target) {
+ LibrarySpecificUnit unit = target;
+ return <String, TaskInput>{
+ TYPE_PROVIDER_INPUT: TYPE_PROVIDER.of(AnalysisContextTarget.request),
+ UNIT_INPUT: RESOLVED_UNIT7.of(unit)
+ };
+ }
+
+ /**
+ * Create a [ResolveFunctionBodiesInUnitTask] based on the given [target] in
+ * the given [context].
+ */
+ static ResolveFunctionBodiesInUnitTask createTask(
+ AnalysisContext context, AnalysisTarget target) {
+ return new ResolveFunctionBodiesInUnitTask(context, target);
+ }
+}
+
+/**
* A task that finishes resolution by requesting [RESOLVED_UNIT_NO_CONSTANTS] for every
* unit in the libraries closure and produces [LIBRARY_ELEMENT].
*/
@@ -3430,95 +3601,6 @@
}
/**
- * A task that builds [RESOLVED_UNIT8] for a unit.
- */
-class ResolveUnitReferencesTask extends SourceBasedAnalysisTask {
- /**
- * The name of the [LIBRARY_ELEMENT5] input.
- */
- static const String LIBRARY_INPUT = 'LIBRARY_INPUT';
-
- /**
- * The name of the [RESOLVED_UNIT4] input.
- */
- static const String UNIT_INPUT = 'UNIT_INPUT';
-
- /**
- * The name of the [TYPE_PROVIDER] input.
- */
- static const String TYPE_PROVIDER_INPUT = 'TYPE_PROVIDER_INPUT';
-
- /**
- * The task descriptor describing this kind of task.
- */
- static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
- 'ResolveUnitReferencesTask',
- createTask,
- buildInputs,
- <ResultDescriptor>[RESOLVE_REFERENCES_ERRORS, RESOLVED_UNIT8]);
-
- ResolveUnitReferencesTask(
- InternalAnalysisContext context, AnalysisTarget target)
- : super(context, target);
-
- @override
- TaskDescriptor get descriptor => DESCRIPTOR;
-
- @override
- void internalPerform() {
- RecordingErrorListener errorListener = new RecordingErrorListener();
- //
- // Prepare inputs.
- //
- LibraryElement libraryElement = getRequiredInput(LIBRARY_INPUT);
- CompilationUnit unit = getRequiredInput(UNIT_INPUT);
- CompilationUnitElement unitElement = unit.element;
- TypeProvider typeProvider = getRequiredInput(TYPE_PROVIDER_INPUT);
- //
- // Resolve references.
- //
- InheritanceManager inheritanceManager =
- new InheritanceManager(libraryElement);
- AstVisitor visitor = new ResolverVisitor(
- libraryElement, unitElement.source, typeProvider, errorListener,
- inheritanceManager: inheritanceManager);
- unit.accept(visitor);
- //
- // Record outputs.
- //
- outputs[RESOLVE_REFERENCES_ERRORS] =
- removeDuplicateErrors(errorListener.errors);
- outputs[RESOLVED_UNIT8] = unit;
- }
-
- /**
- * Return a map from the names of the inputs of this kind of task to the task
- * input descriptors describing those inputs for a task with the
- * given [target].
- */
- static Map<String, TaskInput> buildInputs(AnalysisTarget target) {
- LibrarySpecificUnit unit = target;
- return <String, TaskInput>{
- 'fullyBuiltLibraryElements': IMPORT_EXPORT_SOURCE_CLOSURE
- .of(unit.library)
- .toListOf(LIBRARY_ELEMENT5),
- LIBRARY_INPUT: LIBRARY_ELEMENT5.of(unit.library),
- UNIT_INPUT: RESOLVED_UNIT4.of(unit),
- TYPE_PROVIDER_INPUT: TYPE_PROVIDER.of(AnalysisContextTarget.request)
- };
- }
-
- /**
- * Create a [ResolveUnitReferencesTask] based on the given [target] in
- * the given [context].
- */
- static ResolveUnitReferencesTask createTask(
- AnalysisContext context, AnalysisTarget target) {
- return new ResolveUnitReferencesTask(context, target);
- }
-}
-
-/**
* A task that builds [RESOLVED_UNIT3] for a unit.
*/
class ResolveUnitTypeNamesTask extends SourceBasedAnalysisTask {
diff --git a/pkg/analyzer/lib/src/task/dart_work_manager.dart b/pkg/analyzer/lib/src/task/dart_work_manager.dart
index f813fe9..5b66e0b 100644
--- a/pkg/analyzer/lib/src/task/dart_work_manager.dart
+++ b/pkg/analyzer/lib/src/task/dart_work_manager.dart
@@ -19,10 +19,9 @@
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/utilities_collection.dart';
import 'package:analyzer/src/task/dart.dart';
-import 'package:analyzer/src/task/driver.dart';
+import 'package:analyzer/src/task/html.dart';
import 'package:analyzer/task/dart.dart';
import 'package:analyzer/task/model.dart';
-import 'package:analyzer/src/task/html.dart';
/**
* The manager for Dart specific analysis.
@@ -43,7 +42,10 @@
*/
static final List<ResultDescriptor> _UNIT_ERRORS = <ResultDescriptor>[
HINTS,
- RESOLVE_REFERENCES_ERRORS,
+ INFER_STATIC_VARIABLE_TYPES_ERRORS,
+ LIBRARY_UNIT_ERRORS,
+ PARTIALLY_RESOLVE_REFERENCES_ERRORS,
+ RESOLVE_FUNCTION_BODIES_ERRORS,
RESOLVE_TYPE_NAMES_ERRORS,
VARIABLE_REFERENCE_ERRORS,
VERIFY_ERRORS
diff --git a/pkg/analyzer/test/generated/all_the_rest_test.dart b/pkg/analyzer/test/generated/all_the_rest_test.dart
index ec4b9c0..9765af9 100644
--- a/pkg/analyzer/test/generated/all_the_rest_test.dart
+++ b/pkg/analyzer/test/generated/all_the_rest_test.dart
@@ -7351,7 +7351,7 @@
}
void test_assertStatement_throw() {
- _assertTrue("assert((throw 0));");
+ _assertFalse("assert((throw 0));");
}
void test_assignmentExpression() {
@@ -7383,15 +7383,31 @@
}
void test_binaryExpression_and_rhs() {
- _assertTrue("a && (throw '');");
+ _assertFalse("a && (throw '');");
}
void test_binaryExpression_and_rhs2() {
- _assertTrue("false && (throw '');");
+ _assertFalse("false && (throw '');");
}
void test_binaryExpression_and_rhs3() {
- _assertFalse("true && (throw '');");
+ _assertTrue("true && (throw '');");
+ }
+
+ void test_binaryExpression_ifNull() {
+ _assertFalse("a ?? b;");
+ }
+
+ void test_binaryExpression_ifNull_lhs() {
+ _assertTrue("throw '' ?? b;");
+ }
+
+ void test_binaryExpression_ifNull_rhs() {
+ _assertFalse("a ?? (throw '');");
+ }
+
+ void test_binaryExpression_ifNull_rhs2() {
+ _assertFalse("null ?? (throw '');");
}
void test_binaryExpression_or() {
@@ -7403,15 +7419,15 @@
}
void test_binaryExpression_or_rhs() {
- _assertTrue("a || (throw '');");
+ _assertFalse("a || (throw '');");
}
void test_binaryExpression_or_rhs2() {
- _assertTrue("true || (throw '');");
+ _assertFalse("true || (throw '');");
}
void test_binaryExpression_or_rhs3() {
- _assertFalse("false || (throw '');");
+ _assertTrue("false || (throw '');");
}
void test_block_empty() {
@@ -7462,6 +7478,62 @@
_assertFalse("c ? throw '' : j;");
}
+ void test_conditionalAccess() {
+ _assertFalse("a?.b;");
+ }
+
+ void test_conditionalAccess_lhs() {
+ _assertTrue("(throw '')?.b;");
+ }
+
+ void test_conditionalAccessAssign() {
+ _assertFalse("a?.b = c;");
+ }
+
+ void test_conditionalAccessAssign_lhs() {
+ _assertTrue("(throw '')?.b = c;");
+ }
+
+ void test_conditionalAccessAssign_rhs() {
+ _assertFalse("a?.b = throw '';");
+ }
+
+ void test_conditionalAccessAssign_rhs2() {
+ _assertFalse("null?.b = throw '';");
+ }
+
+ void test_conditionalAccessIfNullAssign() {
+ _assertFalse("a?.b ??= c;");
+ }
+
+ void test_conditionalAccessIfNullAssign_lhs() {
+ _assertTrue("(throw '')?.b ??= c;");
+ }
+
+ void test_conditionalAccessIfNullAssign_rhs() {
+ _assertFalse("a?.b ??= throw '';");
+ }
+
+ void test_conditionalAccessIfNullAssign_rhs2() {
+ _assertFalse("null?.b ??= throw '';");
+ }
+
+ void test_conditionalCall() {
+ _assertFalse("a?.b(c);");
+ }
+
+ void test_conditionalCall_lhs() {
+ _assertTrue("(throw '')?.b(c);");
+ }
+
+ void test_conditionalCall_rhs() {
+ _assertFalse("a?.b(throw '');");
+ }
+
+ void test_conditionalCall_rhs2() {
+ _assertFalse("null?.b(throw '');");
+ }
+
void test_creation() {
expect(new ExitDetector(), isNotNull);
}
@@ -7618,6 +7690,14 @@
_assertFalse("if (c) return 0; else j++;");
}
+ void test_ifNullAssign() {
+ _assertFalse("a ??= b;");
+ }
+
+ void test_ifNullAssign_rhs() {
+ _assertFalse("a ??= throw '';");
+ }
+
void test_indexExpression() {
_assertFalse("a[b];");
}
diff --git a/pkg/analyzer/test/src/task/dart_test.dart b/pkg/analyzer/test/src/task/dart_test.dart
index b3c6036..95a9ff5 100644
--- a/pkg/analyzer/test/src/task/dart_test.dart
+++ b/pkg/analyzer/test/src/task/dart_test.dart
@@ -22,6 +22,7 @@
import 'package:analyzer/task/model.dart';
import 'package:unittest/unittest.dart';
+import '../../generated/resolver_test.dart';
import '../../generated/test_support.dart';
import '../../reflective_tests.dart';
import '../../utils.dart';
@@ -54,8 +55,9 @@
runReflectiveTests(LibraryUnitErrorsTaskTest);
runReflectiveTests(ParseDartTaskTest);
runReflectiveTests(PartiallyResolveUnitReferencesTaskTest);
+ runReflectiveTests(ResolveFunctionBodiesInUnitTaskTest);
runReflectiveTests(ResolveLibraryTypeNamesTaskTest);
- runReflectiveTests(ResolveUnitReferencesTaskTest);
+// runReflectiveTests(ResolveUnitReferencesTaskTest);
runReflectiveTests(ResolveUnitTypeNamesTaskTest);
runReflectiveTests(ResolveVariableReferencesTaskTest);
runReflectiveTests(ScanDartTaskTest);
@@ -109,10 +111,10 @@
isInstanceOf isParseDartTask = new isInstanceOf<ParseDartTask>();
isInstanceOf isPartiallyResolveUnitReferencesTask =
new isInstanceOf<PartiallyResolveUnitReferencesTask>();
+isInstanceOf isResolveFunctionBodiesInUnitTask =
+ new isInstanceOf<ResolveFunctionBodiesInUnitTask>();
isInstanceOf isResolveLibraryTypeNamesTask =
new isInstanceOf<ResolveLibraryTypeNamesTask>();
-isInstanceOf isResolveUnitReferencesTask =
- new isInstanceOf<ResolveUnitReferencesTask>();
isInstanceOf isResolveUnitTypeNamesTask =
new isInstanceOf<ResolveUnitTypeNamesTask>();
isInstanceOf isResolveVariableReferencesTask =
@@ -125,45 +127,6 @@
Source source;
LibrarySpecificUnit target;
- test_buildInputs() {
- LibrarySpecificUnit target =
- new LibrarySpecificUnit(emptySource, emptySource);
- Map<String, TaskInput> inputs =
- BuildCompilationUnitElementTask.buildInputs(target);
- expect(inputs, isNotNull);
- expect(
- inputs.keys,
- unorderedEquals(
- [BuildCompilationUnitElementTask.PARSED_UNIT_INPUT_NAME]));
- }
-
- test_constructor() {
- BuildCompilationUnitElementTask task =
- new BuildCompilationUnitElementTask(context, emptySource);
- expect(task, isNotNull);
- expect(task.context, context);
- expect(task.target, emptySource);
- }
-
- test_createTask() {
- BuildCompilationUnitElementTask task =
- BuildCompilationUnitElementTask.createTask(context, emptySource);
- expect(task, isNotNull);
- expect(task.context, context);
- expect(task.target, emptySource);
- }
-
- test_description() {
- BuildCompilationUnitElementTask task =
- new BuildCompilationUnitElementTask(null, emptySource);
- expect(task.description, isNotNull);
- }
-
- test_descriptor() {
- TaskDescriptor descriptor = BuildCompilationUnitElementTask.DESCRIPTOR;
- expect(descriptor, isNotNull);
- }
-
test_perform_find_constants() {
_performBuildTask('''
const x = 1;
@@ -242,33 +205,6 @@
@reflectiveTest
class BuildDirectiveElementsTaskTest extends _AbstractDartTaskTest {
- test_constructor() {
- BuildDirectiveElementsTask task =
- new BuildDirectiveElementsTask(context, emptySource);
- expect(task, isNotNull);
- expect(task.context, context);
- expect(task.target, emptySource);
- }
-
- test_createTask() {
- BuildDirectiveElementsTask task =
- BuildDirectiveElementsTask.createTask(context, emptySource);
- expect(task, isNotNull);
- expect(task.context, context);
- expect(task.target, emptySource);
- }
-
- test_description() {
- BuildDirectiveElementsTask task =
- new BuildDirectiveElementsTask(null, emptySource);
- expect(task.description, isNotNull);
- }
-
- test_descriptor() {
- TaskDescriptor descriptor = BuildDirectiveElementsTask.DESCRIPTOR;
- expect(descriptor, isNotNull);
- }
-
test_perform() {
List<Source> sources = newSources({
'/libA.dart': '''
@@ -696,33 +632,6 @@
LibraryElement libraryElement;
- test_constructor() {
- BuildLibraryElementTask task =
- new BuildLibraryElementTask(context, emptySource);
- expect(task, isNotNull);
- expect(task.context, context);
- expect(task.target, emptySource);
- }
-
- test_createTask() {
- BuildLibraryElementTask task =
- BuildLibraryElementTask.createTask(context, emptySource);
- expect(task, isNotNull);
- expect(task.context, context);
- expect(task.target, emptySource);
- }
-
- test_description() {
- BuildLibraryElementTask task =
- new BuildLibraryElementTask(null, emptySource);
- expect(task.description, isNotNull);
- }
-
- test_descriptor() {
- TaskDescriptor descriptor = BuildLibraryElementTask.DESCRIPTOR;
- expect(descriptor, isNotNull);
- }
-
test_perform() {
_performBuildTask({
'/lib.dart': '''
@@ -957,41 +866,6 @@
@reflectiveTest
class BuildPublicNamespaceTaskTest extends _AbstractDartTaskTest {
- test_buildInputs() {
- Map<String, TaskInput> inputs =
- BuildPublicNamespaceTask.buildInputs(emptySource);
- expect(inputs, isNotNull);
- expect(
- inputs.keys, unorderedEquals([BuildPublicNamespaceTask.LIBRARY_INPUT]));
- }
-
- test_constructor() {
- BuildPublicNamespaceTask task =
- new BuildPublicNamespaceTask(context, emptySource);
- expect(task, isNotNull);
- expect(task.context, context);
- expect(task.target, emptySource);
- }
-
- test_createTask() {
- BuildPublicNamespaceTask task =
- BuildPublicNamespaceTask.createTask(context, emptySource);
- expect(task, isNotNull);
- expect(task.context, context);
- expect(task.target, emptySource);
- }
-
- test_description() {
- BuildPublicNamespaceTask task =
- new BuildPublicNamespaceTask(null, emptySource);
- expect(task.description, isNotNull);
- }
-
- test_descriptor() {
- TaskDescriptor descriptor = BuildPublicNamespaceTask.DESCRIPTOR;
- expect(descriptor, isNotNull);
- }
-
test_perform() {
List<Source> sources = newSources({
'/lib.dart': '''
@@ -1574,40 +1448,6 @@
@reflectiveTest
class ContainingLibrariesTaskTest extends _AbstractDartTaskTest {
- test_buildInputs() {
- Map<String, TaskInput> inputs =
- ContainingLibrariesTask.buildInputs(emptySource);
- expect(inputs, isNotNull);
- expect(inputs, isEmpty);
- }
-
- test_constructor() {
- ContainingLibrariesTask task =
- new ContainingLibrariesTask(context, emptySource);
- expect(task, isNotNull);
- expect(task.context, context);
- expect(task.target, emptySource);
- }
-
- test_createTask() {
- ContainingLibrariesTask task =
- ContainingLibrariesTask.createTask(context, emptySource);
- expect(task, isNotNull);
- expect(task.context, context);
- expect(task.target, emptySource);
- }
-
- test_description() {
- ContainingLibrariesTask task =
- new ContainingLibrariesTask(null, emptySource);
- expect(task.description, isNotNull);
- }
-
- test_descriptor() {
- TaskDescriptor descriptor = ContainingLibrariesTask.DESCRIPTOR;
- expect(descriptor, isNotNull);
- }
-
test_perform_definingCompilationUnit() {
AnalysisTarget library = newSource('/test.dart', 'library test;');
computeResult(library, INCLUDED_PARTS);
@@ -1650,44 +1490,6 @@
@reflectiveTest
class DartErrorsTaskTest extends _AbstractDartTaskTest {
- test_buildInputs() {
- Map<String, TaskInput> inputs = DartErrorsTask.buildInputs(emptySource);
- expect(inputs, isNotNull);
- expect(
- inputs.keys,
- unorderedEquals([
- DartErrorsTask.BUILD_DIRECTIVES_ERRORS_INPUT,
- DartErrorsTask.BUILD_LIBRARY_ERRORS_INPUT,
- DartErrorsTask.PARSE_ERRORS_INPUT,
- DartErrorsTask.SCAN_ERRORS_INPUT,
- DartErrorsTask.LIBRARY_UNIT_ERRORS_INPUT
- ]));
- }
-
- test_constructor() {
- DartErrorsTask task = new DartErrorsTask(context, emptySource);
- expect(task, isNotNull);
- expect(task.context, context);
- expect(task.target, emptySource);
- }
-
- test_createTask() {
- DartErrorsTask task = DartErrorsTask.createTask(context, emptySource);
- expect(task, isNotNull);
- expect(task.context, context);
- expect(task.target, emptySource);
- }
-
- test_description() {
- DartErrorsTask task = new DartErrorsTask(null, emptySource);
- expect(task.description, isNotNull);
- }
-
- test_descriptor() {
- TaskDescriptor descriptor = DartErrorsTask.DESCRIPTOR;
- expect(descriptor, isNotNull);
- }
-
test_perform_definingCompilationUnit() {
AnalysisTarget library =
newSource('/test.dart', 'library test; import "dart:math";');
@@ -2204,6 +2006,7 @@
InterfaceType stringType = context.typeProvider.stringType;
expect(topLevel.type, stringType);
expect(field.type, stringType);
+ expect(outputs[INFER_STATIC_VARIABLE_ERRORS], hasLength(0));
}
void test_perform_cycle() {
@@ -2226,6 +2029,23 @@
expect(piFirst.type, context.typeProvider.boolType);
expect(pi.type.isDynamic, isTrue);
expect(tau.type.isDynamic, isTrue);
+ expect(outputs[INFER_STATIC_VARIABLE_ERRORS], hasLength(0));
+ }
+
+ void test_perform_error() {
+ AnalysisTarget source = newSource(
+ '/test.dart',
+ '''
+var a = '' / null;
+''');
+ computeResult(new LibrarySpecificUnit(source, source), RESOLVED_UNIT5);
+ CompilationUnit unit = outputs[RESOLVED_UNIT5];
+ VariableElement a = getTopLevelVariable(unit, 'a').name.staticElement;
+
+ computeResult(a, INFERRED_STATIC_VARIABLE,
+ matcher: isInferStaticVariableTypeTask);
+ expect(a.type.isDynamic, isTrue);
+ expect(outputs[INFER_STATIC_VARIABLE_ERRORS], hasLength(1));
}
void test_perform_null() {
@@ -2241,6 +2061,7 @@
computeResult(a, INFERRED_STATIC_VARIABLE,
matcher: isInferStaticVariableTypeTask);
expect(a.type.isDynamic, isTrue);
+ expect(outputs[INFER_STATIC_VARIABLE_ERRORS], hasLength(0));
}
}
@@ -2280,47 +2101,6 @@
@reflectiveTest
class LibraryUnitErrorsTaskTest extends _AbstractDartTaskTest {
- test_buildInputs() {
- Map<String, TaskInput> inputs = LibraryUnitErrorsTask
- .buildInputs(new LibrarySpecificUnit(emptySource, emptySource));
- expect(inputs, isNotNull);
- expect(
- inputs.keys,
- unorderedEquals([
- LibraryUnitErrorsTask.HINTS_INPUT,
- LibraryUnitErrorsTask.RESOLVE_REFERENCES_ERRORS_INPUT,
- LibraryUnitErrorsTask.RESOLVE_TYPE_NAMES_ERRORS_INPUT,
- LibraryUnitErrorsTask.VARIABLE_REFERENCE_ERRORS_INPUT,
- LibraryUnitErrorsTask.VERIFY_ERRORS_INPUT
- ]));
- }
-
- test_constructor() {
- LibraryUnitErrorsTask task =
- new LibraryUnitErrorsTask(context, emptySource);
- expect(task, isNotNull);
- expect(task.context, context);
- expect(task.target, emptySource);
- }
-
- test_createTask() {
- LibraryUnitErrorsTask task =
- LibraryUnitErrorsTask.createTask(context, emptySource);
- expect(task, isNotNull);
- expect(task.context, context);
- expect(task.target, emptySource);
- }
-
- test_description() {
- LibraryUnitErrorsTask task = new LibraryUnitErrorsTask(null, emptySource);
- expect(task.description, isNotNull);
- }
-
- test_descriptor() {
- TaskDescriptor descriptor = LibraryUnitErrorsTask.DESCRIPTOR;
- expect(descriptor, isNotNull);
- }
-
test_perform_definingCompilationUnit() {
AnalysisTarget library =
newSource('/test.dart', 'library test; import "dart:math";');
@@ -2348,42 +2128,6 @@
class ParseDartTaskTest extends _AbstractDartTaskTest {
Source source;
- test_buildInputs() {
- Map<String, TaskInput> inputs = ParseDartTask.buildInputs(emptySource);
- expect(inputs, isNotNull);
- expect(
- inputs.keys,
- unorderedEquals([
- ParseDartTask.LINE_INFO_INPUT_NAME,
- ParseDartTask.MODIFICATION_TIME_INPUT_NAME,
- ParseDartTask.TOKEN_STREAM_INPUT_NAME
- ]));
- }
-
- test_constructor() {
- ParseDartTask task = new ParseDartTask(context, emptySource);
- expect(task, isNotNull);
- expect(task.context, context);
- expect(task.target, emptySource);
- }
-
- test_createTask() {
- ParseDartTask task = ParseDartTask.createTask(context, emptySource);
- expect(task, isNotNull);
- expect(task.context, context);
- expect(task.target, emptySource);
- }
-
- test_description() {
- ParseDartTask task = new ParseDartTask(null, emptySource);
- expect(task.description, isNotNull);
- }
-
- test_descriptor() {
- TaskDescriptor descriptor = ParseDartTask.DESCRIPTOR;
- expect(descriptor, isNotNull);
- }
-
test_perform() {
_performParseTask(r'''
part of lib;
@@ -2585,6 +2329,42 @@
}
@reflectiveTest
+class ResolveFunctionBodiesInUnitTaskTest extends _AbstractDartTaskTest {
+ void test_perform() {
+ AnalysisTarget source = newSource(
+ '/test.dart',
+ '''
+void f() {
+ var c = new C();
+ c.m();
+}
+class C {
+ void m() {
+ f();
+ }
+}
+''');
+ computeResult(new LibrarySpecificUnit(source, source), RESOLVED_UNIT8,
+ matcher: isResolveFunctionBodiesInUnitTask);
+ CompilationUnit unit = outputs[RESOLVED_UNIT8];
+
+ FunctionDeclaration f = unit.declarations[0];
+ _assertResolved(f.functionExpression.body);
+
+ MethodDeclaration m = (unit.declarations[1] as ClassDeclaration).members[0];
+ _assertResolved(m.body);
+
+ expect(outputs[RESOLVE_FUNCTION_BODIES_ERRORS], hasLength(0));
+ }
+
+ void _assertResolved(FunctionBody body) {
+ ResolutionVerifier verifier = new ResolutionVerifier();
+ body.accept(verifier);
+ verifier.assertResolved();
+ }
+}
+
+@reflectiveTest
class ResolveLibraryTypeNamesTaskTest extends _AbstractDartTaskTest {
test_perform() {
Source sourceLib = newSource(
@@ -2645,92 +2425,6 @@
}
@reflectiveTest
-class ResolveUnitReferencesTaskTest extends _AbstractDartTaskTest {
- test_perform() {
- Source source = newSource(
- '/test.dart',
- '''
-class A {
- m() {}
-}
-main(A a) {
- a.m();
-}
-''');
- LibrarySpecificUnit target = new LibrarySpecificUnit(source, source);
- // prepare unit and "a.m()" invocation
- computeResult(target, RESOLVED_UNIT8, matcher: isResolveUnitReferencesTask);
- CompilationUnit unit = outputs[RESOLVED_UNIT8];
- // walk the AST
- FunctionDeclaration function = unit.declarations[1];
- BlockFunctionBody body = function.functionExpression.body;
- ExpressionStatement statement = body.block.statements[0];
- MethodInvocation invocation = statement.expression;
- expect(unit, same(outputs[RESOLVED_UNIT8]));
- // a.m() is resolved now
- expect(invocation.methodName.staticElement, isNotNull);
- }
-
- test_perform_errors() {
- Source source = newSource(
- '/test.dart',
- '''
-class A {
-}
-main(A a) {
- a.unknownMethod();
-}
-''');
- LibrarySpecificUnit target = new LibrarySpecificUnit(source, source);
- computeResult(target, RESOLVED_UNIT8, matcher: isResolveUnitReferencesTask);
- // validate
- _fillErrorListener(RESOLVE_REFERENCES_ERRORS);
- errorListener.assertErrorsWithCodes(
- <ErrorCode>[StaticTypeWarningCode.UNDEFINED_METHOD]);
- }
-
- test_perform_importExport() {
- newSource(
- '/a.dart',
- '''
-library a;
-class A<T> {
- T m() {}
-}
-''');
- newSource(
- '/b.dart',
- '''
-library b;
-export 'a.dart';
-''');
- Source sourceC = newSource(
- '/c.dart',
- '''
-library c;
-import 'b.dart';
-main() {
- new A<int>().m();
-}
-''');
- computeResult(new LibrarySpecificUnit(sourceC, sourceC), RESOLVED_UNIT8,
- matcher: isResolveUnitReferencesTask);
- // validate
- CompilationUnit unit = outputs[RESOLVED_UNIT8];
- expect(unit, isNotNull);
- FunctionDeclaration functionDeclaration = unit.declarations[0];
- BlockFunctionBody body = functionDeclaration.functionExpression.body;
- List<Statement> statements = body.block.statements;
- ExpressionStatement statement = statements[0];
- MethodInvocation invocation = statement.expression;
- MethodElement methodElement = invocation.methodName.staticElement;
- expect(methodElement, isNotNull);
- expect(methodElement.type, isNotNull);
- expect(methodElement.returnType.toString(), 'int');
- }
-}
-
-@reflectiveTest
class ResolveUnitTypeNamesTaskTest extends _AbstractDartTaskTest {
test_perform() {
Source source = newSource(
@@ -2900,36 +2594,6 @@
@reflectiveTest
class ScanDartTaskTest extends _AbstractDartTaskTest {
- test_buildInputs() {
- Map<String, TaskInput> inputs = ScanDartTask.buildInputs(emptySource);
- expect(inputs, isNotNull);
- expect(inputs.keys, unorderedEquals([ScanDartTask.CONTENT_INPUT_NAME]));
- }
-
- test_constructor() {
- ScanDartTask task = new ScanDartTask(context, emptySource);
- expect(task, isNotNull);
- expect(task.context, context);
- expect(task.target, emptySource);
- }
-
- test_createTask() {
- ScanDartTask task = ScanDartTask.createTask(context, emptySource);
- expect(task, isNotNull);
- expect(task.context, context);
- expect(task.target, emptySource);
- }
-
- test_description() {
- ScanDartTask task = new ScanDartTask(null, emptySource);
- expect(task.description, isNotNull);
- }
-
- test_descriptor() {
- TaskDescriptor descriptor = ScanDartTask.DESCRIPTOR;
- expect(descriptor, isNotNull);
- }
-
test_perform_errors() {
_performScanTask('import "');
expect(outputs, hasLength(3));
diff --git a/pkg/compiler/lib/src/common/backend_api.dart b/pkg/compiler/lib/src/common/backend_api.dart
index e715f29..42ad090 100644
--- a/pkg/compiler/lib/src/common/backend_api.dart
+++ b/pkg/compiler/lib/src/common/backend_api.dart
@@ -343,6 +343,9 @@
/// times, but [onQueueClosed] is only called once.
void onQueueClosed() {}
+ /// Called when the compiler starts running the codegen enqueuer.
+ void onCodegenStart() {}
+
/// Called after [element] has been resolved.
// TODO(johnniwinther): Change [TreeElements] to [Registry] or a dependency
// node. [elements] is currently unused by the implementation.
diff --git a/pkg/compiler/lib/src/common/names.dart b/pkg/compiler/lib/src/common/names.dart
index 967e873..7051ffe 100644
--- a/pkg/compiler/lib/src/common/names.dart
+++ b/pkg/compiler/lib/src/common/names.dart
@@ -87,6 +87,8 @@
static final Selector compareTo =
new Selector.call(const PublicName("compareTo"), CallStructure.ONE_ARG);
+
+ static final Selector equals = new Selector.binaryOperator('==');
}
/// [Uri]s commonly used.
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index e04c88a..db180aa 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -1153,6 +1153,7 @@
log('Compiling...');
phase = PHASE_COMPILING;
+ backend.onCodegenStart();
// TODO(johnniwinther): Move these to [CodegenEnqueuer].
if (hasIsolateSupport) {
backend.enableIsolateSupport(enqueuer.codegen);
diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart
index 712e5cd..8ad289f 100644
--- a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart
+++ b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart
@@ -777,10 +777,6 @@
ast.Send node,
MethodElement function,
_) {
- // TODO(karlklose): support foreign functions.
- if (compiler.backend.isForeign(function)) {
- return giveup(node, 'handleStaticFunctionGet: foreign: $function');
- }
return irBuilder.buildStaticFunctionGet(function);
}
@@ -1183,9 +1179,6 @@
ast.NodeList arguments,
CallStructure callStructure,
_) {
- if (compiler.backend.isForeign(getter)) {
- return giveup(node, 'handleStaticGetterInvoke: foreign: $getter');
- }
ir.Primitive target = irBuilder.buildStaticGetterGet(
getter, sourceInformationBuilder.buildGet(node));
return irBuilder.buildCallInvocation(target,
@@ -2261,12 +2254,12 @@
}
internalError(ast.Node node, String message) {
- giveup(node, message);
+ compiler.internalError(node, message);
}
@override
visitNode(ast.Node node) {
- internalError(node, "Unhandled node");
+ giveup(node, "Unhandled node");
}
dynamic giveup(ast.Node node, [String reason]) {
diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart
index b685c45..d93de7b 100644
--- a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart
+++ b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart
@@ -3,7 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
library dart2js.ir_nodes;
-import '../constants/values.dart' as values show ConstantValue;
+import 'dart:collection';
+import '../constants/values.dart' as values;
import '../dart_types.dart' show DartType, InterfaceType, TypeVariableType;
import '../elements/elements.dart';
import '../io/source_information.dart' show SourceInformation;
@@ -119,6 +120,41 @@
}
}
+class EffectiveUseIterator extends Iterator<Reference<Primitive>> {
+ Reference<Primitive> current;
+ Reference<Primitive> next;
+ final List<Refinement> stack = <Refinement>[];
+
+ EffectiveUseIterator(Primitive prim) : next = prim.firstRef;
+
+ bool moveNext() {
+ Reference<Primitive> ref = next;
+ while (true) {
+ if (ref == null) {
+ if (stack.isNotEmpty) {
+ ref = stack.removeLast().firstRef;
+ } else {
+ current = null;
+ return false;
+ }
+ } else if (ref.parent is Refinement) {
+ stack.add(ref.parent);
+ ref = ref.next;
+ } else {
+ current = ref;
+ next = current.next;
+ return true;
+ }
+ }
+ }
+}
+
+class EffectiveUseIterable extends IterableBase<Reference<Primitive>> {
+ Primitive primitive;
+ EffectiveUseIterable(this.primitive);
+ EffectiveUseIterator get iterator => new EffectiveUseIterator(primitive);
+}
+
/// A named value.
///
/// The identity of the [Primitive] object is the name of the value.
@@ -153,6 +189,34 @@
/// The source information associated with this primitive.
// TODO(johnniwinther): Require source information for all primitives.
SourceInformation get sourceInformation => null;
+
+ /// If this is a [Refinement] node, returns the value being refined.
+ Primitive get effectiveDefinition => this;
+
+ /// True if the two primitives are (refinements of) the same value.
+ bool sameValue(Primitive other) {
+ return effectiveDefinition == other.effectiveDefinition;
+ }
+
+ /// Iterates all non-refinement uses of the primitive and all uses of
+ /// a [Refinement] of this primitive (transitively).
+ ///
+ /// Notes regarding concurrent modification:
+ /// - The current reference may safely be unlinked.
+ /// - Yet unvisited references may not be unlinked.
+ /// - References to this primitive created during iteration will not be seen.
+ /// - References to a refinement of this primitive may not be created during
+ /// iteration.
+ EffectiveUseIterable get effectiveUses => new EffectiveUseIterable(this);
+
+ bool get hasMultipleEffectiveUses {
+ Iterator it = effectiveUses.iterator;
+ return it.moveNext() && it.moveNext();
+ }
+
+ bool get hasNoEffectiveUses {
+ return effectiveUses.isEmpty;
+ }
}
/// Operands to invocations and primitives are always variables. They point to
@@ -439,6 +503,28 @@
accept(Visitor visitor) => visitor.visitInvokeConstructor(this);
}
+/// An alias for [value] in a context where the value is known to satisfy
+/// [type].
+///
+/// Refinement nodes are inserted before the type propagator pass and removed
+/// afterwards, so as not to complicate passes that don't reason about types,
+/// but need to reason about value references being identical (i.e. referring
+/// to the same primitive).
+class Refinement extends Primitive {
+ Reference<Primitive> value;
+ final TypeMask type;
+
+ Refinement(Primitive value, this.type)
+ : value = new Reference<Primitive>(value);
+
+ bool get isSafeForElimination => true;
+ bool get isSafeForReordering => false;
+
+ accept(Visitor visitor) => visitor.visitRefinement(this);
+
+ Primitive get effectiveDefinition => value.definition.effectiveDefinition;
+}
+
/// An "is" type test.
///
/// Returns `true` if [value] is an instance of [type].
@@ -883,6 +969,18 @@
final Set<ClassElement> interceptedClasses = new Set<ClassElement>();
final SourceInformation sourceInformation;
+ /// If non-null, all uses of this the interceptor call are guaranteed to
+ /// see this value.
+ ///
+ /// The interceptor call is not immediately replaced by the constant, because
+ /// that might prevent the interceptor from being shared.
+ ///
+ /// The precise input type is not known when sharing interceptors, because
+ /// refinement nodes have been removed by then. So this field carries the
+ /// known constant until we know if it should be shared or replaced by
+ /// the constant.
+ values.InterceptorConstantValue constantValue;
+
Interceptor(Primitive input, this.sourceInformation)
: this.input = new Reference<Primitive>(input);
@@ -1201,6 +1299,7 @@
T visitGetLength(GetLength node);
T visitGetIndex(GetIndex node);
T visitSetIndex(SetIndex node);
+ T visitRefinement(Refinement node);
// Support for literal foreign code.
T visitForeignCode(ForeignCode node);
@@ -1504,6 +1603,12 @@
processReference(node.index);
processReference(node.value);
}
+
+ processRefinement(Refinement node) {}
+ visitRefinement(Refinement node) {
+ processRefinement(node);
+ processReference(node.value);
+ }
}
typedef void StackAction();
diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes_sexpr.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes_sexpr.dart
index 988ee69..1131ce8 100644
--- a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes_sexpr.dart
+++ b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes_sexpr.dart
@@ -361,6 +361,12 @@
String continuation = access(node.continuation);
return '(Await $value $continuation)';
}
+
+ @override
+ String visitRefinement(Refinement node) {
+ String value = access(node.value);
+ return '(Refinement $value ${node.type})';
+ }
}
class ConstantStringifier extends ConstantValueVisitor<String, Null> {
diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_tracer.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_tracer.dart
index ecbe343..6b56330 100644
--- a/pkg/compiler/lib/src/cps_ir/cps_ir_tracer.dart
+++ b/pkg/compiler/lib/src/cps_ir/cps_ir_tracer.dart
@@ -393,6 +393,12 @@
String continuation = formatReference(node.continuation);
return 'Await $value $continuation';
}
+
+ @override
+ visitRefinement(cps_ir.Refinement node) {
+ String value = formatReference(node.value);
+ return 'Refinement $value ${node.type}';
+ }
}
/**
@@ -669,4 +675,8 @@
visitAwait(cps_ir.Await node) {
unexpectedNode(node);
}
+
+ visitRefinement(cps_ir.Refinement node) {
+ unexpectedNode(node);
+ }
}
diff --git a/pkg/compiler/lib/src/cps_ir/insert_refinements.dart b/pkg/compiler/lib/src/cps_ir/insert_refinements.dart
new file mode 100644
index 0000000..03cafdf
--- /dev/null
+++ b/pkg/compiler/lib/src/cps_ir/insert_refinements.dart
@@ -0,0 +1,243 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for 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 cps_ir.optimization.insert_refinements;
+
+import 'optimizers.dart' show Pass;
+import 'shrinking_reductions.dart' show ParentVisitor;
+import 'cps_ir_nodes.dart';
+import '../types/types.dart';
+import '../types/constants.dart';
+import '../constants/values.dart';
+import '../world.dart';
+import '../common/names.dart';
+import '../universe/universe.dart';
+import '../elements/elements.dart';
+
+/// Inserts [Refinement] nodes in the IR to allow for sparse path-sensitive
+/// type analysis in the [TypePropagator] pass.
+///
+/// Refinement nodes are inserted at the arms of a [Branch] node with a
+/// condition of form `x is T` or `x == null`.
+///
+/// Refinement nodes are inserted after a method invocation to refine the
+/// receiver to the types that can respond to the given selector.
+class InsertRefinements extends RecursiveVisitor implements Pass {
+ String get passName => 'Insert refinement nodes';
+
+ final TypesTask types;
+ final World world;
+ final TypeMask nonNullType;
+ final TypeMask nullType;
+
+ /// Maps unrefined primitives to its refinement currently in scope (if any).
+ final Map<Primitive, Refinement> refinementFor = <Primitive, Refinement>{};
+
+ InsertRefinements(this.types, World world)
+ : this.world = world,
+ nonNullType = new TypeMask.nonNullSubtype(world.objectClass, world),
+ nullType = new TypeMask.empty();
+
+ void rewrite(FunctionDefinition node) {
+ new ParentVisitor().visit(node);
+ visit(node.body);
+ }
+
+ /// Updates references to refer to the refinement currently in scope.
+ void processReference(Reference node) {
+ Refinement refined = refinementFor[node.definition];
+ if (refined != null) {
+ node.changeTo(refined);
+ }
+ }
+
+ /// Sinks the binding of [cont] to immediately above [use].
+ ///
+ /// This is used to ensure that everything in scope at [use] is also in scope
+ /// inside [cont], so refinements can be inserted inside [cont] without
+ /// accidentally referencing a primitive out of scope.
+ ///
+ /// It is always safe to do this for single-use continuations, because
+ /// strictly more things are in scope at the use site, and there can't be any
+ /// other use of [cont] that might fall out of scope since there is only
+ /// that single use.
+ void sinkContinuationToUse(Continuation cont, Expression use) {
+ assert(cont.hasExactlyOneUse && cont.firstRef.parent == use);
+ assert(!cont.isRecursive);
+ LetCont let = cont.parent;
+ InteriorNode useParent = use.parent;
+ if (useParent == let) return;
+ if (let.continuations.length > 1) {
+ // Create a new LetCont binding only this continuation.
+ let.continuations.remove(cont);
+ let = new LetCont(cont, null);
+ cont.parent = let;
+ } else {
+ // Remove LetCont from current position.
+ InteriorNode letParent = let.parent;
+ letParent.body = let.body;
+ let.body.parent = letParent;
+ }
+
+ // Insert LetCont before use.
+ useParent.body = let;
+ let.body = use;
+ use.parent = let;
+ let.parent = useParent;
+ }
+
+ Primitive unfoldInterceptor(Primitive prim) {
+ return prim is Interceptor ? prim.input.definition : prim;
+ }
+
+ /// Enqueues [cont] for processing in a context where [refined] is the
+ /// current refinement for its value.
+ void pushRefinement(Continuation cont, Refinement refined) {
+ Primitive value = refined.effectiveDefinition;
+ Primitive currentRefinement = refinementFor[value];
+ pushAction(() {
+ refinementFor[value] = currentRefinement;
+ if (refined.hasNoUses) {
+ // Clean up refinements that are not used.
+ refined.value.unlink();
+ } else {
+ cont.body = new LetPrim(refined, cont.body);
+ refined.parent = cont.body;
+ refined.value.parent = refined;
+ }
+ });
+ push(cont);
+ pushAction(() {
+ refinementFor[value] = refined;
+ });
+ }
+
+ void visitInvokeMethod(InvokeMethod node) {
+ Continuation cont = node.continuation.definition;
+
+ // Update references to their current refined values.
+ processReference(node.receiver);
+ node.arguments.forEach(processReference);
+
+ // If the call is intercepted, we want to refine the actual receiver,
+ // not the interceptor.
+ Primitive receiver = unfoldInterceptor(node.receiver.definition);
+
+ // Sink the continuation to the call to ensure everything in scope
+ // here is also in scope inside the continuations.
+ sinkContinuationToUse(cont, node);
+
+ if (node.selector.isClosureCall) {
+ // Do not try to refine the receiver of closure calls; the class world
+ // does not know about closure classes.
+ push(cont);
+ } else {
+ // Filter away receivers that throw on this selector.
+ TypeMask type = world.allFunctions.receiverType(node.selector, node.mask);
+ pushRefinement(cont, new Refinement(receiver, type));
+ }
+ }
+
+ CallExpression getCallWithResult(Primitive prim) {
+ if (prim is Parameter && prim.parent is Continuation) {
+ Continuation cont = prim.parent;
+ if (cont.hasExactlyOneUse && cont.firstRef.parent is CallExpression) {
+ return cont.firstRef.parent;
+ }
+ }
+ return null;
+ }
+
+ bool isTrue(Primitive prim) {
+ return prim is Constant && prim.value.isTrue;
+ }
+
+ TypeMask getTypeOf(ConstantValue constant) {
+ return computeTypeMask(types.compiler, constant);
+ }
+
+ void visitBranch(Branch node) {
+ processReference(node.condition);
+ Primitive condition = node.condition.definition;
+ CallExpression call = getCallWithResult(condition);
+
+ Continuation trueCont = node.trueContinuation.definition;
+ Continuation falseCont = node.falseContinuation.definition;
+
+ // Sink both continuations to the Branch to ensure everything in scope
+ // here is also in scope inside the continuations.
+ sinkContinuationToUse(trueCont, node);
+ sinkContinuationToUse(falseCont, node);
+
+ // If the condition is an 'is' check, promote the checked value.
+ if (condition is TypeTest && condition.type.element is ClassElement) {
+ Primitive value = condition.value.definition;
+ ClassElement classElement = condition.type.element;
+ TypeMask type = new TypeMask.nonNullSubtype(classElement, world);
+ Primitive refinedValue = new Refinement(value, type);
+ pushRefinement(trueCont, refinedValue);
+ push(falseCont);
+ return;
+ }
+
+ // If the condition is comparison with a constant, promote the other value.
+ // This can happen either for calls to `==` or `identical` calls, such
+ // as the ones inserted by the unsugaring pass.
+
+ void refineEquality(Primitive first,
+ Primitive second,
+ Continuation trueCont,
+ Continuation falseCont) {
+ if (second is Constant && second.value.isNull) {
+ Refinement refinedTrue = new Refinement(first, nullType);
+ Refinement refinedFalse = new Refinement(first, nonNullType);
+ pushRefinement(trueCont, refinedTrue);
+ pushRefinement(falseCont, refinedFalse);
+ } else if (first is Constant && first.value.isNull) {
+ Refinement refinedTrue = new Refinement(second, nullType);
+ Refinement refinedFalse = new Refinement(second, nonNullType);
+ pushRefinement(trueCont, refinedTrue);
+ pushRefinement(falseCont, refinedFalse);
+ } else {
+ push(trueCont);
+ push(falseCont);
+ }
+ }
+
+ if (call is InvokeMethod && call.selector == Selectors.equals) {
+ refineEquality(call.arguments[0].definition,
+ call.arguments[1].definition,
+ trueCont,
+ falseCont);
+ return;
+ }
+
+ if (condition is ApplyBuiltinOperator &&
+ condition.operator == BuiltinOperator.Identical) {
+ refineEquality(condition.arguments[0].definition,
+ condition.arguments[1].definition,
+ trueCont,
+ falseCont);
+ return;
+ }
+
+ push(trueCont);
+ push(falseCont);
+ }
+
+ @override
+ Expression traverseLetCont(LetCont node) {
+ for (Continuation cont in node.continuations) {
+ if (cont.hasExactlyOneUse &&
+ (cont.firstRef.parent is InvokeMethod ||
+ cont.firstRef.parent is Branch)) {
+ // Do not push the continuation here.
+ // visitInvokeMethod and visitBranch will do that.
+ } else {
+ push(cont);
+ }
+ }
+ return node.body;
+ }
+}
diff --git a/pkg/compiler/lib/src/cps_ir/let_sinking.dart b/pkg/compiler/lib/src/cps_ir/let_sinking.dart
deleted file mode 100644
index 3b46d9a..0000000
--- a/pkg/compiler/lib/src/cps_ir/let_sinking.dart
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
-// for 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 dart2js.cps_ir.let_sinking;
-
-import 'cps_ir_nodes.dart';
-import 'optimizers.dart';
-import 'loop_hierarchy.dart';
-
-/// Sinks single-use primitives to the use when this is safe and profitable.
-///
-/// To avoid sinking non-constant primitives into loops, this pass performs a
-/// control-flow analysis to determine the effective nesting of loops.
-///
-/// In the example below, the value 'p' can be sunk to its use site in the
-/// 'else' branch because that branch is not effectively part of a loop,
-/// despite being lexically nested in a recursive continuation.
-///
-/// let prim p = getInterceptor(<something>)
-/// let rec kont x =
-/// if (<loop condition>)
-/// <loop body>
-/// InvokeContinuation kont x'
-/// else
-/// <after loop>
-/// return p.foo()
-///
-class LetSinker extends RecursiveVisitor implements Pass {
- String get passName => 'Let sinking';
-
- LoopHierarchy loopHierarchy;
- List<Continuation> stack = <Continuation>[];
-
- /// Maps a sinkable primitive to its loop header.
- Map<Primitive, Continuation> loopHeaderForPrimitive =
- <Primitive, Continuation>{};
-
- Continuation currentLoopHeader;
-
- void rewrite(FunctionDefinition node) {
- new ParentVisitor().visit(node);
- loopHierarchy = new LoopHierarchy(node);
- visit(node.body);
- }
-
- @override
- Expression traverseLetPrim(LetPrim node) {
- Primitive prim = node.primitive;
- if (prim.hasExactlyOneUse && prim.isSafeForReordering) {
- // This can potentially be sunk. Register the loop header, so when we
- // find the use site, we can check if they are in the same loop.
- loopHeaderForPrimitive[prim] = currentLoopHeader;
- pushAction(() {
- if (node.primitive != null) {
- // The primitive could not be sunk. Try to sink dependencies here.
- visit(node.primitive);
- } else {
- // The primitive was sunk. Destroy the old LetPrim.
- InteriorNode parent = node.parent;
- parent.body = node.body;
- node.body.parent = parent;
- }
- });
- } else {
- visit(node.primitive);
- }
-
- // Visit the body, wherein this primitive may be sunk to its use site.
- return node.body;
- }
-
- @override
- Expression traverseContinuation(Continuation cont) {
- Continuation oldLoopHeader = currentLoopHeader;
- pushAction(() {
- currentLoopHeader = oldLoopHeader;
- });
- currentLoopHeader = loopHierarchy.getLoopHeader(cont);
- return cont.body;
- }
-
- void processReference(Reference ref) {
- Definition definition = ref.definition;
- if (definition is Primitive &&
- definition is! Parameter &&
- definition.hasExactlyOneUse &&
- definition.isSafeForReordering) {
- // Check if use is in the same loop.
- Continuation bindingLoop = loopHeaderForPrimitive.remove(definition);
- if (bindingLoop == currentLoopHeader || definition is Constant) {
- // Sink the definition.
-
- Expression use = getEnclosingExpression(ref.parent);
- LetPrim binding = definition.parent;
- binding.primitive = null; // Mark old binding for deletion.
- LetPrim newBinding = new LetPrim(definition);
- definition.parent = newBinding;
- InteriorNode useParent = use.parent;
- useParent.body = newBinding;
- newBinding.body = use;
- use.parent = newBinding;
- newBinding.parent = useParent;
-
- // Now that the final binding location has been found, sink the
- // dependencies of the definition down here as well.
- visit(definition);
- }
- }
- }
-
- Expression getEnclosingExpression(Node node) {
- while (node is! Expression) {
- node = node.parent;
- }
- return node;
- }
-}
-
diff --git a/pkg/compiler/lib/src/cps_ir/optimizers.dart b/pkg/compiler/lib/src/cps_ir/optimizers.dart
index 11c9fae..d10823a 100644
--- a/pkg/compiler/lib/src/cps_ir/optimizers.dart
+++ b/pkg/compiler/lib/src/cps_ir/optimizers.dart
@@ -13,7 +13,8 @@
export 'redundant_join.dart' show RedundantJoinEliminator;
export 'shrinking_reductions.dart' show ShrinkingReducer, ParentVisitor;
export 'mutable_ssa.dart' show MutableVariableEliminator;
-export 'let_sinking.dart' show LetSinker;
+export 'insert_refinements.dart' show InsertRefinements;
+export 'remove_refinements.dart' show RemoveRefinements;
export 'loop_invariant_code_motion.dart' show LoopInvariantCodeMotion;
export 'share_interceptors.dart' show ShareInterceptors;
diff --git a/pkg/compiler/lib/src/cps_ir/remove_refinements.dart b/pkg/compiler/lib/src/cps_ir/remove_refinements.dart
new file mode 100644
index 0000000..f5088ae2
--- /dev/null
+++ b/pkg/compiler/lib/src/cps_ir/remove_refinements.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for 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 cps_ir.optimization.remove_refinements;
+
+import 'optimizers.dart' show Pass;
+import 'shrinking_reductions.dart' show ParentVisitor;
+import 'cps_ir_nodes.dart';
+
+/// Removes all [Refinement] nodes from the IR.
+///
+/// This simplifies subsequent passes that don't rely on path-sensitive
+/// type information but depend on equality between primitives.
+class RemoveRefinements extends RecursiveVisitor implements Pass {
+ String get passName => 'Remove refinement nodes';
+
+ void rewrite(FunctionDefinition node) {
+ new ParentVisitor().visit(node);
+ visit(node);
+ }
+
+ @override
+ Expression traverseLetPrim(LetPrim node) {
+ if (node.primitive is Refinement) {
+ Refinement refinement = node.primitive;
+ refinement.value.definition.substituteFor(refinement);
+ refinement.value.unlink();
+ InteriorNode parent = node.parent;
+ parent.body = node.body;
+ node.body.parent = parent;
+ }
+ return node.body;
+ }
+}
diff --git a/pkg/compiler/lib/src/cps_ir/scalar_replacement.dart b/pkg/compiler/lib/src/cps_ir/scalar_replacement.dart
index 114d8a0..81df956 100644
--- a/pkg/compiler/lib/src/cps_ir/scalar_replacement.dart
+++ b/pkg/compiler/lib/src/cps_ir/scalar_replacement.dart
@@ -1,6 +1,7 @@
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for 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 dart2js.cps_ir.scalar_replacement;
import 'optimizers.dart';
diff --git a/pkg/compiler/lib/src/cps_ir/share_interceptors.dart b/pkg/compiler/lib/src/cps_ir/share_interceptors.dart
index 7b360d4..27afeb5 100644
--- a/pkg/compiler/lib/src/cps_ir/share_interceptors.dart
+++ b/pkg/compiler/lib/src/cps_ir/share_interceptors.dart
@@ -6,6 +6,7 @@
import 'cps_ir_nodes.dart';
import 'optimizers.dart';
+import '../constants/values.dart';
/// Merges calls to `getInterceptor` when one call dominates the other.
///
@@ -14,8 +15,11 @@
class ShareInterceptors extends RecursiveVisitor implements Pass {
String get passName => 'Share interceptors';
- final Map<Primitive, Interceptor> interceptorFor =
- <Primitive, Interceptor>{};
+ final Map<Primitive, Primitive> interceptorFor =
+ <Primitive, Primitive>{};
+
+ final Map<ConstantValue, Primitive> sharedConstantFor =
+ <ConstantValue, Primitive>{};
void rewrite(FunctionDefinition node) {
visit(node.body);
@@ -26,10 +30,49 @@
if (node.primitive is Interceptor) {
Interceptor interceptor = node.primitive;
Primitive input = interceptor.input.definition;
- Interceptor existing = interceptorFor[input];
+ Primitive existing = interceptorFor[input];
if (existing != null) {
- existing.interceptedClasses.addAll(interceptor.interceptedClasses);
+ if (existing is Interceptor) {
+ existing.interceptedClasses.addAll(interceptor.interceptedClasses);
+ }
existing.substituteFor(interceptor);
+ } else if (interceptor.constantValue != null) {
+ InterceptorConstantValue value = interceptor.constantValue;
+ // There is no interceptor obtained from this particular input, but
+ // there might one obtained from another input that is known to
+ // have the same result, so try to reuse that.
+ Primitive shared = sharedConstantFor[value];
+ if (shared != null) {
+ shared.substituteFor(interceptor);
+ } else {
+ Constant constant = new Constant(value);
+ constant.hint = interceptor.hint;
+ node.primitive = constant;
+ constant.parent = node;
+ interceptor.input.unlink();
+ constant.substituteFor(interceptor);
+ interceptorFor[input] = constant;
+ sharedConstantFor[value] = constant;
+ pushAction(() {
+ interceptorFor.remove(input);
+ sharedConstantFor.remove(value);
+
+ if (constant.hasExactlyOneUse) {
+ // As a heuristic, always sink single-use interceptor constants
+ // to their use, even if it is inside a loop.
+ Expression use = getEnclosingExpression(constant.firstRef.parent);
+ InteriorNode parent = node.parent;
+ parent.body = node.body;
+ node.body.parent = parent;
+
+ InteriorNode useParent = use.parent;
+ useParent.body = node;
+ node.body = use;
+ use.parent = node;
+ node.parent = useParent;
+ }
+ });
+ }
} else {
interceptorFor[input] = interceptor;
pushAction(() {
@@ -39,4 +82,11 @@
}
return node.body;
}
+
+ Expression getEnclosingExpression(Node node) {
+ while (node is! Expression) {
+ node = node.parent;
+ }
+ return node;
+ }
}
diff --git a/pkg/compiler/lib/src/cps_ir/shrinking_reductions.dart b/pkg/compiler/lib/src/cps_ir/shrinking_reductions.dart
index 011af0f..0e61782 100644
--- a/pkg/compiler/lib/src/cps_ir/shrinking_reductions.dart
+++ b/pkg/compiler/lib/src/cps_ir/shrinking_reductions.dart
@@ -693,6 +693,10 @@
node.continuation.parent = node;
node.input.parent = node;
}
+
+ processRefinement(Refinement node) {
+ node.value.parent = node;
+ }
}
class _ReductionKind {
diff --git a/pkg/compiler/lib/src/cps_ir/type_propagation.dart b/pkg/compiler/lib/src/cps_ir/type_propagation.dart
index 3212f94..5ef491b 100644
--- a/pkg/compiler/lib/src/cps_ir/type_propagation.dart
+++ b/pkg/compiler/lib/src/cps_ir/type_propagation.dart
@@ -1,6 +1,7 @@
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+library dart2js.cps_ir.type_propagation;
import 'optimizers.dart';
@@ -700,6 +701,7 @@
void visitLetPrim(LetPrim node) {
AbstractValue value = getValue(node.primitive);
if (node.primitive is! Constant &&
+ node.primitive is! Refinement &&
node.primitive.isSafeForElimination &&
value.isConstant) {
// If the value is a known constant, compile it as a constant.
@@ -1132,16 +1134,17 @@
/// This is a short-term solution to avoid inserting a lot of bounds checks,
/// since there is currently no optimization for eliminating them.
bool hasTooManyIndexAccesses(Primitive receiver) {
+ receiver = receiver.effectiveDefinition;
int count = 0;
- for (Reference ref = receiver.firstRef; ref != null; ref = ref.next) {
+ for (Reference ref in receiver.effectiveUses) {
Node use = ref.parent;
if (use is InvokeMethod &&
(use.selector.isIndex || use.selector.isIndexSet) &&
- getDartReceiver(use) == receiver) {
+ getDartReceiver(use).sameValue(receiver)) {
++count;
- } else if (use is GetIndex && use.object.definition == receiver) {
+ } else if (use is GetIndex && use.object.definition.sameValue(receiver)) {
++count;
- } else if (use is SetIndex && use.object.definition == receiver) {
+ } else if (use is SetIndex && use.object.definition.sameValue(receiver)) {
++count;
}
if (count > 2) return true;
@@ -1322,7 +1325,7 @@
// Check that all uses of the iterator are 'moveNext' and 'current'.
assert(!isInterceptedSelector(Selectors.moveNext));
assert(!isInterceptedSelector(Selectors.current));
- for (Reference ref = iterator.firstRef; ref != null; ref = ref.next) {
+ for (Reference ref in iterator.effectiveUses) {
if (ref.parent is! InvokeMethod) return false;
InvokeMethod use = ref.parent;
if (ref != use.receiver) return false;
@@ -1339,8 +1342,8 @@
MutableVariable current = new MutableVariable(new LoopItemEntity());
// Rewrite all uses of the iterator.
- while (iterator.firstRef != null) {
- InvokeMethod use = iterator.firstRef.parent;
+ for (Reference ref in iterator.effectiveUses) {
+ InvokeMethod use = ref.parent;
Continuation useCont = use.continuation.definition;
if (use.selector == Selectors.current) {
// Rewrite iterator.current to a use of the 'current' variable.
@@ -1446,6 +1449,9 @@
}
}
+ // All effective uses have been rewritten.
+ destroyRefinementsOfDeadPrimitive(iterator);
+
// Rewrite the iterator call to initializers for 'index' and 'current'.
CpsFragment cps = new CpsFragment();
cps.letMutable(index, cps.makeZero());
@@ -1488,7 +1494,8 @@
while (true) {
Node parent = node.parent;
if (parent is LetCont ||
- parent is LetPrim && parent.primitive.isSafeForReordering) {
+ parent is LetPrim && parent.primitive.isSafeForReordering ||
+ parent is LetPrim && parent.primitive is Refinement) {
node = parent;
} else {
return parent;
@@ -1510,7 +1517,7 @@
assert(!isInterceptedSelector(call));
assert(call.argumentCount == node.arguments.length);
- Primitive tearOff = node.receiver.definition;
+ Primitive tearOff = node.receiver.definition.effectiveDefinition;
// Note: We don't know if [tearOff] is actually a tear-off.
// We name variables based on the pattern we are trying to match.
@@ -1545,9 +1552,6 @@
Continuation getterCont = tearOffInvoke.continuation.definition;
- // TODO(asgerf): Support torn-off intercepted methods.
- if (isInterceptedSelector(getter)) return false;
-
Primitive object = tearOffInvoke.receiver.definition;
// Ensure that the object actually has a foo member, since we might
@@ -1561,7 +1565,7 @@
// If there are multiple uses, we cannot eliminate the getter call and
// therefore risk duplicating its side effects.
- if (!isPure && tearOff.hasMultipleUses) return false;
+ if (!isPure && tearOff.hasMultipleEffectiveUses) return false;
// If the getter call is impure, we risk reordering side effects.
if (!isPure && getEffectiveParent(node) != getterCont) {
@@ -1578,10 +1582,11 @@
node.receiver.unlink();
replaceSubtree(node, invoke, unlink: false);
- if (tearOff.hasNoUses) {
+ if (tearOff.hasNoEffectiveUses) {
// Eliminate the getter call if it has no more uses.
// This cannot be delegated to other optimizations because we need to
// avoid duplication of side effects.
+ destroyRefinementsOfDeadPrimitive(tearOff);
getterCont.parameters.clear();
replaceSubtree(tearOffInvoke, new InvokeContinuation(getterCont, []));
} else {
@@ -1597,6 +1602,18 @@
return false;
}
+ void destroyRefinementsOfDeadPrimitive(Primitive prim) {
+ while (prim.firstRef != null) {
+ Refinement refine = prim.firstRef.parent;
+ destroyRefinementsOfDeadPrimitive(refine);
+ LetPrim letPrim = refine.parent;
+ InteriorNode parent = letPrim.parent;
+ parent.body = letPrim.body;
+ letPrim.body.parent = parent;
+ prim.firstRef.unlink();
+ }
+ }
+
/// Inlines a single-use closure if it leaves the closure object with only
/// field accesses. This is optimized later by [ScalarReplacer].
bool specializeSingleUseClosureCall(InvokeMethod node) {
@@ -1628,6 +1645,11 @@
Selector targetSelector = new Selector.fromElement(functionElement);
if (call.callStructure != targetSelector.callStructure) return false;
+ // Don't inline if [target] contains try-catch or try-finally. JavaScript
+ // engines typically do poor optimization of the entire function containing
+ // the 'try'.
+ if (functionElement.resolvedAst.elements.containsTryStatement) return false;
+
FunctionDefinition target =
functionCompiler.compileToCpsIR(functionElement);
@@ -1645,11 +1667,6 @@
return false;
}
- // Don't inline if [target] contains try-catch or try-finally. JavaScript
- // engines typically do poor optimization of the entire function containing
- // the 'try'.
- if (ContainsTry.analyze(target)) return false;
-
node.receiver.definition.substituteFor(target.thisParameter);
for (int i = 0; i < node.arguments.length; ++i) {
node.arguments[i].definition.substituteFor(target.parameters[i]);
@@ -1692,7 +1709,7 @@
node.receiverIsNotNull = receiver.isDefinitelyNotNull;
if (isInterceptedSelector(node.selector) &&
- node.receiver.definition == node.arguments[0].definition) {
+ node.receiver.definition.sameValue(node.arguments[0].definition)) {
// The receiver and first argument are the same; that means we already
// determined in visitInterceptor that we are targeting a non-interceptor.
@@ -1833,9 +1850,8 @@
ast.Statement body = node.target.node.body;
bool shouldInline() {
- if (backend.annotations.noInline(node.target)) {
- return false;
- }
+ if (backend.annotations.noInline(node.target)) return false;
+ if (node.target.resolvedAst.elements.containsTryStatement) return false;
// Inline functions that are a single return statement, expression
// statement, or block containing a return statement or expression
@@ -1986,7 +2002,7 @@
if (parent is LetPrim && parent.primitive is ApplyBuiltinMethod) {
ApplyBuiltinMethod previous = parent.primitive;
if (previous.method == BuiltinMethod.Push &&
- previous.receiver.definition == node.receiver.definition) {
+ previous.receiver.definition.sameValue(node.receiver.definition)) {
// We found two consecutive pushes.
// Move all arguments from the first push onto the second one.
List<Reference<Primitive>> arguments = previous.arguments;
@@ -2063,16 +2079,9 @@
} else {
singleClass = typeSystem.singleClass(value.type);
}
- if (singleClass != null) {
- if (singleClass.isSubclassOf(backend.jsInterceptorClass)) {
- Primitive constant = makeConstantPrimitive(
- new InterceptorConstantValue(singleClass.rawType));
- constant.hint = node.hint;
- return constant;
- } else {
- node.input.definition.substituteFor(node);
- return null;
- }
+ if (singleClass != null &&
+ singleClass.isSubclassOf(backend.jsInterceptorClass)) {
+ node.constantValue = new InterceptorConstantValue(singleClass.rawType);
}
// Filter out intercepted classes that do not match the input type.
node.interceptedClasses.retainWhere((ClassElement clazz) {
@@ -2120,6 +2129,8 @@
JavaScriptBackend get backend => typeSystem.backend;
+ World get classWorld => typeSystem.classWorld;
+
AbstractValue get nothing => lattice.nothing;
AbstractValue nonConstant([TypeMask type]) => lattice.nonConstant(type);
@@ -2560,9 +2571,15 @@
break; // Cast fails. Continuation should remain unreachable.
case AbstractBool.Maybe:
- // TODO(asgerf): Narrow type of output to those that survive the cast.
setReachable(cont);
- setValue(cont.parameters.single, input);
+ TypeMask type = input.type;
+ if (node.type.element is ClassElement) {
+ // Narrow type of output to those that survive the cast.
+ type = type.intersection(
+ new TypeMask.subtype(node.type.element, classWorld),
+ classWorld);
+ }
+ setValue(cont.parameters.single, nonConstant(type));
break;
}
}
@@ -2676,7 +2693,7 @@
}
@override
- visitTypeExpression(TypeExpression node) {
+ void visitTypeExpression(TypeExpression node) {
// TODO(karlklose): come up with a type marker for JS entities or switch to
// real constants of type [Type].
setValue(node, nonConstant());
@@ -2688,7 +2705,7 @@
}
@override
- visitForeignCode(ForeignCode node) {
+ void visitForeignCode(ForeignCode node) {
if (node.continuation != null) {
Continuation continuation = node.continuation.definition;
setReachable(continuation);
@@ -2715,10 +2732,23 @@
}
@override
- visitAwait(Await node) {
+ void visitAwait(Await node) {
Continuation continuation = node.continuation.definition;
setReachable(continuation);
}
+
+ @override
+ void visitRefinement(Refinement node) {
+ AbstractValue value = getValue(node.value.definition);
+ if (value.isNothing || typeSystem.areDisjoint(value.type, node.type)) {
+ setValue(node, nothing);
+ } else if (value.isConstant) {
+ setValue(node, value);
+ } else {
+ setValue(node,
+ nonConstant(value.type.intersection(node.type, classWorld)));
+ }
+ }
}
/// Represents the abstract value of a primitive value at some point in the
@@ -2837,25 +2867,3 @@
values.remove(node.variable);
}
}
-
-
-class ContainsTry extends RecursiveVisitor {
- bool _found = false;
- ContainsTry._();
-
- /// Scans [root] for evidence of try-catch and try-finally.
- static bool analyze(Node root) {
- ContainsTry visitor = new ContainsTry._();
- visitor.visit(root);
- return visitor._found;
- }
-
- visit(Node node) {
- if (_found) return; // Early exit if we know the answer.
- super.visit(node);
- }
-
- processLetHandler(LetHandler node) {
- _found = true;
- }
-}
diff --git a/pkg/compiler/lib/src/diagnostics/messages.dart b/pkg/compiler/lib/src/diagnostics/messages.dart
index 99aa824..b812fd3 100644
--- a/pkg/compiler/lib/src/diagnostics/messages.dart
+++ b/pkg/compiler/lib/src/diagnostics/messages.dart
@@ -421,6 +421,7 @@
UNIMPLEMENTED_SETTER,
UNIMPLEMENTED_SETTER_ONE,
UNMATCHED_TOKEN,
+ UNRECOGNIZED_VERSION_OF_LOOKUP_MAP,
UNSUPPORTED_BANG_EQ_EQ,
UNSUPPORTED_EQ_EQ_EQ,
UNSUPPORTED_LITERAL_SYMBOL,
@@ -3295,6 +3296,10 @@
"more code and prevents the compiler from doing some optimizations.",
howToFix: "Consider removing this 'noSuchMethod' implementation."),
+ MessageKind.UNRECOGNIZED_VERSION_OF_LOOKUP_MAP:
+ const MessageTemplate(MessageKind.UNRECOGNIZED_VERSION_OF_LOOKUP_MAP,
+ "Unsupported version of package:lookup_map.",
+ howToFix: DONT_KNOW_HOW_TO_FIX),
}; // End of TEMPLATES.
diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart
index f8ba241..e031100 100644
--- a/pkg/compiler/lib/src/js_backend/backend.dart
+++ b/pkg/compiler/lib/src/js_backend/backend.dart
@@ -989,12 +989,8 @@
} else if (constant.isType) {
enqueueInResolution(getCreateRuntimeType(), registry);
registry.registerInstantiation(typeImplementation.rawType);
- TypeConstantValue typeConstant = constant;
- DartType representedType = typeConstant.representedType;
- if (representedType != const DynamicType()) {
- lookupMapAnalysis.registerTypeConstant(representedType.element);
- }
}
+ lookupMapAnalysis.registerConstantKey(constant);
}
void registerInstantiatedConstantType(DartType type, Registry registry) {
@@ -2163,7 +2159,7 @@
} else if (uri == Uris.dart_html) {
htmlLibraryIsLoaded = true;
} else if (uri == PACKAGE_LOOKUP_MAP) {
- lookupMapAnalysis.initRuntimeClass(find(library, 'LookupMap'));
+ lookupMapAnalysis.init(library);
}
annotations.onLibraryScanned(library);
});
@@ -2609,7 +2605,13 @@
return true;
}
- void onQueueClosed() => lookupMapAnalysis.onQueueClosed();
+ void onQueueClosed() {
+ lookupMapAnalysis.onQueueClosed();
+ }
+
+ void onCodegenStart() {
+ lookupMapAnalysis.onCodegenStart();
+ }
void onElementResolved(Element element, TreeElements elements) {
if ((element.isFunction || element.isGenerativeConstructor) &&
diff --git a/pkg/compiler/lib/src/js_backend/codegen/task.dart b/pkg/compiler/lib/src/js_backend/codegen/task.dart
index 16c882a..93a2320 100644
--- a/pkg/compiler/lib/src/js_backend/codegen/task.dart
+++ b/pkg/compiler/lib/src/js_backend/codegen/task.dart
@@ -170,18 +170,22 @@
assert(checkCpsIntegrity(cpsNode));
}
+ applyCpsPass(new RedundantJoinEliminator());
+ applyCpsPass(new RedundantPhiEliminator());
+ applyCpsPass(new InsertRefinements(compiler.typesTask, compiler.world));
TypePropagator typePropagator = new TypePropagator(compiler, this);
applyCpsPass(typePropagator);
dumpTypedIR(cpsNode, typePropagator);
- applyCpsPass(new LoopInvariantCodeMotion());
- applyCpsPass(new ShareInterceptors());
- applyCpsPass(new ScalarReplacer(compiler));
+ applyCpsPass(new RemoveRefinements());
applyCpsPass(new ShrinkingReducer());
+ applyCpsPass(new ScalarReplacer(compiler));
applyCpsPass(new MutableVariableEliminator());
applyCpsPass(new RedundantJoinEliminator());
applyCpsPass(new RedundantPhiEliminator());
applyCpsPass(new ShrinkingReducer());
- applyCpsPass(new LetSinker());
+ applyCpsPass(new LoopInvariantCodeMotion());
+ applyCpsPass(new ShareInterceptors());
+ applyCpsPass(new ShrinkingReducer());
return cpsNode;
}
diff --git a/pkg/compiler/lib/src/js_backend/lookup_map_analysis.dart b/pkg/compiler/lib/src/js_backend/lookup_map_analysis.dart
index 9d44af1..7be572b 100644
--- a/pkg/compiler/lib/src/js_backend/lookup_map_analysis.dart
+++ b/pkg/compiler/lib/src/js_backend/lookup_map_analysis.dart
@@ -7,15 +7,24 @@
import '../common/registry.dart' show Registry;
import '../compiler.dart' show Compiler;
+import '../diagnostics/messages.dart' show MessageKind;
import '../constants/values.dart' show
ConstantValue,
ConstructedConstantValue,
ListConstantValue,
NullConstantValue,
+ StringConstantValue,
TypeConstantValue;
import '../dart_types.dart' show DartType;
-import '../elements/elements.dart' show Elements, Element, ClassElement,
- FieldElement, FunctionElement, FunctionSignature;
+import '../elements/elements.dart' show
+ ClassElement,
+ Element,
+ Elements,
+ FieldElement,
+ FunctionElement,
+ FunctionSignature,
+ LibraryElement,
+ VariableElement;
import '../enqueue.dart' show Enqueuer;
import 'js_backend.dart' show JavaScriptBackend;
import '../dart_types.dart' show DynamicType, InterfaceType;
@@ -63,6 +72,13 @@
/// discover that a key in a map is potentially used.
final JavaScriptBackend backend;
+ /// The resolved [VariableElement] associated with the top-level `_version`.
+ VariableElement lookupMapVersionVariable;
+
+ /// The resolved [LibraryElement] associated with
+ /// `package:lookup_map/lookup_map.dart`.
+ LibraryElement lookupMapLibrary;
+
/// The resolved [ClassElement] associated with `LookupMap`.
ClassElement typeLookupMapClass;
@@ -79,18 +95,29 @@
/// this analysis.
final Map<ConstantValue, _LookupMapInfo> _lookupMaps = {};
- /// Types that we have discovered to be in use in the program.
- final _inUse = new Set<ClassElement>();
+ /// Keys that we have discovered to be in use in the program.
+ final _inUse = new Set<ConstantValue>();
- /// Pending work to do if we discover that a new type is in use. For each type
- /// that we haven't seen, we record the list of lookup-maps that use such type
- /// as a key.
- final _pending = <ClassElement, List<_LookupMapInfo>>{};
+ /// Internal helper to memoize the mapping between class elements and their
+ /// corresponding type constants.
+ final _typeConstants = <ClassElement, TypeConstantValue>{};
+
+ /// Internal helper to memoize which classes (ignoring Type) override equals.
+ ///
+ /// Const keys of these types will not be tree-shaken because we can't
+ /// statically guarantee that the program doesn't produce an equivalent key at
+ /// runtime. Technically if we limit lookup-maps to check for identical keys,
+ /// we could allow const instances of these types. However, we internally use
+ /// a hash map within lookup-maps today, so we need this restriction.
+ final _typesWithEquals = <ClassElement, bool>{};
+
+ /// Pending work to do if we discover that a new key is in use. For each key
+ /// that we haven't seen, we record the list of lookup-maps that contain an
+ /// entry with that key.
+ final _pending = <ConstantValue, List<_LookupMapInfo>>{};
/// Whether the backend is currently processing the codegen queue.
- // TODO(sigmund): is there a better way to do this. Do we need to plumb the
- // enqueuer on each callback?
- bool get _inCodegen => backend.compiler.phase == Compiler.PHASE_COMPILING;
+ bool _inCodegen = false;
LookupMapAnalysis(this.backend);
@@ -101,14 +128,53 @@
return typeLookupMapClass != null;
}
- /// Initializes this analysis by providing the resolver information of
- /// `LookupMap`.
- void initRuntimeClass(ClassElement cls) {
+ /// Initializes this analysis by providing the resolved library. This is
+ /// invoked during resolution when the `lookup_map` library is discovered.
+ void init(LibraryElement library) {
+ lookupMapLibrary = library;
+ // We will enable the lookupMapAnalysis as long as we get a known version of
+ // the lookup_map package. We otherwise produce a warning.
+ lookupMapVersionVariable = library.implementation.findLocal('_version');
+ if (lookupMapVersionVariable == null) {
+ backend.compiler.reportInfo(library,
+ MessageKind.UNRECOGNIZED_VERSION_OF_LOOKUP_MAP);
+ } else {
+ backend.compiler.enqueuer.resolution.addToWorkList(
+ lookupMapVersionVariable);
+ }
+ }
+
+ /// Checks if the version of lookup_map is valid, and if so, enable this
+ /// analysis during codegen.
+ void onCodegenStart() {
+ _inCodegen = true;
+ if (lookupMapVersionVariable == null) return;
+
+ // At this point, the lookupMapVersionVariable should be resolved and it's
+ // constant value should be available.
+ StringConstantValue value =
+ backend.constants.getConstantValueForVariable(lookupMapVersionVariable);
+ if (value == null) {
+ backend.compiler.reportInfo(lookupMapVersionVariable,
+ MessageKind.UNRECOGNIZED_VERSION_OF_LOOKUP_MAP);
+ return;
+ }
+
+ // TODO(sigmund): add proper version resolution using the pub_semver package
+ // when we introduce the next version.
+ String version = value.primitiveValue.slowToString();
+ if (version != '0.0.1') {
+ backend.compiler.reportInfo(lookupMapVersionVariable,
+ MessageKind.UNRECOGNIZED_VERSION_OF_LOOKUP_MAP);
+ return;
+ }
+
+ ClassElement cls = lookupMapLibrary.findLocal('LookupMap');
cls.computeType(backend.compiler);
entriesField = cls.lookupMember('_entries');
keyField = cls.lookupMember('_key');
valueField = cls.lookupMember('_value');
- // TODO(sigmund): Maybe inline nested maps make the output code smaller?
+ // TODO(sigmund): Maybe inline nested maps to make the output code smaller?
typeLookupMapClass = cls;
}
@@ -126,12 +192,46 @@
() => new _LookupMapInfo(lookupMap, this).._updateUsed());
}
- /// Records that [type] is used in the program, and updates every map that
- /// has it as a key.
- void _addUse(ClassElement type) {
- if (_inUse.add(type)) {
- _pending[type]?.forEach((info) => info._markUsed(type));
- _pending.remove(type);
+ /// Whether [key] is a constant value whose type overrides equals.
+ bool _overridesEquals(ConstantValue key) {
+ if (key is ConstructedConstantValue) {
+ ClassElement element = key.type.element;
+ return _typesWithEquals.putIfAbsent(element, () =>
+ element.lookupMember('==').enclosingClass !=
+ backend.compiler.objectClass);
+ }
+ return false;
+ }
+
+ /// Whether we need to preserve [key]. This is true for keys that are not
+ /// candidates for tree-shaking in the first place (primitives and non-type
+ /// const values overriding equals) and keys that we have seen in the program.
+ bool _shouldKeep(ConstantValue key) =>
+ key.isPrimitive || _inUse.contains(key) || _overridesEquals(key);
+
+ void _addClassUse(ClassElement cls) {
+ ConstantValue key = _typeConstants.putIfAbsent(cls,
+ () => backend.constantSystem.createType(backend.compiler, cls.rawType));
+ _addUse(key);
+ }
+
+ /// Record that [key] is used and update every lookup map that contains it.
+ void _addUse(ConstantValue key) {
+ if (_inUse.add(key)) {
+ _pending[key]?.forEach((info) => info._markUsed(key));
+ _pending.remove(key);
+ }
+ }
+
+ /// If [key] is a type, cache it in [_typeConstants].
+ _registerTypeKey(ConstantValue key) {
+ if (key is TypeConstantValue) {
+ ClassElement cls = key.representedType.element;
+ if (cls == null || !cls.isClass) {
+ // TODO(sigmund): report error?
+ return;
+ }
+ _typeConstants[cls] = key;
}
}
@@ -139,14 +239,14 @@
void registerInstantiatedClass(ClassElement element) {
if (!_isEnabled || !_inCodegen) return;
// TODO(sigmund): only add if .runtimeType is ever used
- _addUse(element);
+ _addClassUse(element);
}
/// Callback from the enqueuer, invoked when [type] is instantiated.
void registerInstantiatedType(InterfaceType type, Registry registry) {
if (!_isEnabled || !_inCodegen) return;
// TODO(sigmund): only add if .runtimeType is ever used
- _addUse(type.element);
+ _addClassUse(type.element);
// TODO(sigmund): only do this when type-argument expressions are used?
_addGenerics(type, registry);
}
@@ -157,7 +257,7 @@
if (!type.isGeneric) return;
for (var arg in type.typeArguments) {
if (arg is InterfaceType) {
- _addUse(arg.element);
+ _addClassUse(arg.element);
// Note: this call was needed to generate correct code for
// type_lookup_map/generic_type_test
// TODO(sigmund): can we get rid of this?
@@ -168,12 +268,17 @@
}
}
- /// Callback from the codegen enqueuer, invoked when a type constant
- /// corresponding to the [element] is used in the program.
- void registerTypeConstant(Element element) {
+ /// Callback from the codegen enqueuer, invoked when a constant (which is
+ /// possibly a const key or a type literal) is used in the program.
+ void registerTypeConstant(ClassElement element) {
if (!_isEnabled || !_inCodegen) return;
- assert(element.isClass);
- _addUse(element);
+ _addClassUse(element);
+ }
+
+ void registerConstantKey(ConstantValue constant) {
+ if (!_isEnabled || !_inCodegen) return;
+ if (constant.isPrimitive || _overridesEquals(constant)) return;
+ _addUse(constant);
}
/// Callback from the backend, invoked when reaching the end of the enqueuing
@@ -206,6 +311,11 @@
? 'lookup-map: nothing was tree-shaken'
: 'lookup-map: found $count unused keys ($sb)');
}
+
+ // Release resources.
+ _lookupMaps.clear();
+ _pending.clear();
+ _inUse.clear();
}
}
@@ -235,17 +345,12 @@
/// Entries in the lookup map whose keys have not been seen in the rest of the
/// program.
- Map<ClassElement, ConstantValue> unusedEntries =
- <ClassElement, ConstantValue>{};
+ Map<ConstantValue, ConstantValue> unusedEntries =
+ <ConstantValue, ConstantValue> {};
/// Entries that have been used, and thus will be part of the generated code.
- Map<ClassElement, ConstantValue> usedEntries =
- <ClassElement, ConstantValue>{};
-
- /// Internal helper to memoize the mapping between map class elements and
- /// their corresponding type constants.
- Map<ClassElement, TypeConstantValue> _typeConstants =
- <ClassElement, TypeConstantValue>{};
+ Map<ConstantValue, ConstantValue> usedEntries =
+ <ConstantValue, ConstantValue> {};
/// Creates and initializes the information containing all keys of the
/// original map marked as unused.
@@ -254,10 +359,7 @@
singlePair = !key.isNull;
if (singlePair) {
- TypeConstantValue typeKey = key;
- ClassElement cls = typeKey.representedType.element;
- _typeConstants[cls] = typeKey;
- unusedEntries[cls] = original.fields[analysis.valueField];
+ unusedEntries[key] = original.fields[analysis.valueField];
// Note: we modify the constant in-place, see comment in [original].
original.fields[analysis.keyField] = new NullConstantValue();
@@ -266,14 +368,8 @@
ListConstantValue list = original.fields[analysis.entriesField];
List<ConstantValue> keyValuePairs = list.entries;
for (int i = 0; i < keyValuePairs.length; i += 2) {
- TypeConstantValue type = keyValuePairs[i];
- ClassElement cls = type.representedType.element;
- if (cls == null || !cls.isClass) {
- // TODO(sigmund): report an error
- continue;
- }
- _typeConstants[cls] = type;
- unusedEntries[cls] = keyValuePairs[i + 1];
+ ConstantValue key = keyValuePairs[i];
+ unusedEntries[key] = keyValuePairs[i + 1];
}
// Note: we modify the constant in-place, see comment in [original].
@@ -288,23 +384,24 @@
/// we call [_markUsed] on each individual key as it gets discovered.
void _updateUsed() {
// Note: we call toList because `_markUsed` modifies the map.
- for (ClassElement type in unusedEntries.keys.toList()) {
- if (analysis._inUse.contains(type)) {
- _markUsed(type);
+ for (ConstantValue key in unusedEntries.keys.toList()) {
+ analysis._registerTypeKey(key);
+ if (analysis._shouldKeep(key)) {
+ _markUsed(key);
} else {
- analysis._pending.putIfAbsent(type, () => []).add(this);
+ analysis._pending.putIfAbsent(key, () => []).add(this);
}
}
}
- /// Marks that [type] is a key that has been seen, and thus, the corresponding
- /// entry in this map should be considered reachable.
- void _markUsed(ClassElement type) {
+ /// Marks that [key] has been seen, and thus, the corresponding entry in this
+ /// map should be considered reachable.
+ _markUsed(ConstantValue key) {
assert(!emitted);
- assert(unusedEntries.containsKey(type));
- assert(!usedEntries.containsKey(type));
- ConstantValue constant = unusedEntries.remove(type);
- usedEntries[type] = constant;
+ assert(unusedEntries.containsKey(key));
+ assert(!usedEntries.containsKey(key));
+ ConstantValue constant = unusedEntries.remove(key);
+ usedEntries[key] = constant;
analysis.backend.registerCompileTimeConstant(constant,
analysis.backend.compiler.globalDependencies,
addForEmission: false);
@@ -316,7 +413,7 @@
DartType listType = originalEntries.type;
List<ConstantValue> keyValuePairs = <ConstantValue>[];
usedEntries.forEach((key, value) {
- keyValuePairs.add(_typeConstants[key]);
+ keyValuePairs.add(key);
keyValuePairs.add(value);
});
diff --git a/pkg/compiler/lib/src/resolution/members.dart b/pkg/compiler/lib/src/resolution/members.dart
index 5b7558b..dbfdd13 100644
--- a/pkg/compiler/lib/src/resolution/members.dart
+++ b/pkg/compiler/lib/src/resolution/members.dart
@@ -4563,6 +4563,10 @@
}
ResolutionResult visitTryStatement(TryStatement node) {
+ // TODO(karlklose): also track the information about mutated variables,
+ // catch, and finally-block.
+ registry.registerTryStatement();
+
visit(node.tryBlock);
if (node.catchBlocks.isEmpty && node.finallyBlock == null) {
error(node.getEndToken().next, MessageKind.NO_CATCH_NOR_FINALLY);
diff --git a/pkg/compiler/lib/src/resolution/registry.dart b/pkg/compiler/lib/src/resolution/registry.dart
index 1c294aa..6a40754 100644
--- a/pkg/compiler/lib/src/resolution/registry.dart
+++ b/pkg/compiler/lib/src/resolution/registry.dart
@@ -609,4 +609,8 @@
void registerAsyncForIn(AsyncForIn node) {
backend.resolutionCallbacks.onAsyncForIn(node, this);
}
+
+ void registerTryStatement() {
+ mapping.containsTryStatement = true;
+ }
}
diff --git a/pkg/compiler/lib/src/resolution/tree_elements.dart b/pkg/compiler/lib/src/resolution/tree_elements.dart
index f69e8c2..3fed5c3 100644
--- a/pkg/compiler/lib/src/resolution/tree_elements.dart
+++ b/pkg/compiler/lib/src/resolution/tree_elements.dart
@@ -122,6 +122,9 @@
/// Returns the label that [node] targets.
LabelDefinition getTargetLabel(GotoStatement node);
+
+ /// `true` if the [analyzedElement]'s source code contains a [TryStatement].
+ bool get containsTryStatement;
}
class TreeElementMapping extends TreeElements {
@@ -140,6 +143,7 @@
Setlet<Send> _asserts;
Maplet<Send, SendStructure> _sendStructureMap;
Setlet<DartType> _requiredTypes;
+ bool containsTryStatement = false;
/// Map from nodes to the targets they define.
Map<Node, JumpTarget> _definedTargets;
diff --git a/pkg/compiler/lib/src/ssa/builder.dart b/pkg/compiler/lib/src/ssa/builder.dart
index f9d87f9..209e151 100644
--- a/pkg/compiler/lib/src/ssa/builder.dart
+++ b/pkg/compiler/lib/src/ssa/builder.dart
@@ -1623,6 +1623,22 @@
sourceInformation: sourceInformationBuilder.buildIf(function.body));
}
}
+ if (const bool.fromEnvironment('unreachable-throw') == true) {
+ var emptyParameters = parameters.values.where((p) =>
+ p.instructionType.isEmpty && !p.instructionType.isNullable);
+ if (emptyParameters.length > 0) {
+ String message = compiler.enableMinification
+ ? 'unreachable'
+ : 'unreachable: ${functionElement} because: ${emptyParameters}';
+ // TODO(sra): Use a library function that throws a proper error object.
+ push(new HForeignCode(
+ js.js.parseForeignJS('throw "$message"'),
+ backend.dynamicType,
+ <HInstruction>[],
+ isStatement: true));
+ return closeFunction();
+ }
+ }
function.body.accept(this);
return closeFunction();
}
@@ -8449,6 +8465,8 @@
int maxInliningNodes,
bool useMaxInliningNodes,
{bool allowLoops: false}) {
+ if (function.resolvedAst.elements.containsTryStatement) return false;
+
InlineWeeder weeder =
new InlineWeeder(maxInliningNodes, useMaxInliningNodes, allowLoops);
ast.FunctionExpression functionExpression = function.node;
@@ -8531,11 +8549,6 @@
seenReturn = true;
}
- void visitTryStatement(ast.Node node) {
- if (!registerNode()) return;
- tooDifficult = true;
- }
-
void visitThrow(ast.Throw node) {
if (!registerNode()) return;
// For now, we don't want to handle throw after a return even if
diff --git a/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart b/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart
index 4949bc9..f6b6a72 100644
--- a/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart
+++ b/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart
@@ -358,6 +358,8 @@
return exp is Constant ||
exp is This ||
exp is CreateInvocationMirror ||
+ exp is CreateInstance ||
+ exp is CreateBox ||
exp is GetStatic && exp.element.isFunction ||
exp is Interceptor ||
exp is ApplyBuiltinOperator ||
diff --git a/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart b/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart
index 89b8b19..9ad632c 100644
--- a/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart
+++ b/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart
@@ -675,6 +675,11 @@
return makeCallExpression(node, value);
}
+ @override
+ Expression visitRefinement(cps_ir.Refinement node) {
+ throw 'Unexpected Refinement node in tree builder';
+ }
+
/********** UNUSED VISIT METHODS *************/
unexpectedNode(cps_ir.Node node) {
diff --git a/pkg/lookup_map/README.md b/pkg/lookup_map/README.md
index 73f5a81..ecab6b5 100644
--- a/pkg/lookup_map/README.md
+++ b/pkg/lookup_map/README.md
@@ -14,6 +14,8 @@
that primitives, Strings, and constant objects that override the `==` operator
cannot be tree-shaken.
+**Note**: this feature is currently experimental in dart2js, we recommend trying
+other alternatives before relying on this feature.
## Examples
diff --git a/pkg/lookup_map/lib/lookup_map.dart b/pkg/lookup_map/lib/lookup_map.dart
index 609f854..0c03094 100644
--- a/pkg/lookup_map/lib/lookup_map.dart
+++ b/pkg/lookup_map/lib/lookup_map.dart
@@ -91,3 +91,10 @@
/// An expando that stores a flatten version of a [LookupMap], this is
/// computed and stored the first time the map is accessed.
final _flatMap = new Expando('_flat_map');
+
+/// Internal constant that matches the version in the pubspec. This is used by
+/// dart2js to ensure that optimizations are only enabled on known versions of
+/// this code.
+// Note: this needs to be kept in sync with the pubspec, otherwise
+// test/version_check_test would fail.
+final _version = '0.0.1';
diff --git a/pkg/lookup_map/pubspec.yaml b/pkg/lookup_map/pubspec.yaml
index fdc8946..8c420d0 100644
--- a/pkg/lookup_map/pubspec.yaml
+++ b/pkg/lookup_map/pubspec.yaml
@@ -1,3 +1,10 @@
name: lookup_map
description: a lookup-only map that can be tree-shaken by dart2js
+# Note: the version needs to be kept in sync with the string in
+# lib/lookup_map.dart, otherwise test/version_check_test would fail.
version: 0.0.1
+author: "Dart Team <misc@dartlang.org>"
+homepage: https://github.com/dart-lang/sdk/blob/master/pkg/lookup_map/README.md
+dev_dependencies:
+ test: ^0.12.3+8
+ yaml: ^2.1.2
diff --git a/pkg/lookup_map/test/lookup_map_test.dart b/pkg/lookup_map/test/lookup_map_test.dart
new file mode 100644
index 0000000..ce001dd
--- /dev/null
+++ b/pkg/lookup_map/test/lookup_map_test.dart
@@ -0,0 +1,74 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:lookup_map/lookup_map.dart';
+
+import 'package:test/test.dart';
+
+class Key {
+ final int id;
+ const Key(this.id);
+}
+
+class A{}
+const B = const Key(1);
+class C{}
+
+main() {
+ test('entries constructor', () {
+ var m = const LookupMap(const [
+ A, "the-text-for-A",
+ B, "the-text-for-B",
+ 1.2, "the-text-for-1.2"]);
+ expect(m[A], 'the-text-for-A');
+ expect(m[B], 'the-text-for-B');
+ expect(m[1.2], 'the-text-for-1.2');
+ expect(m[C], null);
+ expect(m[1.3], null);
+ });
+
+ test('pair constructor', () {
+ var m = const LookupMap.pair(A, "the-text-for-A");
+ expect(m[A], 'the-text-for-A');
+ expect(m[B], null);
+ });
+
+ test('nested lookup', () {
+ var m = const LookupMap(const [],
+ const [const LookupMap.pair(A, "the-text-for-A")]);
+ expect(m[A], 'the-text-for-A');
+ expect(m[B], null);
+ });
+
+ test('entry shadows nested maps', () {
+ var m = const LookupMap(const [
+ A, "the-text-for-A2",
+ ], const [
+ const LookupMap.pair(A, "the-text-for-A1"),
+ ]);
+ expect(m[A], 'the-text-for-A2');
+ });
+
+ test('nested maps shadow in order', () {
+ var m = const LookupMap(const [ ], const [
+ const LookupMap.pair(A, "the-text-for-A1"),
+ const LookupMap.pair(B, "the-text-for-B2"),
+ const LookupMap.pair(A, "the-text-for-A2"),
+ const LookupMap.pair(B, "the-text-for-B1"),
+ ]);
+ expect(m[A], 'the-text-for-A2');
+ expect(m[B], 'the-text-for-B1');
+ });
+
+ // This test would fail if dart2js has a bug, but we keep it here for our
+ // sanity.
+ test('reachable lookups are not tree-shaken', () {
+ var m = const LookupMap(const [
+ A, B,
+ B, C,
+ C, 3.4,
+ ]);
+ expect(m[m[m[A]]], 3.4);
+ });
+}
diff --git a/pkg/lookup_map/test/version_check_test.dart b/pkg/lookup_map/test/version_check_test.dart
new file mode 100644
index 0000000..2da0873
--- /dev/null
+++ b/pkg/lookup_map/test/version_check_test.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for 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:mirrors';
+import 'package:lookup_map/lookup_map.dart'; // accessed via mirrors;
+import 'package:test/test.dart';
+import 'package:yaml/yaml.dart';
+
+/// This dartdoc helps remove a warning for the unused import on [LookupMap].
+main() {
+ test('validate version number matches', () {
+ var pubspecPath = Platform.script.resolve('../pubspec.yaml').path;
+ var yaml = loadYaml(new File(pubspecPath).readAsStringSync());
+ var version1 = yaml['version'];
+ var library = currentMirrorSystem().findLibrary(#lookup_map);
+ var version2 = library.getField(new Symbol('_version')).reflectee;
+ expect(version1, version2);
+ });
+}
diff --git a/pkg/pkg.status b/pkg/pkg.status
index 5877a42..ef4d6f7d 100644
--- a/pkg/pkg.status
+++ b/pkg/pkg.status
@@ -20,6 +20,7 @@
[ $compiler == none && ($runtime == drt || $runtime == dartium || $runtime == ContentShellOnAndroid) ]
mutation_observer: Skip # Issue 21149
unittest/*: Skip # Issue 21949
+lookup_map/*: SkipByDesign
[ $runtime == vm && $mode == debug]
analysis_server/test/analysis_server_test: SkipSlow # Times out
@@ -107,6 +108,7 @@
analyzer/test/src/task/inputs_test: Pass, Slow # Issue 21628
analyzer/test/src/task/manager_test: Pass, Slow # Issue 21628
analyzer/test/src/task/model_test: Pass, Slow # Issue 21628
+lookup_map/test/version_check_test: SkipByDesign # Only meant to run in vm.
[ $runtime == jsshell ]
async/test/stream_zip_test: RuntimeError, OK # Timers are not supported.
@@ -194,3 +196,4 @@
analyzer/test/src/task/inputs_test: Crash # (Iterable<JavaFile> ... cannot handle sync*/async* functions
analyzer/test/src/task/manager_test: Crash # (Iterable<JavaFile> ... cannot handle sync*/async* functions
analyzer/test/src/task/model_test: Crash # (Iterable<JavaFile> ... cannot handle sync*/async* functions
+lookup_map/test/lookup_map_test: RuntimeError # $async$temp1.get$tests is not a function
diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc
index 45fe8b4..ee7b80d 100644
--- a/runtime/bin/main.cc
+++ b/runtime/bin/main.cc
@@ -85,7 +85,7 @@
extern const char* kPrecompiledSymbolName;
static const char* kPrecompiledVmIsolateName = "precompiled.vmisolate";
static const char* kPrecompiledIsolateName = "precompiled.isolate";
-static const char* kPrecompiledInstructionsName = "precompiled.instructions";
+static const char* kPrecompiledInstructionsName = "precompiled.S";
// Global flag that is used to indicate that we want to trace resolution of
diff --git a/runtime/bin/socket_patch.dart b/runtime/bin/socket_patch.dart
index 94285aa..be459ba 100644
--- a/runtime/bin/socket_patch.dart
+++ b/runtime/bin/socket_patch.dart
@@ -280,9 +280,6 @@
static const Duration _RETRY_DURATION_LOOPBACK =
const Duration(milliseconds: 25);
- // Use default Map so we keep order.
- static Map<int, _NativeSocket> _sockets = new Map<int, _NativeSocket>();
-
// Socket close state
bool isClosed = false;
bool isClosing = false;
@@ -317,13 +314,9 @@
bool writeAvailable = false;
static final Stopwatch sw = new Stopwatch()..start();
- // Statistics.
- int totalRead = 0;
- int totalWritten = 0;
- int readCount = 0;
- int writeCount = 0;
- double lastRead;
- double lastWrite;
+
+ static bool connectedResourceHandler = false;
+ _ReadWriteResourceInfo resourceInfo;
// The owner object is the object that the Socket is being used by, e.g.
// a HttpServer, a WebSocket connection, a process pipe, etc.
@@ -441,6 +434,8 @@
_RETRY_DURATION_LOOPBACK :
_RETRY_DURATION;
var timer = new Timer(duration, connectNext);
+ setupResourceInfo(socket);
+
connecting[socket] = timer;
// Setup handlers for receiving the first write event which
// indicate that the socket is fully connected.
@@ -492,7 +487,6 @@
.then((address) {
var socket = new _NativeSocket.listen();
socket.localAddress = address;
-
var result = socket.nativeCreateBindListen(address._in_addr,
port,
backlog,
@@ -505,11 +499,16 @@
port: port);
}
if (port != 0) socket.localPort = port;
+ setupResourceInfo(socket);
socket.connectToEventHandler();
return socket;
});
}
+ static void setupResourceInfo(_NativeSocket socket) {
+ socket.resourceInfo = new _SocketResourceInfo(socket);
+ }
+
static Future<_NativeSocket> bindDatagram(
host, int port, bool reuseAddress) {
return new Future.value(host)
@@ -534,6 +533,7 @@
port: port);
}
if (port != 0) socket.localPort = port;
+ setupResourceInfo(socket);
return socket;
});
}
@@ -575,10 +575,18 @@
}
if (result != null) {
available -= result.length;
- totalRead += result.length;
+ // TODO(ricow): Remove when we track internal and pipe uses.
+ assert(resourceInfo != null || isPipe || isInternal);
+ if (resourceInfo != null) {
+ resourceInfo.totalRead += result.length;
+ }
}
- readCount++;
- lastRead = timestamp;
+ // TODO(ricow): Remove when we track internal and pipe uses.
+ assert(resourceInfo != null || isPipe || isInternal);
+ if (resourceInfo != null) {
+ resourceInfo.readCount++;
+ resourceInfo.lastRead = timestamp;
+ }
return result;
}
@@ -595,10 +603,18 @@
// receive. If available becomes > 0, the _NativeSocket will continue to
// emit read events.
available = nativeAvailable();
- totalRead += result.data.length;
+ // TODO(ricow): Remove when we track internal and pipe uses.
+ assert(resourceInfo != null || isPipe || isInternal);
+ if (resourceInfo != null) {
+ resourceInfo.totalRead += result.data.length;
+ }
}
- readCount++;
- lastRead = timestamp;
+ // TODO(ricow): Remove when we track internal and pipe uses.
+ assert(resourceInfo != null || isPipe || isInternal);
+ if (resourceInfo != null) {
+ resourceInfo.readCount++;
+ resourceInfo.lastRead = timestamp;
+ }
return result;
}
@@ -637,9 +653,13 @@
}
// Negate the result, as stated above.
if (result < 0) result = -result;
- totalWritten += result;
- writeCount++;
- lastWrite = timestamp;
+ // TODO(ricow): Remove when we track internal and pipe uses.
+ assert(resourceInfo != null || isPipe || isInternal);
+ if (resourceInfo != null) {
+ resourceInfo.totalWritten += result;
+ resourceInfo.writeCount++;
+ resourceInfo.lastWrite = timestamp;
+ }
return result;
}
@@ -656,9 +676,13 @@
scheduleMicrotask(() => reportError(result, "Send failed"));
result = 0;
}
- totalWritten += result;
- writeCount++;
- lastWrite = timestamp;
+ // TODO(ricow): Remove when we track internal and pipe uses.
+ assert(resourceInfo != null || isPipe || isInternal);
+ if (resourceInfo != null) {
+ resourceInfo.totalWritten += result;
+ resourceInfo.writeCount++;
+ resourceInfo.lastWrite = timestamp;
+ }
return result;
}
@@ -673,8 +697,13 @@
if (nativeAccept(socket) != true) return null;
socket.localPort = localPort;
socket.localAddress = address;
- totalRead += 1;
- lastRead = timestamp;
+ setupResourceInfo(socket);
+ // TODO(ricow): Remove when we track internal and pipe uses.
+ assert(resourceInfo != null || isPipe || isInternal);
+ if (resourceInfo != null) {
+ resourceInfo.totalRead += 1;
+ resourceInfo.lastRead = timestamp;
+ }
return socket;
}
@@ -787,6 +816,11 @@
if (i == DESTROYED_EVENT) {
assert(isClosing);
assert(!isClosed);
+ // TODO(ricow): Remove/update when we track internal and pipe uses.
+ assert(resourceInfo != null || isPipe || isInternal);
+ if (resourceInfo != null) {
+ _SocketResourceInfo.SocketClosed(resourceInfo);
+ }
isClosed = true;
closeCompleter.complete();
disconnectFromEventHandler();
@@ -904,7 +938,14 @@
assert(!isClosed);
if (eventPort == null) {
eventPort = new RawReceivePort(multiplex);
- _sockets[_serviceId] = this;
+ }
+ if (!connectedResourceHandler) {
+ registerExtension('__getOpenSockets',
+ _SocketResourceInfo.getOpenSockets);
+ registerExtension('__getSocketByID',
+ _SocketResourceInfo.getSocketInfoMapByID);
+
+ connectedResourceHandler = true;
}
}
@@ -912,7 +953,6 @@
assert(eventPort != null);
eventPort.close();
eventPort = null;
- _sockets.remove(_serviceId);
// Now that we don't track this Socket anymore, we can clear the owner
// field.
owner = null;
@@ -1017,123 +1057,6 @@
if (result is OSError) throw result;
}
- String get _serviceTypePath => 'io/sockets';
- String get _serviceTypeName => 'Socket';
-
- String _JSONKind() {
- return isListening ? "Listening" :
- isPipe ? "Pipe" :
- isInternal ? "Internal" : "Normal";
- }
-
- Map _toJSONPipe(bool ref) {
- var name = 'Anonymous Pipe';
- var r = {
- 'id': _servicePath,
- 'type': _serviceType(ref),
- 'name': name,
- 'user_name': name,
- 'kind': _JSONKind(),
- };
- if (ref) {
- return r;
- }
- r['readClosed'] = isClosedRead;
- r['writeClosed'] = isClosedWrite;
- r['closing'] = isClosing;
- r['fd'] = nativeGetSocketId();
- if (owner != null) {
- r['owner'] = owner._toJSON(true);
- }
- return r;
- }
-
- Map _toJSONInternal(bool ref) {
- var name = 'Internal';
- var r = {
- 'id': _servicePath,
- 'type': _serviceType(ref),
- 'name': name,
- 'user_name': name,
- 'kind': _JSONKind(),
- };
- if (ref) {
- return r;
- }
- r['closing'] = isClosing;
- r['fd'] = nativeGetSocketId();
- if (owner != null) {
- r['owner'] = owner._toJSON(true);
- }
- return r;
- }
-
- Map _toJSONNetwork(bool ref) {
- var name = '${address.host}:$port';
- if (isTcp && !isListening) name += " <-> ${remoteAddress.host}:$remotePort";
- var r = {
- 'id': _servicePath,
- 'type': _serviceType(ref),
- 'name': name,
- 'user_name': name,
- 'kind': _JSONKind(),
- };
- if (ref) {
- return r;
- }
- var protocol = isTcp ? "TCP" : isUdp ? "UDP" : null;
- var localAddress;
- var localPort;
- var rAddress;
- var rPort;
- try {
- localAddress = address.address;
- } catch (e) { }
- try {
- localPort = port;
- } catch (e) { }
- try {
- rAddress = this.remoteAddress.address;
- } catch (e) { }
- try {
- rPort = remotePort;
- } catch (e) { }
- r['localAddress'] = localAddress;
- r['localPort'] = localPort;
- r['remoteAddress'] = rAddress;
- r['remotePort'] = rPort;
- r['protocol'] = protocol;
- r['readClosed'] = isClosedRead;
- r['writeClosed'] = isClosedWrite;
- r['closing'] = isClosing;
- r['listening'] = isListening;
- r['fd'] = nativeGetSocketId();
- if (owner != null) {
- r['owner'] = owner._toJSON(true);
- }
- return r;
- }
-
- Map _toJSON(bool ref) {
- var map;
- if (isPipe) {
- map = _toJSONPipe(ref);
- } else if (isInternal) {
- map = _toJSONInternal(ref);
- } else {
- map = _toJSONNetwork(ref);
- }
- if (!ref) {
- map['available'] = available;
- map['totalRead'] = totalRead;
- map['totalWritten'] = totalWritten;
- map['readCount'] = totalWritten;
- map['writeCount'] = writeCount;
- map['lastRead'] = lastRead;
- map['lastWrite'] = lastWrite;
- }
- return map;
- }
void nativeSetSocketId(int id) native "Socket_SetSocketId";
nativeAvailable() native "Socket_Available";
@@ -1286,8 +1209,6 @@
return new _RawServerSocketReference(_referencePort.sendPort);
}
- Map _toJSON(bool ref) => _socket._toJSON(ref);
-
void set _owner(owner) { _socket.owner = owner; }
}
@@ -1465,7 +1386,6 @@
}
}
- Map _toJSON(bool ref) => _socket._toJSON(ref);
void set _owner(owner) { _socket.owner = owner; }
}
@@ -1528,8 +1448,6 @@
return new _ServerSocketReference(_socket.reference);
}
- Map _toJSON(bool ref) => _socket._toJSON(ref);
-
void set _owner(owner) { _socket._owner = owner; }
}
@@ -1843,7 +1761,6 @@
}
}
- Map _toJSON(bool ref) => _raw._toJSON(ref);
void set _owner(owner) { _raw._owner = owner; }
}
@@ -1999,4 +1916,3 @@
port);
}
-String _socketsStats() => _SocketsObservatory.toJSON();
diff --git a/runtime/lib/integers.cc b/runtime/lib/integers.cc
index 1ee5b25..b1ca62c 100644
--- a/runtime/lib/integers.cc
+++ b/runtime/lib/integers.cc
@@ -261,7 +261,7 @@
}
if (value.IsSmi()) {
const Smi& smi_value = Smi::Cast(value);
- return smi_value.ShiftOp(kind, amount, silent);
+ return smi_value.ShiftOp(kind, amount, Heap::kNew, silent);
}
if (value.IsMint()) {
const int64_t mint_value = value.AsInt64Value();
diff --git a/runtime/lib/mirrors.cc b/runtime/lib/mirrors.cc
index c1a869a..65f7c62 100644
--- a/runtime/lib/mirrors.cc
+++ b/runtime/lib/mirrors.cc
@@ -602,7 +602,7 @@
#define CHECK_KIND_SHIFT(name) \
field = cls.LookupField(String::Handle(String::New(#name))); \
ASSERT(!field.IsNull()); \
- value ^= field.value(); \
+ value ^= field.StaticValue(); \
ASSERT(value.Value() == Mirrors::name);
MIRRORS_KIND_SHIFT_LIST(CHECK_KIND_SHIFT)
#undef CHECK_KIND_SHIFT
@@ -677,7 +677,7 @@
}
} else {
if (!field.IsUninitialized()) {
- return field.value();
+ return field.StaticValue();
}
// An uninitialized field was found. Check for a getter in the field's
// owner classs.
@@ -754,7 +754,7 @@
DartEntry::InvokeFunction(getter, Object::empty_array()));
return ReturnResult(result);
}
- return field.value();
+ return field.StaticValue();
}
@@ -1620,7 +1620,7 @@
UNREACHABLE();
}
- field.set_value(value);
+ field.SetStaticValue(value);
return value.raw();
}
@@ -1917,7 +1917,7 @@
UNREACHABLE();
}
- field.set_value(value);
+ field.SetStaticValue(value);
return value.raw();
}
diff --git a/runtime/lib/regexp.cc b/runtime/lib/regexp.cc
index 6fd9d6f..14bf14f 100644
--- a/runtime/lib/regexp.cc
+++ b/runtime/lib/regexp.cc
@@ -83,6 +83,7 @@
DEFINE_NATIVE_ENTRY(JSSyntaxRegExp_ExecuteMatch, 3) {
+ // This function is intrinsified. See Intrinsifier::JSRegExp_ExecuteMatch.
const JSRegExp& regexp = JSRegExp::CheckedHandle(arguments->NativeArgAt(0));
ASSERT(!regexp.IsNull());
GET_NON_NULL_NATIVE_ARGUMENT(String, subject, arguments->NativeArgAt(1));
@@ -93,15 +94,7 @@
zone);
}
- // This function is intrinsified. See Intrinsifier::JSRegExp_ExecuteMatch.
- const intptr_t cid = subject.GetClassId();
-
- // Retrieve the cached function.
- const Function& fn = Function::Handle(regexp.function(cid));
- ASSERT(!fn.IsNull());
-
- // And finally call the generated code.
- return IRRegExpMacroAssembler::Execute(fn, subject, start_index, zone);
+ return IRRegExpMacroAssembler::Execute(regexp, subject, start_index, zone);
}
} // namespace dart
diff --git a/runtime/observatory/tests/service/coverage_test.dart b/runtime/observatory/tests/service/coverage_test.dart
index 4b44b37..5223092 100644
--- a/runtime/observatory/tests/service/coverage_test.dart
+++ b/runtime/observatory/tests/service/coverage_test.dart
@@ -11,7 +11,7 @@
int globalVar = 100;
class MyClass {
- static void myFunction(int value) {
+ static void myFunction(int value) {
if (value < 0) {
print("negative");
} else {
@@ -22,9 +22,9 @@
static void otherFunction(int value) {
if (value < 0) {
- print("otherFunction <");
+ print("otherFunction <");
} else {
- print("otherFunction >=");
+ print("otherFunction >=");
}
}
}
@@ -79,8 +79,8 @@
expect(coverage['coverage'][0]['hits'],
equals([15, 1, 16, 0, 18, 1, 20, 1,
24, 1, 25, 1, 27, 0]));
- expect(coverage['coverage'][1]['hits'].take(12),
- equals([33, 1, 34, 1, 32, 1, 105, 2, 105, 1]));
+ expect(coverage['coverage'][1]['hits'],
+ equals([33, 1, 34, 1, 105, 2]));
// Script
await cls.load();
@@ -91,8 +91,8 @@
expect(coverage['coverage'][0]['hits'],
equals([15, 1, 16, 0, 18, 1, 20, 1,
24, 1, 25, 1, 27, 0]));
- expect(coverage['coverage'][1]['hits'].take(12),
- equals([33, 1, 34, 1, 32, 1, 105, 2, 105, 1]));
+ expect(coverage['coverage'][1]['hits'],
+ equals([33, 1, 34, 1, 105, 2]));
// Isolate
coverage = await isolate.invokeRpcNoUpgrade('_getCoverage', {});
diff --git a/runtime/observatory/tests/service/file_service_test.dart b/runtime/observatory/tests/service/file_service_test.dart
new file mode 100644
index 0000000..dbb8e53
--- /dev/null
+++ b/runtime/observatory/tests/service/file_service_test.dart
@@ -0,0 +1,101 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:developer';
+import 'dart:io' as io;
+import 'package:observatory/service_io.dart';
+import 'package:unittest/unittest.dart';
+import 'test_helper.dart';
+
+Future setupFiles() async {
+ var dir = await io.Directory.systemTemp.createTemp('file_service');
+ var writingFile;
+ var readingFile;
+
+ void closeDown() {
+ if (writingFile != null) {
+ writingFile.closeSync();
+ }
+ if (readingFile != null) {
+ readingFile.closeSync();
+ }
+ dir.deleteSync(recursive: true);
+ }
+
+ Future<ServiceExtensionResponse> cleanup(ignored_a, ignored_b) {
+ closeDown();
+ var result = JSON.encode({'type' : 'foobar'});
+ return new Future.value(new ServiceExtensionResponse.result(result));
+ }
+
+ Future<ServiceExtensionResponse> setup(ignored_a, ignored_b) async {
+ try {
+ var filePath = dir.path + io.Platform.pathSeparator + "file";
+ var f = new io.File(filePath);
+ writingFile = await f.open(mode: io.FileMode.WRITE);
+ await writingFile.writeByte(42);
+ await writingFile.writeByte(42);
+ await writingFile.writeByte(42);
+
+ var file = new io.File.fromUri(io.Platform.script);
+ readingFile = await file.open();
+ await readingFile.readByte();
+ await readingFile.readByte();
+ await readingFile.readByte();
+ await readingFile.readByte();
+ await readingFile.readByte();
+
+ // The utility functions should close the files after them, so we
+ // don't expect the calls below to result in open files.
+ var writeTemp = dir.path + io.Platform.pathSeparator + "other_file";
+ var utilFile = new io.File(writeTemp);
+ await utilFile.writeAsString('foobar');
+ var readTemp = new io.File(writeTemp);
+ var result = await readTemp.readAsString();
+ expect(result, equals('foobar'));
+
+ } catch (e) {
+ closeDown();
+ throw e;
+ }
+ var result = JSON.encode({'type' : 'foobar'});
+ return new Future.value(new ServiceExtensionResponse.result(result));
+ }
+ registerExtension('__cleanup', cleanup);
+ registerExtension('__setup', setup);
+}
+
+var fileTests = [
+ (Isolate isolate) async {
+ await isolate.invokeRpcNoUpgrade('__setup', {});
+ try {
+ var result = await isolate.invokeRpcNoUpgrade('__getOpenFiles', {});
+ expect(result['type'], equals('_openfiles'));
+
+ expect(result['data'].length, equals(2));
+ var writing = await isolate.invokeRpcNoUpgrade(
+ '__getFileByID', { 'id' : result['data'][0]['id'] });
+
+ expect(writing['total_read'], equals(0));
+ expect(writing['read_count'], equals(0));
+ expect(writing['write_count'], equals(3));
+ expect(writing['total_written'], equals(3));
+
+ var reading = await isolate.invokeRpcNoUpgrade(
+ '__getFileByID', { 'id' : result['data'][1]['id'] });
+
+
+ expect(reading['total_read'], equals(5));
+ expect(reading['read_count'], equals(5));
+ expect(reading['write_count'], equals(0));
+ expect(reading['total_written'], equals(0));
+ } finally {
+ await isolate.invokeRpcNoUpgrade('__cleanup', {});
+ }
+ },
+];
+
+main(args) async => runIsolateTests(args, fileTests, testeeBefore:setupFiles);
diff --git a/runtime/observatory/tests/service/tcp_socket_closing_service_test.dart b/runtime/observatory/tests/service/tcp_socket_closing_service_test.dart
new file mode 100644
index 0000000..aef5571
--- /dev/null
+++ b/runtime/observatory/tests/service/tcp_socket_closing_service_test.dart
@@ -0,0 +1,74 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io' as io;
+import 'package:observatory/service_io.dart';
+import 'package:unittest/unittest.dart';
+import 'test_helper.dart';
+
+
+/// Test that we correctly remove sockets that have been closed from the list
+/// of open sockets. We explictly leave one socket open.
+
+Future setup() async {
+ var serverSocket = await io.ServerSocket.bind('127.0.0.1', 0);
+ serverSocket.listen((s) {
+ s.drain();
+ s.close();
+ });
+ var socket = await io.Socket.connect("127.0.0.1", serverSocket.port);
+ socket.write("foobar");
+ socket.write("foobar");
+
+ await socket.flush();
+ await socket.close();
+ await socket.drain();
+
+ var socket2 = await io.Socket.connect("127.0.0.1", serverSocket.port);
+ socket2.write("foobarfoobar");
+ await socket2.flush();
+ await socket2.close();
+ await socket2.drain();
+ await serverSocket.close();
+
+ var server = await io.RawDatagramSocket.bind('127.0.0.1', 0);
+ server.listen((io.RawSocketEvent event) {
+ if(event == io.RawSocketEvent.READ) {
+ io.Datagram dg = server.receive();
+ dg.data.forEach((x) => true);
+ server.close();
+ }
+ });
+ var client = await io.RawDatagramSocket.bind('127.0.0.1', 0);
+ client.send(UTF8.encoder.convert('foobar'),
+ new io.InternetAddress('127.0.0.1'), server.port);
+ client.close();
+
+ // The one socket to expect.
+ await io.ServerSocket.bind('127.0.0.1', 0);
+}
+
+var tests = [
+ // Initial.
+ (Isolate isolate) async {
+ var result = await isolate.invokeRpcNoUpgrade('__getOpenSockets', {});
+ expect(result['type'], equals('_opensockets'));
+ // We expect only one socket to be open, the server socket create at the
+ // end of test.
+ expect(result['data'].length, equals(1));
+ var server = await isolate.invokeRpcNoUpgrade(
+ '__getSocketByID', { 'id' : result['data'][0]['id'] });
+ expect(server['listening'], isTrue);
+ expect(server['last_read'], equals(0));
+ expect(server['total_read'], equals(0));
+ expect(server['last_write'], equals(0));
+ expect(server['total_written'], equals(0));
+ expect(server['write_count'], equals(0));
+ expect(server['read_count'], equals(0));
+ },
+];
+
+main(args) async => runIsolateTests(args, tests, testeeBefore:setup);
diff --git a/runtime/observatory/tests/service/tcp_socket_service_test.dart b/runtime/observatory/tests/service/tcp_socket_service_test.dart
new file mode 100644
index 0000000..0a85611
--- /dev/null
+++ b/runtime/observatory/tests/service/tcp_socket_service_test.dart
@@ -0,0 +1,175 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io' as io;
+import 'package:observatory/service_io.dart';
+import 'package:unittest/unittest.dart';
+import 'test_helper.dart';
+
+Future setupTCP() async {
+ // Note that we don't close after us, by design we leave the sockets opens
+ // to allow us to query them from the other isolate.
+ var serverSocket = await io.ServerSocket.bind('127.0.0.1', 0);
+ serverSocket.listen((s) {
+ s.transform(UTF8.decoder).listen(print);
+ s.close();
+ });
+ var socket = await io.Socket.connect("127.0.0.1", serverSocket.port);
+ socket.write("foobar");
+ socket.write("foobar");
+ await socket.flush();
+
+ var socket2 = await io.Socket.connect("127.0.0.1", serverSocket.port);
+ socket2.write("foobarfoobar");
+ await socket2.flush();
+}
+
+void expectTimeBiggerThanZero(time) {
+ // Stopwatch resolution on windows makes us sometimes report 0;
+ if (io.Platform.isWindows) {
+ expect(time, greaterThanOrEqualTo(0));
+ } else {
+ expect(time, greaterThan(0));
+ }
+}
+
+var tcpTests = [
+ // Initial.
+ (Isolate isolate) async {
+ var result = await isolate.invokeRpcNoUpgrade('__getOpenSockets', {});
+ expect(result['type'], equals('_opensockets'));
+ // We expect 3 sockets to be open (in this order):
+ // The server socket accepting connections, on port X
+ // The accepted connection on the client, on port Y
+ // The client connection, on port X
+ expect(result['data'].length, equals(5));
+ // The first socket will have a name like listening:127.0.0.1:X
+ // The second will have a name like 127.0.0.1:Y
+ // The third will have a name like 127.0.0.1:X
+ expect(result['data'][0]['name'].startsWith('listening:127.0.0.1'), isTrue);
+ expect(result['data'][1]['name'].startsWith('127.0.0.1:'), isTrue);
+ expect(result['data'][2]['name'].startsWith('127.0.0.1:'), isTrue);
+
+ var listening = await isolate.invokeRpcNoUpgrade(
+ '__getSocketByID', { 'id' : result['data'][0]['id'] });
+ expect(listening['id'], equals(result['data'][0]['id']));
+ expect(listening['listening'], isTrue);
+ expect(listening['socket_type'], equals('TCP'));
+ expect(listening['port'], greaterThanOrEqualTo(1024));
+ expectTimeBiggerThanZero(listening['last_read']);
+
+ expect(listening['total_read'], equals(2));
+ expect(listening['last_write'], equals(0));
+ expect(listening['total_written'], equals(0));
+ expect(listening['write_count'], equals(0));
+ expect(listening['read_count'], equals(0));
+ expect(listening['remote_host'], equals('NA'));
+ expect(listening['remote_port'], equals('NA'));
+
+ var client = await isolate.invokeRpcNoUpgrade(
+ '__getSocketByID', { 'id' : result['data'][1]['id'] });
+ expect(client['id'], equals(result['data'][1]['id']));
+
+ var server = await isolate.invokeRpcNoUpgrade(
+ '__getSocketByID', { 'id' : result['data'][2]['id'] });
+ expect(server['id'], equals(result['data'][2]['id']));
+
+ // We expect the client to be connected on the port and
+ // host of the listening socket.
+ expect(client['remote_port'], equals(listening['port']));
+ expect(client['remote_host'], equals(listening['host']));
+ // We expect the third socket (accepted server) to be connected to the
+ // same port and host as the listening socket (the listening one).
+ expect(server['port'], equals(listening['port']));
+ expect(server['host'], equals(listening['host']));
+
+ expect(client['listening'], isFalse);
+ expect(server['listening'], isFalse);
+
+ expect(client['socket_type'], equals('TCP'));
+ expect(server['socket_type'], equals('TCP'));
+
+ // We are using no reserved ports.
+ expect(client['port'], greaterThanOrEqualTo(1024));
+ expect(server['port'], greaterThanOrEqualTo(1024));
+
+ // The client and server "mirror" each other in reads and writes, and the
+ // timestamps are in correct order.
+ expect(client['last_read'], equals(0));
+ expectTimeBiggerThanZero(server['last_read']);
+ expect(client['total_read'], equals(0));
+ expect(server['total_read'], equals(12));
+ expect(client['read_count'], equals(0));
+ expect(server['read_count'], greaterThanOrEqualTo(1));
+
+ expectTimeBiggerThanZero(client['last_write']);
+ expect(server['last_write'], equals(0));
+ expect(client['total_written'], equals(12));
+ expect(server['total_written'], equals(0));
+ expect(client['write_count'], greaterThanOrEqualTo(2));
+ expect(server['write_count'], equals(0));
+
+ // Order
+ // Stopwatch resolution on windows can make us have the same timestamp.
+ if (io.Platform.isWindows) {
+ expect(server['last_read'], greaterThanOrEqualTo(client['last_write']));
+ } else {
+ expect(server['last_read'], greaterThan(client['last_write']));
+ }
+
+ var second_client = await isolate.invokeRpcNoUpgrade(
+ '__getSocketByID', { 'id' : result['data'][3]['id'] });
+ expect(second_client['id'], equals(result['data'][3]['id']));
+ var second_server = await isolate.invokeRpcNoUpgrade(
+ '__getSocketByID', { 'id' : result['data'][4]['id'] });
+ expect(second_server['id'], equals(result['data'][4]['id']));
+
+ // We expect the client to be connected on the port and
+ // host of the listening socket.
+ expect(second_client['remote_port'], equals(listening['port']));
+ expect(second_client['remote_host'], equals(listening['host']));
+ // We expect the third socket (accepted server) to be connected to the
+ // same port and host as the listening socket (the listening one).
+ expect(second_server['port'], equals(listening['port']));
+ expect(second_server['host'], equals(listening['host']));
+
+ expect(second_client['listening'], isFalse);
+ expect(second_server['listening'], isFalse);
+
+ expect(second_client['socket_type'], equals('TCP'));
+ expect(second_server['socket_type'], equals('TCP'));
+
+ // We are using no reserved ports.
+ expect(second_client['port'], greaterThanOrEqualTo(1024));
+ expect(second_server['port'], greaterThanOrEqualTo(1024));
+
+ // The client and server "mirror" each other in reads and writes, and the
+ // timestamps are in correct order.
+ expect(second_client['last_read'], equals(0));
+ expectTimeBiggerThanZero(second_server['last_read']);
+ expect(second_client['total_read'], equals(0));
+ expect(second_server['total_read'], equals(12));
+ expect(second_client['read_count'], equals(0));
+ expect(second_server['read_count'], greaterThanOrEqualTo(1));
+
+ expectTimeBiggerThanZero(second_client['last_write']);
+ expect(second_server['last_write'], equals(0));
+ expect(second_client['total_written'], equals(12));
+ expect(second_server['total_written'], equals(0));
+ expect(second_client['write_count'], greaterThanOrEqualTo(1));
+ expect(second_server['write_count'], equals(0));
+
+ // Order
+ // Stopwatch resolution on windows make us sometimes report the same value.
+ if (io.Platform.isWindows) {
+ expect(server['last_read'], greaterThanOrEqualTo(client['last_write']));
+ } else {
+ expect(server['last_read'], greaterThan(client['last_write']));
+ }
+ },
+];
+
+main(args) async => runIsolateTests(args, tcpTests, testeeBefore:setupTCP);
diff --git a/runtime/observatory/tests/service/udp_socket_service_test.dart b/runtime/observatory/tests/service/udp_socket_service_test.dart
new file mode 100644
index 0000000..6b4b3b9
--- /dev/null
+++ b/runtime/observatory/tests/service/udp_socket_service_test.dart
@@ -0,0 +1,82 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io' as io;
+import 'package:observatory/service_io.dart';
+import 'package:unittest/unittest.dart';
+import 'test_helper.dart';
+
+Future setupUDP() async {
+ var server = await io.RawDatagramSocket.bind('127.0.0.1', 0);
+ server.listen((io.RawSocketEvent event) {
+ if(event == io.RawSocketEvent.READ) {
+ io.Datagram dg = server.receive();
+ dg.data.forEach((x) => true);
+ }
+ });
+ var client = await io.RawDatagramSocket.bind('127.0.0.1', 0);
+ client.send(UTF8.encoder.convert('foobar'),
+ new io.InternetAddress('127.0.0.1'), server.port);
+}
+
+var udpTests = [
+ // Initial.
+ (Isolate isolate) async {
+ var result = await isolate.invokeRpcNoUpgrade('__getOpenSockets', {});
+ expect(result['type'], equals('_opensockets'));
+ // We expect 2 sockets to be open (in this order):
+ // The server socket accepting connections, on port X
+ // The client socket on port Y
+ expect(result['data'].length, equals(2));
+ // The first socket will have a name like listening:127.0.0.1:X
+ // The second will have a name like 127.0.0.1:Y
+ // The third will have a name like 127.0.0.1:X
+ expect(result['data'][0]['name'].startsWith('127.0.0.1'), isTrue);
+ expect(result['data'][1]['name'].startsWith('127.0.0.1:'), isTrue);
+
+ var server = await isolate.invokeRpcNoUpgrade(
+ '__getSocketByID', { 'id' : result['data'][0]['id'] });
+ expect(server['id'], equals(result['data'][0]['id']));
+ expect(server['remote_port'], equals('NA'));
+ expect(server['remote_host'], equals('NA'));
+ expect(server['listening'], isFalse);
+ expect(server['socket_type'], equals('UDP'));
+ expect(server['port'], greaterThanOrEqualTo(1024));
+ // Stopwatch resolution on windows makes us sometimes report 0;
+ if (io.Platform.isWindows) {
+ expect(server['last_read'], greaterThanOrEqualTo(0));
+ } else {
+ expect(server['last_read'], greaterThan(0));
+ }
+ expect(server['total_read'], equals(6));
+ expect(server['last_write'], equals(0));
+ expect(server['total_written'], equals(0));
+ expect(server['write_count'], equals(0));
+ expect(server['read_count'], greaterThanOrEqualTo(1));
+
+ var client = await isolate.invokeRpcNoUpgrade(
+ '__getSocketByID', { 'id' : result['data'][1]['id'] });
+ expect(client['id'], equals(result['data'][1]['id']));
+ expect(client['remote_port'], equals('NA'));
+ expect(client['remote_host'], equals('NA'));
+ expect(client['listening'], isFalse);
+ expect(client['socket_type'], equals('UDP'));
+ expect(client['port'], greaterThanOrEqualTo(1024));
+ expect(client['last_read'], equals(0));
+ expect(client['total_read'], equals(0));
+ // Stopwatch resolution on windows makes us sometimes report 0;
+ if (io.Platform.isWindows) {
+ expect(client['last_write'], greaterThanOrEqualTo(0));
+ } else {
+ expect(client['last_write'], greaterThan(0));
+ }
+ expect(client['total_written'], equals(6));
+ expect(client['write_count'], greaterThanOrEqualTo(1));
+ expect(client['read_count'], equals(0));
+ },
+];
+
+main(args) async => runIsolateTests(args, udpTests, testeeBefore:setupUDP);
diff --git a/runtime/vm/ast.cc b/runtime/vm/ast.cc
index 26618b5..48bd053 100644
--- a/runtime/vm/ast.cc
+++ b/runtime/vm/ast.cc
@@ -572,9 +572,11 @@
if (is_super_getter()) {
ASSERT(receiver() != NULL);
const String& setter_name =
- String::ZoneHandle(zone, Field::SetterSymbol(field_name_));
- Function& setter = Function::ZoneHandle(zone,
- Resolver::ResolveDynamicAnyArgs(cls(), setter_name));
+ String::ZoneHandle(zone, Field::LookupSetterSymbol(field_name_));
+ Function& setter = Function::ZoneHandle(zone);
+ if (!setter_name.IsNull()) {
+ setter = Resolver::ResolveDynamicAnyArgs(cls(), setter_name);
+ }
if (setter.IsNull() || setter.is_abstract()) {
// No instance setter found in super class chain,
// noSuchMethod will be called at runtime.
@@ -618,15 +620,17 @@
}
// No field found in prefix. Look for a setter function.
- const String& setter_name = String::Handle(zone,
- Field::SetterName(field_name_));
- obj = prefix.LookupObject(setter_name);
- if (obj.IsFunction()) {
- const Function& setter = Function::ZoneHandle(zone,
- Function::Cast(obj).raw());
- ASSERT(setter.is_static() && setter.IsSetterFunction());
- return new StaticSetterNode(
- token_pos(), NULL, field_name_, setter, rhs);
+ const String& setter_name =
+ String::Handle(zone, Field::LookupSetterSymbol(field_name_));
+ if (!setter_name.IsNull()) {
+ obj = prefix.LookupObject(setter_name);
+ if (obj.IsFunction()) {
+ const Function& setter =
+ Function::ZoneHandle(zone, Function::Cast(obj).raw());
+ ASSERT(setter.is_static() && setter.IsSetterFunction());
+ return new StaticSetterNode(
+ token_pos(), NULL, field_name_, setter, rhs);
+ }
}
// No writeable field and no setter found in the prefix. Return a
@@ -651,14 +655,17 @@
}
// No field found in library. Look for a setter function.
- const String& setter_name = String::Handle(zone,
- Field::SetterName(field_name_));
- obj = library.ResolveName(setter_name);
- if (obj.IsFunction()) {
- const Function& setter = Function::ZoneHandle(zone,
- Function::Cast(obj).raw());
- ASSERT(setter.is_static() && setter.IsSetterFunction());
- return new StaticSetterNode(token_pos(), NULL, field_name_, setter, rhs);
+ const String& setter_name =
+ String::Handle(zone, Field::LookupSetterSymbol(field_name_));
+ if (!setter_name.IsNull()) {
+ obj = library.ResolveName(setter_name);
+ if (obj.IsFunction()) {
+ const Function& setter =
+ Function::ZoneHandle(zone, Function::Cast(obj).raw());
+ ASSERT(setter.is_static() && setter.IsSetterFunction());
+ return
+ new StaticSetterNode(token_pos(), NULL, field_name_, setter, rhs);
+ }
}
// No writeable field and no setter found in the library. Return a
@@ -685,8 +692,9 @@
return new StaticSetterNode(token_pos(), NULL, cls(), field_name_, rhs);
}
#if defined(DEBUG)
- const String& getter_name = String::Handle(zone,
- Field::GetterName(field_name_));
+ const String& getter_name =
+ String::Handle(zone, Field::LookupGetterSymbol(field_name_));
+ ASSERT(!getter_name.IsNull());
const Function& getter =
Function::Handle(zone, cls().LookupStaticFunction(getter_name));
ASSERT(!getter.IsNull() &&
@@ -741,7 +749,10 @@
return NULL;
}
const String& getter_name =
- String::Handle(Field::GetterName(this->field_name()));
+ String::Handle(Field::LookupGetterSymbol(this->field_name()));
+ if (getter_name.IsNull()) {
+ return NULL;
+ }
const Function& getter_func =
Function::Handle(this->cls().LookupStaticFunction(getter_name));
if (getter_func.IsNull() || !getter_func.is_const()) {
diff --git a/runtime/vm/ast.h b/runtime/vm/ast.h
index 7b4ae79..fa4ba21 100644
--- a/runtime/vm/ast.h
+++ b/runtime/vm/ast.h
@@ -1311,7 +1311,7 @@
virtual const Instance* EvalConstExpr() const {
ASSERT(field_.is_static());
return !is_deferred_reference_ && field_.is_const()
- ? &Instance::ZoneHandle(field_.value())
+ ? &Instance::ZoneHandle(field_.StaticValue())
: NULL;
}
diff --git a/runtime/vm/ast_transformer.cc b/runtime/vm/ast_transformer.cc
index 76faa89..b4583cf 100644
--- a/runtime/vm/ast_transformer.cc
+++ b/runtime/vm/ast_transformer.cc
@@ -62,7 +62,8 @@
LocalVariable* AwaitTransformer::EnsureCurrentTempVar() {
- String& symbol = String::ZoneHandle(Z, String::NewFormatted("%d", temp_cnt_));
+ String& symbol =
+ String::ZoneHandle(Z, Symbols::NewFormatted("%d", temp_cnt_));
symbol = Symbols::FromConcat(Symbols::AwaitTempVarPrefix(), symbol);
ASSERT(!symbol.IsNull());
// Look up the variable in the scope used for async temp variables.
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index fc4dfad..494a42d 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -715,8 +715,8 @@
arguments.ToCString());
}
Error& error = Error::Handle();
- super_type_arg =
- super_type_arg.InstantiateFrom(arguments, &error, trail);
+ super_type_arg = super_type_arg.InstantiateFrom(
+ arguments, &error, trail, Heap::kOld);
if (!error.IsNull()) {
// InstantiateFrom does not report an error if the type is still
// uninstantiated. Instead, it will return a new BoundedType so
@@ -818,7 +818,8 @@
if (declared_bound.IsInstantiated()) {
instantiated_bound = declared_bound.raw();
} else {
- instantiated_bound = declared_bound.InstantiateFrom(arguments, &error);
+ instantiated_bound =
+ declared_bound.InstantiateFrom(arguments, &error, NULL, Heap::kOld);
}
if (!instantiated_bound.IsFinalized()) {
// The bound refers to type parameters, creating a cycle; postpone
@@ -1361,8 +1362,8 @@
}
}
if (field.is_static() &&
- (field.value() != Object::null()) &&
- (field.value() != Object::sentinel().raw())) {
+ (field.StaticValue() != Object::null()) &&
+ (field.StaticValue() != Object::sentinel().raw())) {
// The parser does not preset the value if the type is a type parameter or
// is parameterized unless the value is null.
Error& error = Error::Handle(Z);
@@ -1371,7 +1372,8 @@
} else {
ASSERT(type.IsInstantiated());
}
- const Instance& const_value = Instance::Handle(Z, field.value());
+ const Instance& const_value =
+ Instance::Handle(Z, field.StaticValue());
if (!error.IsNull() ||
(!type.IsDynamicType() &&
!const_value.IsInstanceOf(type,
@@ -1413,7 +1415,7 @@
getter.set_result_type(type);
getter.set_is_debuggable(false);
cls.AddFunction(getter);
- field.set_value(Object::sentinel());
+ field.SetStaticValue(Object::sentinel(), true);
}
}
}
@@ -2431,8 +2433,9 @@
const Field& values_field =
Field::Handle(enum_cls.LookupStaticField(Symbols::Values()));
ASSERT(!values_field.IsNull());
- ASSERT(Instance::Handle(values_field.value()).IsArray());
- Array& values_list = Array::Handle(Array::RawCast(values_field.value()));
+ ASSERT(Instance::Handle(values_field.StaticValue()).IsArray());
+ Array& values_list = Array::Handle(
+ Array::RawCast(values_field.StaticValue()));
const Array& fields = Array::Handle(enum_cls.fields());
Field& field = Field::Handle();
@@ -2442,7 +2445,7 @@
for (intptr_t i = 0; i < fields.Length(); i++) {
field = Field::RawCast(fields.At(i));
if (!field.is_static()) continue;
- ordinal_value = field.value();
+ ordinal_value = field.StaticValue();
// The static fields that need to be initialized with enum instances
// contain the smi value of the ordinal number, which was stored in
// the field by the parser. Other fields contain non-smi values.
@@ -2456,7 +2459,7 @@
UNREACHABLE();
}
ASSERT(enum_value.IsCanonical());
- field.set_value(enum_value);
+ field.SetStaticValue(enum_value, true);
field.RecordStore(enum_value);
intptr_t ord = Smi::Cast(ordinal_value).Value();
ASSERT(ord < values_list.Length());
diff --git a/runtime/vm/compiler.cc b/runtime/vm/compiler.cc
index 8b96530..b02e904 100644
--- a/runtime/vm/compiler.cc
+++ b/runtime/vm/compiler.cc
@@ -724,26 +724,26 @@
// FinalizeCode.
const Array& deopt_info_array =
Array::Handle(zone, graph_compiler.CreateDeoptInfo(&assembler));
- INC_STAT(isolate, total_code_size,
+ INC_STAT(thread, total_code_size,
deopt_info_array.Length() * sizeof(uword));
const Code& code = Code::Handle(
Code::FinalizeCode(function, &assembler, optimized));
code.set_is_optimized(optimized);
const Array& intervals = graph_compiler.inlined_code_intervals();
- INC_STAT(isolate, total_code_size,
+ INC_STAT(thread, total_code_size,
intervals.Length() * sizeof(uword));
code.SetInlinedIntervals(intervals);
const Array& inlined_id_array =
Array::Handle(zone, graph_compiler.InliningIdToFunction());
- INC_STAT(isolate, total_code_size,
+ INC_STAT(thread, total_code_size,
inlined_id_array.Length() * sizeof(uword));
code.SetInlinedIdToFunction(inlined_id_array);
const Array& caller_inlining_id_map_array =
Array::Handle(zone, graph_compiler.CallerInliningIdMap());
- INC_STAT(isolate, total_code_size,
+ INC_STAT(thread, total_code_size,
caller_inlining_id_map_array.Length() * sizeof(uword));
code.SetInlinedCallerIdMap(caller_inlining_id_map_array);
@@ -1046,9 +1046,18 @@
function.token_pos(),
(function.end_token_pos() - function.token_pos()));
}
+ INC_STAT(thread, num_functions_compiled, 1);
+ if (optimized) {
+ INC_STAT(thread, num_functions_optimized, 1);
+ }
{
HANDLESCOPE(thread);
+ const int64_t num_tokens_before = STAT_VALUE(thread, num_tokens_consumed);
pipeline->ParseFunction(parsed_function);
+ const int64_t num_tokens_after = STAT_VALUE(thread, num_tokens_consumed);
+ INC_STAT(thread,
+ num_func_tokens_compiled,
+ num_tokens_after - num_tokens_before);
}
const bool success = CompileParsedFunctionHelper(pipeline,
@@ -1277,23 +1286,15 @@
}
-static void CreateLocalVarDescriptors(const ParsedFunction& parsed_function) {
- const Function& func = parsed_function.function();
- LocalVarDescriptors& var_descs = LocalVarDescriptors::Handle();
- var_descs = parsed_function.node_sequence()->scope()->GetVarDescriptors(func);
- Code::Handle(func.unoptimized_code()).set_var_descriptors(var_descs);
-}
-
-
void Compiler::CompileStaticInitializer(const Field& field) {
ASSERT(field.is_static());
- if (field.initializer() != Function::null()) {
+ if (field.PrecompiledInitializer() != Function::null()) {
// TODO(rmacnak): Investigate why this happens for _enum_names.
OS::Print("Warning: Ignoring repeated request for initializer for %s\n",
field.ToCString());
return;
}
- ASSERT(field.initializer() == Function::null());
+ ASSERT(field.PrecompiledInitializer() == Function::null());
Thread* thread = Thread::Current();
StackZone zone(thread);
@@ -1308,7 +1309,7 @@
Isolate::kNoDeoptId);
const Function& initializer = parsed_function->function();
- field.set_initializer(initializer);
+ field.SetPrecompiledInitializer(initializer);
}
@@ -1316,16 +1317,15 @@
ASSERT(field.is_static());
// The VM sets the field's value to transiton_sentinel prior to
// evaluating the initializer value.
- ASSERT(field.value() == Object::transition_sentinel().raw());
+ ASSERT(field.StaticValue() == Object::transition_sentinel().raw());
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
- Function& initializer = Function::Handle(field.initializer());
-
// Under precompilation, the initializer may have already been compiled, in
// which case use it. Under lazy compilation or early in precompilation, the
// initializer has not yet been created, so create it now, but don't bother
// remembering it because it won't be used again.
- if (initializer.IsNull()) {
+ Function& initializer = Function::Handle();
+ if (!field.HasPrecompiledInitializer()) {
Thread* const thread = Thread::Current();
StackZone zone(thread);
ParsedFunction* parsed_function =
@@ -1338,15 +1338,12 @@
parsed_function,
false, // optimized
Isolate::kNoDeoptId);
- // Eagerly create local var descriptors.
- CreateLocalVarDescriptors(*parsed_function);
-
initializer = parsed_function->function().raw();
+ } else {
+ initializer ^= field.PrecompiledInitializer();
}
// Invoke the function to evaluate the expression.
- const Object& result = PassiveObject::Handle(
- DartEntry::InvokeFunction(initializer, Object::empty_array()));
- return result.raw();
+ return DartEntry::InvokeFunction(initializer, Object::empty_array());
} else {
Thread* const thread = Thread::Current();
Isolate* const isolate = thread->isolate();
@@ -1410,8 +1407,6 @@
false,
Isolate::kNoDeoptId);
- // Eagerly create local var descriptors.
- CreateLocalVarDescriptors(*parsed_function);
const Object& result = PassiveObject::Handle(
DartEntry::InvokeFunction(func, Object::empty_array()));
return result.raw();
diff --git a/runtime/vm/compiler_stats.cc b/runtime/vm/compiler_stats.cc
index 59d1dd4..e04c0c0 100644
--- a/runtime/vm/compiler_stats.cc
+++ b/runtime/vm/compiler_stats.cc
@@ -5,6 +5,7 @@
#include "vm/compiler_stats.h"
#include "vm/flags.h"
+#include "vm/log.h"
#include "vm/object_graph.h"
#include "vm/timer.h"
@@ -14,16 +15,17 @@
DEFINE_FLAG(bool, compiler_stats, false, "Compiler stat counters.");
-class TokenStreamVisitor : public ObjectGraph::Visitor {
+class TokenStreamVisitor : public ObjectVisitor {
public:
- explicit TokenStreamVisitor(CompilerStats* compiler_stats)
- : obj_(Object::Handle()), stats_(compiler_stats) {
+ TokenStreamVisitor(Isolate* isolate, CompilerStats* compiler_stats)
+ : ObjectVisitor(isolate),
+ obj_(Object::Handle()),
+ stats_(compiler_stats) {
}
- virtual Direction VisitObject(ObjectGraph::StackIterator* it) {
- RawObject* raw_obj = it->Get();
+ void VisitObject(RawObject* raw_obj) {
if (raw_obj->IsFreeListElement()) {
- return kProceed;
+ return;
}
obj_ = raw_obj;
if (obj_.GetClassId() == TokenStream::kClassId) {
@@ -42,7 +44,6 @@
kind = tkit.CurrentTokenKind();
}
}
- return kProceed;
}
private:
@@ -71,24 +72,46 @@
num_literal_tokens_total(0),
num_ident_tokens_total(0),
num_tokens_consumed(0),
- num_token_checks(0),
- num_tokens_lookahead(0),
num_cached_consts(0),
num_const_cache_hits(0),
- num_classes_compiled(0),
+ num_classes_parsed(0),
+ num_class_tokens(0),
+ num_functions_parsed(0),
num_functions_compiled(0),
+ num_functions_optimized(0),
+ num_func_tokens_compiled(0),
num_implicit_final_getters(0),
+ num_method_extractors(0),
src_length(0),
total_code_size(0),
total_instr_size(0),
pc_desc_size(0),
- vardesc_size(0) {
+ vardesc_size(0),
+ text(NULL) {
}
-void CompilerStats::Print() {
+// This function is used as a callback in the log object to which the
+// compiler stats are printed. It will be called only once, to print
+// the accumulated text when all of the compiler stats values are
+// added to the log.
+static void PrintToStats(const char* format, ...) PRINTF_ATTRIBUTE(1, 2);
+static void PrintToStats(const char* format, ...) {
+ Thread* thread = Thread::Current();
+ Isolate* isolate = thread->isolate();
+ CompilerStats* stats = isolate->compiler_stats();
+ Zone* zone = thread->zone();
+ ASSERT(stats != NULL);
+ va_list args;
+ va_start(args, format);
+ stats->text = zone->VPrint(format, args);
+ va_end(args);
+}
+
+
+char* CompilerStats::PrintToZone() {
if (!FLAG_compiler_stats) {
- return;
+ return NULL;
}
// Traverse the heap and compute number of tokens in all
@@ -96,85 +119,97 @@
num_tokens_total = 0;
num_literal_tokens_total = 0;
num_ident_tokens_total = 0;
- TokenStreamVisitor visitor(this);
- ObjectGraph graph(isolate_);
- graph.IterateObjects(&visitor);
+ TokenStreamVisitor visitor(isolate_, this);
+ isolate_->heap()->IterateObjects(&visitor);
+ Dart::vm_isolate()->heap()->IterateObjects(&visitor);
- OS::Print("==== Compiler Stats for isolate '%s' ====\n",
+ Log log(PrintToStats);
+ LogBlock lb(isolate_, &log);
+
+ log.Print("==== Compiler Stats for isolate '%s' ====\n",
isolate_->debugger_name());
- OS::Print("Number of tokens: %" Pd64 "\n", num_tokens_total);
- OS::Print(" Literal tokens: %" Pd64 "\n", num_literal_tokens_total);
- OS::Print(" Ident tokens: %" Pd64 "\n", num_ident_tokens_total);
- OS::Print("Tokens consumed: %" Pd64 " (%.2f times number of tokens)\n",
- num_tokens_consumed,
- (1.0 * num_tokens_consumed) / num_tokens_total);
- OS::Print("Tokens checked: %" Pd64 " (%.2f times tokens consumed)\n",
- num_token_checks, (1.0 * num_token_checks) / num_tokens_consumed);
- OS::Print("Token lookahead: %" Pd64 " (%" Pd64 "%% of tokens checked)\n",
- num_tokens_lookahead,
- (100 * num_tokens_lookahead) / num_token_checks);
- OS::Print("Consts cached: %" Pd64 "\n", num_cached_consts);
- OS::Print("Consts cache hits: %" Pd64 "\n", num_const_cache_hits);
+ log.Print("Number of tokens: %" Pd64 "\n", num_tokens_total);
+ log.Print(" Literal tokens: %" Pd64 "\n", num_literal_tokens_total);
+ log.Print(" Ident tokens: %" Pd64 "\n", num_ident_tokens_total);
+ log.Print("Source length: %" Pd64 " characters\n", src_length);
- OS::Print("Classes parsed: %" Pd64 "\n", num_classes_compiled);
- OS::Print("Functions compiled: %" Pd64 "\n", num_functions_compiled);
- OS::Print(" Impl getters: %" Pd64 "\n", num_implicit_final_getters);
+ log.Print("==== Parser stats:\n");
+ log.Print("Total tokens consumed: %" Pd64 "\n", num_tokens_consumed);
+ log.Print("Classes parsed: %" Pd64 "\n", num_classes_parsed);
+ log.Print(" Tokens consumed: %" Pd64 "\n", num_class_tokens);
+ log.Print("Functions parsed: %" Pd64 "\n", num_functions_parsed);
+ log.Print(" Tokens consumed: %" Pd64 "\n", num_func_tokens_compiled);
+ log.Print("Impl getter funcs: %" Pd64 "\n", num_implicit_final_getters);
+ log.Print("Impl method extractors: %" Pd64 "\n", num_method_extractors);
+ log.Print("Consts cached: %" Pd64 "\n", num_cached_consts);
+ log.Print("Consts cache hits: %" Pd64 "\n", num_const_cache_hits);
- OS::Print("Source length: %" Pd64 " characters\n", src_length);
int64_t scan_usecs = scanner_timer.TotalElapsedTime();
- OS::Print("Scanner time: %" Pd64 " msecs\n",
- scan_usecs / 1000);
+ log.Print("Scanner time: %" Pd64 " msecs\n", scan_usecs / 1000);
int64_t parse_usecs = parser_timer.TotalElapsedTime();
- OS::Print("Parser time: %" Pd64 " msecs\n",
- parse_usecs / 1000);
+ log.Print("Parser time: %" Pd64 " msecs\n", parse_usecs / 1000);
+ log.Print("Parser speed: %" Pd64 " tokens per msec\n",
+ 1000 * num_tokens_consumed / parse_usecs);
int64_t codegen_usecs = codegen_timer.TotalElapsedTime();
- OS::Print("Code gen. time: %" Pd64 " msecs\n",
+
+ log.Print("==== Backend stats:\n");
+ log.Print("Code gen. time: %" Pd64 " msecs\n",
codegen_usecs / 1000);
int64_t graphbuilder_usecs = graphbuilder_timer.TotalElapsedTime();
- OS::Print(" Graph builder: %" Pd64 " msecs\n", graphbuilder_usecs / 1000);
+ log.Print(" Graph builder: %" Pd64 " msecs\n",
+ graphbuilder_usecs / 1000);
int64_t ssa_usecs = ssa_timer.TotalElapsedTime();
- OS::Print(" Graph SSA: %" Pd64 " msecs\n", ssa_usecs / 1000);
+ log.Print(" Graph SSA: %" Pd64 " msecs\n", ssa_usecs / 1000);
int64_t graphinliner_usecs = graphinliner_timer.TotalElapsedTime();
- OS::Print(" Graph inliner: %" Pd64 " msecs\n", graphinliner_usecs / 1000);
+ log.Print(" Graph inliner: %" Pd64 " msecs\n",
+ graphinliner_usecs / 1000);
int64_t graphinliner_parse_usecs =
graphinliner_parse_timer.TotalElapsedTime();
- OS::Print(" Parsing: %" Pd64 " msecs\n",
+ log.Print(" Parsing: %" Pd64 " msecs\n",
graphinliner_parse_usecs / 1000);
int64_t graphinliner_build_usecs =
graphinliner_build_timer.TotalElapsedTime();
- OS::Print(" Building: %" Pd64 " msecs\n",
+ log.Print(" Building: %" Pd64 " msecs\n",
graphinliner_build_usecs / 1000);
int64_t graphinliner_ssa_usecs = graphinliner_ssa_timer.TotalElapsedTime();
- OS::Print(" SSA: %" Pd64 " msecs\n",
+ log.Print(" SSA: %" Pd64 " msecs\n",
graphinliner_ssa_usecs / 1000);
int64_t graphinliner_opt_usecs = graphinliner_opt_timer.TotalElapsedTime();
- OS::Print(" Optimization: %" Pd64 " msecs\n",
+ log.Print(" Optimization: %" Pd64 " msecs\n",
graphinliner_opt_usecs / 1000);
int64_t graphinliner_subst_usecs =
graphinliner_subst_timer.TotalElapsedTime();
- OS::Print(" Substitution: %" Pd64 " msecs\n",
+ log.Print(" Substitution: %" Pd64 " msecs\n",
graphinliner_subst_usecs / 1000);
-
int64_t graphoptimizer_usecs = graphoptimizer_timer.TotalElapsedTime();
- OS::Print(" Graph optimizer: %" Pd64 " msecs\n",
+ log.Print(" Graph optimizer: %" Pd64 " msecs\n",
(graphoptimizer_usecs - graphinliner_usecs) / 1000);
int64_t graphcompiler_usecs = graphcompiler_timer.TotalElapsedTime();
- OS::Print(" Graph compiler: %" Pd64 " msecs\n",
+ log.Print(" Graph compiler: %" Pd64 " msecs\n",
graphcompiler_usecs / 1000);
int64_t codefinalizer_usecs = codefinalizer_timer.TotalElapsedTime();
- OS::Print(" Code finalizer: %" Pd64 " msecs\n",
+ log.Print(" Code finalizer: %" Pd64 " msecs\n",
codefinalizer_usecs / 1000);
- OS::Print("Compilation speed: %" Pd64 " tokens per msec\n",
- (1000 * num_tokens_total) / (parse_usecs + codegen_usecs));
- OS::Print("Code density: %" Pd64 " tokens per KB\n",
- (num_tokens_total * 1024) / total_instr_size);
- OS::Print("Instr size: %" Pd64 " KB\n",
- total_instr_size / 1024);
- OS::Print("Pc Desc size: %" Pd64 " KB\n", pc_desc_size / 1024);
- OS::Print("VarDesc size: %" Pd64 " KB\n", vardesc_size / 1024);
- OS::Print("Code size: %" Pd64 " KB\n", total_code_size / 1024);
+ log.Print("==== Compiled code stats:\n");
+ log.Print("Functions parsed: %" Pd64 "\n", num_functions_parsed);
+ log.Print("Functions compiled: %" Pd64 "\n", num_functions_compiled);
+ log.Print(" optimized: %" Pd64 "\n", num_functions_optimized);
+ log.Print("Tokens compiled: %" Pd64 "\n", num_func_tokens_compiled);
+ log.Print("Compilation speed: %" Pd64 " tokens per msec\n",
+ (1000 * num_func_tokens_compiled) / (parse_usecs + codegen_usecs));
+ log.Print("Code density: %" Pd64 " tokens per KB\n",
+ (num_func_tokens_compiled * 1024) / total_instr_size);
+ log.Print("Code size: %" Pd64 " KB\n", total_code_size / 1024);
+ log.Print(" Instr size: %" Pd64 " KB\n",
+ total_instr_size / 1024);
+ log.Print(" Pc Desc size: %" Pd64 " KB\n", pc_desc_size / 1024);
+ log.Print(" VarDesc size: %" Pd64 " KB\n", vardesc_size / 1024);
+ log.Flush();
+ char* stats_text = text;
+ text = NULL;
+ return stats_text;
}
} // namespace dart
diff --git a/runtime/vm/compiler_stats.h b/runtime/vm/compiler_stats.h
index e6d4162..f0b47f4 100644
--- a/runtime/vm/compiler_stats.h
+++ b/runtime/vm/compiler_stats.h
@@ -11,13 +11,10 @@
#include "vm/timer.h"
-
namespace dart {
-DECLARE_FLAG(bool, compiler_stats);
-// TODO(hausner): Might want to expose some of these values in the
-// observatory. Use the metrics mechanism (metrics.h) for this.
+DECLARE_FLAG(bool, compiler_stats);
class CompilerStats {
public:
@@ -44,30 +41,40 @@
Timer graphcompiler_timer; // Included in codegen_timer.
Timer codefinalizer_timer; // Included in codegen_timer.
- int64_t num_tokens_total;
+ int64_t num_tokens_total; // Isolate + VM isolate
int64_t num_literal_tokens_total;
int64_t num_ident_tokens_total;
int64_t num_tokens_consumed;
- int64_t num_token_checks;
- int64_t num_tokens_lookahead;
int64_t num_cached_consts;
int64_t num_const_cache_hits;
- int64_t num_classes_compiled;
- int64_t num_functions_compiled;
+ int64_t num_classes_parsed;
+ int64_t num_class_tokens;
+ int64_t num_functions_parsed; // Num parsed functions.
+ int64_t num_functions_compiled; // Num unoptimized compilations.
+ int64_t num_functions_optimized; // Num optimized compilations.
+ int64_t num_func_tokens_compiled;
int64_t num_implicit_final_getters;
+ int64_t num_method_extractors;
int64_t src_length; // Total number of characters in source.
int64_t total_code_size; // Bytes allocated for code and meta info.
int64_t total_instr_size; // Total size of generated code in bytes.
int64_t pc_desc_size;
int64_t vardesc_size;
+ char* text;
- void Print();
+ char* PrintToZone();
};
-#define INC_STAT(isolate, counter, incr) \
- if (FLAG_compiler_stats) { (isolate)->compiler_stats()->counter += (incr); }
+// TODO(hausner): make the increment thread-safe.
+#define INC_STAT(thread, counter, incr) \
+ if (FLAG_compiler_stats) { \
+ (thread)->isolate()->compiler_stats()->counter += (incr); }
+
+#define STAT_VALUE(thread, counter) \
+ ((FLAG_compiler_stats != false) ? \
+ (thread)->isolate()->compiler_stats()->counter : 0)
#define CSTAT_TIMER_SCOPE(thr, t) \
TimerScope timer(FLAG_compiler_stats, \
diff --git a/runtime/vm/constant_propagator.cc b/runtime/vm/constant_propagator.cc
index 29818e1..5cf2018 100644
--- a/runtime/vm/constant_propagator.cc
+++ b/runtime/vm/constant_propagator.cc
@@ -706,7 +706,7 @@
void ConstantPropagator::VisitLoadStaticField(LoadStaticFieldInstr* instr) {
const Field& field = instr->StaticField();
ASSERT(field.is_static());
- Instance& obj = Instance::Handle(I, field.value());
+ Instance& obj = Instance::Handle(I, field.StaticValue());
if (field.is_final() && (obj.raw() != Object::sentinel().raw()) &&
(obj.raw() != Object::transition_sentinel().raw())) {
if (obj.IsSmi() || obj.IsOld()) {
diff --git a/runtime/vm/coverage.cc b/runtime/vm/coverage.cc
index 7892e37..d18ce19 100644
--- a/runtime/vm/coverage.cc
+++ b/runtime/vm/coverage.cc
@@ -103,32 +103,33 @@
if ((token_pos < begin_pos) || (token_pos > end_pos)) {
continue;
}
- intptr_t line = pos_to_line[token_pos];
-#if defined(DEBUG)
- const Script& script = Script::Handle(zone, function.script());
- intptr_t test_line = -1;
- script.GetTokenLocation(token_pos, &test_line, NULL);
- ASSERT(test_line == line);
-#endif
- // Merge hit data where possible.
- if (last_line == line) {
- last_count += ic_data->AggregateCount();
- } else {
- if ((last_line != -1) && !as_call_sites) {
- hits_or_sites.AddValue(last_line);
- hits_or_sites.AddValue(last_count);
- }
- last_count = ic_data->AggregateCount();
- last_line = line;
- }
if (as_call_sites) {
bool is_static_call = iter.Kind() == RawPcDescriptors::kUnoptStaticCall;
ic_data->PrintToJSONArray(hits_or_sites, token_pos, is_static_call);
+ } else {
+ intptr_t line = pos_to_line[token_pos];
+#if defined(DEBUG)
+ const Script& script = Script::Handle(zone, function.script());
+ intptr_t test_line = -1;
+ script.GetTokenLocation(token_pos, &test_line, NULL);
+ ASSERT(test_line == line);
+#endif
+ // Merge hit data where possible.
+ if (last_line == line) {
+ last_count += ic_data->AggregateCount();
+ } else {
+ if ((last_line != -1)) {
+ hits_or_sites.AddValue(last_line);
+ hits_or_sites.AddValue(last_count);
+ }
+ last_count = ic_data->AggregateCount();
+ last_line = line;
+ }
}
}
}
// Write last hit value if needed.
- if ((last_line != -1) && !as_call_sites) {
+ if (!as_call_sites && (last_line != -1)) {
hits_or_sites.AddValue(last_line);
hits_or_sites.AddValue(last_count);
}
@@ -164,7 +165,9 @@
i++;
continue;
}
- ComputeTokenPosToLineNumberMap(script, &pos_to_line);
+ if (!as_call_sites) {
+ ComputeTokenPosToLineNumberMap(script, &pos_to_line);
+ }
JSONObject jsobj(&jsarr);
jsobj.AddProperty("source", saved_url.ToCString());
jsobj.AddProperty("script", script);
@@ -185,10 +188,6 @@
continue;
}
CompileAndAdd(function, hits_or_sites, pos_to_line, as_call_sites);
- if (function.HasImplicitClosureFunction()) {
- function = function.ImplicitClosureFunction();
- CompileAndAdd(function, hits_or_sites, pos_to_line, as_call_sites);
- }
i++;
}
}
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index dcda02a..f3f1f52 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -4256,7 +4256,7 @@
return Api::NewHandle(I,
DartEntry::InvokeFunction(getter, Object::empty_array()));
} else if (!field.IsNull()) {
- return Api::NewHandle(I, field.value());
+ return Api::NewHandle(I, field.StaticValue());
} else {
return Api::NewError("%s: did not find static field '%s'.",
CURRENT_FUNC, field_name.ToCString());
@@ -4323,7 +4323,7 @@
DartEntry::InvokeFunction(getter, Object::empty_array()));
}
if (!field.IsNull()) {
- return Api::NewHandle(I, field.value());
+ return Api::NewHandle(I, field.StaticValue());
}
return Api::NewError("%s: did not find top-level variable '%s'.",
CURRENT_FUNC, field_name.ToCString());
@@ -4397,7 +4397,7 @@
return Api::NewError("%s: cannot set final field '%s'.",
CURRENT_FUNC, field_name.ToCString());
} else {
- field.set_value(value_instance);
+ field.SetStaticValue(value_instance);
return Api::Success();
}
} else {
@@ -4475,7 +4475,7 @@
return Api::NewError("%s: cannot set final top-level variable '%s'.",
CURRENT_FUNC, field_name.ToCString());
}
- field.set_value(value_instance);
+ field.SetStaticValue(value_instance);
return Api::Success();
}
return Api::NewError("%s: did not find top-level variable '%s'.",
@@ -5503,7 +5503,7 @@
const Field& dirty_bit = Field::Handle(Z,
libmirrors.LookupLocalField(String::Handle(String::New("dirty"))));
ASSERT(!dirty_bit.IsNull() && dirty_bit.is_static());
- dirty_bit.set_value(Bool::True());
+ dirty_bit.SetStaticValue(Bool::True());
if (complete_futures) {
const Library& corelib = Library::Handle(Z, Library::CoreLibrary());
diff --git a/runtime/vm/datastream.h b/runtime/vm/datastream.h
index bb46e4f..f47e822 100644
--- a/runtime/vm/datastream.h
+++ b/runtime/vm/datastream.h
@@ -10,6 +10,7 @@
#include "vm/allocation.h"
#include "vm/exceptions.h"
#include "vm/globals.h"
+#include "vm/os.h"
namespace dart {
@@ -386,6 +387,12 @@
current_ += len;
}
+ void Print(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ VPrint(format, args);
+ }
+
private:
template<typename T>
void Write(T value) {
@@ -426,6 +433,28 @@
ASSERT(end_ > *buffer_);
}
+ void VPrint(const char* format, va_list args) {
+ // Measure.
+ va_list measure_args;
+ va_copy(measure_args, args);
+ intptr_t len = OS::VSNPrint(NULL, 0, format, measure_args);
+ va_end(measure_args);
+
+ // Alloc.
+ if ((end_ - current_) < (len + 1)) {
+ Resize(len + 1);
+ }
+ ASSERT((end_ - current_) >= (len + 1));
+
+ // Print.
+ va_list print_args;
+ va_copy(print_args, args);
+ OS::VSNPrint(reinterpret_cast<char*>(current_),
+ len + 1, format, print_args);
+ va_end(print_args);
+ current_ += len; // Not len + 1 to swallow the terminating NUL.
+ }
+
private:
uint8_t** const buffer_;
uint8_t* end_;
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index 0cd16d8..96966d9 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -2138,7 +2138,7 @@
const Field& fld = Field::Handle(cls.LookupStaticField(field_name));
if (!fld.IsNull()) {
// Return the value in the field if it has been initialized already.
- const Instance& value = Instance::Handle(fld.value());
+ const Instance& value = Instance::Handle(fld.StaticValue());
ASSERT(value.raw() != Object::transition_sentinel().raw());
if (value.raw() != Object::sentinel().raw()) {
return value.raw();
@@ -2235,11 +2235,11 @@
// If the field is not initialized yet, report the value to be
// "<not initialized>". We don't want to execute the implicit getter
// since it may have side effects.
- if ((field.value() == Object::sentinel().raw()) ||
- (field.value() == Object::transition_sentinel().raw())) {
+ if ((field.StaticValue() == Object::sentinel().raw()) ||
+ (field.StaticValue() == Object::transition_sentinel().raw())) {
field_value = Symbols::NotInitialized().raw();
} else {
- field_value = field.value();
+ field_value = field.StaticValue();
}
if (!prefix.IsNull()) {
field_name = String::Concat(prefix, field_name);
diff --git a/runtime/vm/flow_graph_builder.cc b/runtime/vm/flow_graph_builder.cc
index 371ec0e..d9188c7 100644
--- a/runtime/vm/flow_graph_builder.cc
+++ b/runtime/vm/flow_graph_builder.cc
@@ -3804,10 +3804,11 @@
void EffectGraphVisitor::VisitLoadStaticFieldNode(LoadStaticFieldNode* node) {
if (node->field().is_const()) {
- ASSERT(node->field().value() != Object::sentinel().raw());
- ASSERT(node->field().value() != Object::transition_sentinel().raw());
- Definition* result =
- new(Z) ConstantInstr(Instance::ZoneHandle(Z, node->field().value()));
+ ASSERT(node->field().StaticValue() != Object::sentinel().raw());
+ ASSERT(node->field().StaticValue() !=
+ Object::transition_sentinel().raw());
+ Definition* result = new(Z) ConstantInstr(
+ Instance::ZoneHandle(Z, node->field().StaticValue()));
return ReturnDefinition(result);
}
Value* field_value = Bind(new(Z) ConstantInstr(node->field()));
diff --git a/runtime/vm/flow_graph_compiler.cc b/runtime/vm/flow_graph_compiler.cc
index 4a41786..c1d94b4 100644
--- a/runtime/vm/flow_graph_compiler.cc
+++ b/runtime/vm/flow_graph_compiler.cc
@@ -902,9 +902,12 @@
const ExceptionHandlers& handlers = ExceptionHandlers::Handle(
exception_handlers_list_->FinalizeExceptionHandlers(code.EntryPoint()));
code.set_exception_handlers(handlers);
- INC_STAT(isolate(), total_code_size,
- ExceptionHandlers::InstanceSize(handlers.num_entries()));
- INC_STAT(isolate(), total_code_size, handlers.num_entries() * sizeof(uword));
+ if (FLAG_compiler_stats) {
+ Thread* thread = Thread::Current();
+ INC_STAT(thread, total_code_size,
+ ExceptionHandlers::InstanceSize(handlers.num_entries()));
+ INC_STAT(thread, total_code_size, handlers.num_entries() * sizeof(uword));
+ }
}
@@ -1012,7 +1015,9 @@
}
}
code.set_static_calls_target_table(targets);
- INC_STAT(isolate(), total_code_size, targets.Length() * sizeof(uword));
+ INC_STAT(Thread::Current(),
+ total_code_size,
+ targets.Length() * sizeof(uword));
}
diff --git a/runtime/vm/flow_graph_type_propagator.cc b/runtime/vm/flow_graph_type_propagator.cc
index 6af05d6..c80005d 100644
--- a/runtime/vm/flow_graph_type_propagator.cc
+++ b/runtime/vm/flow_graph_type_propagator.cc
@@ -462,9 +462,10 @@
const AbstractType* compile_type = ToAbstractType();
const AbstractType* other_compile_type = other->ToAbstractType();
- if (compile_type->IsMoreSpecificThan(*other_compile_type, NULL)) {
+ if (compile_type->IsMoreSpecificThan(*other_compile_type, NULL, Heap::kOld)) {
type_ = other_compile_type;
- } else if (other_compile_type->IsMoreSpecificThan(*compile_type, NULL)) {
+ } else if (other_compile_type->
+ IsMoreSpecificThan(*compile_type, NULL, Heap::kOld)) {
// Nothing to do.
} else {
// Can't unify.
@@ -659,7 +660,7 @@
return false;
}
- *is_instance = compile_type.IsMoreSpecificThan(type, NULL);
+ *is_instance = compile_type.IsMoreSpecificThan(type, NULL, Heap::kOld);
return *is_instance;
}
@@ -674,7 +675,7 @@
return IsNull();
}
- return ToAbstractType()->IsMoreSpecificThan(other, NULL);
+ return ToAbstractType()->IsMoreSpecificThan(other, NULL, Heap::kOld);
}
@@ -753,6 +754,8 @@
// Set parameter types here in order to prevent unnecessary CheckClassInstr
// from being generated.
switch (index()) {
+ case RegExpMacroAssembler::kParamRegExpIndex:
+ return CompileType::FromCid(kJSRegExpCid);
case RegExpMacroAssembler::kParamStringIndex:
return CompileType::FromCid(function.string_specialization_cid());
case RegExpMacroAssembler::kParamStartOffsetIndex:
@@ -996,7 +999,7 @@
}
ASSERT(field.is_static());
if (field.is_final()) {
- const Instance& obj = Instance::Handle(field.value());
+ const Instance& obj = Instance::Handle(field.StaticValue());
if ((obj.raw() != Object::sentinel().raw()) &&
(obj.raw() != Object::transition_sentinel().raw()) &&
!obj.IsNull()) {
diff --git a/runtime/vm/intermediate_language.cc b/runtime/vm/intermediate_language.cc
index 7f4a862..8754ecf 100644
--- a/runtime/vm/intermediate_language.cc
+++ b/runtime/vm/intermediate_language.cc
@@ -383,8 +383,8 @@
Instruction* InitStaticFieldInstr::Canonicalize(FlowGraph* flow_graph) {
const bool is_initialized =
- (field_.value() != Object::sentinel().raw()) &&
- (field_.value() != Object::transition_sentinel().raw());
+ (field_.StaticValue() != Object::sentinel().raw()) &&
+ (field_.StaticValue() != Object::transition_sentinel().raw());
return is_initialized ? NULL : this;
}
@@ -398,8 +398,8 @@
LoadStaticFieldInstr* other_load = other->AsLoadStaticField();
ASSERT(other_load != NULL);
// Assert that the field is initialized.
- ASSERT(StaticField().value() != Object::sentinel().raw());
- ASSERT(StaticField().value() != Object::transition_sentinel().raw());
+ ASSERT(StaticField().StaticValue() != Object::sentinel().raw());
+ ASSERT(StaticField().StaticValue() != Object::transition_sentinel().raw());
return StaticField().raw() == other_load->StaticField().raw();
}
@@ -1683,13 +1683,15 @@
case Token::kSHL:
case Token::kSHR:
if (left.IsSmi() && right.IsSmi() && (Smi::Cast(right).Value() >= 0)) {
- result = Smi::Cast(left).ShiftOp(op_kind(), Smi::Cast(right));
+ result = Smi::Cast(left).ShiftOp(op_kind(),
+ Smi::Cast(right),
+ Heap::kOld);
}
break;
case Token::kBIT_AND:
case Token::kBIT_OR:
case Token::kBIT_XOR: {
- result = left.BitOp(op_kind(), right);
+ result = left.BitOp(op_kind(), right, Heap::kOld);
break;
}
case Token::kDIV:
@@ -3459,7 +3461,7 @@
pieces.SetAt(store_index, String::Cast(obj));
} else if (obj.IsSmi()) {
const char* cstr = obj.ToCString();
- pieces.SetAt(store_index, String::Handle(zone, String::New(cstr)));
+ pieces.SetAt(store_index, String::Handle(zone, Symbols::New(cstr)));
} else if (obj.IsBool()) {
pieces.SetAt(store_index,
Bool::Cast(obj).value() ? Symbols::True() : Symbols::False());
diff --git a/runtime/vm/intermediate_language_arm.cc b/runtime/vm/intermediate_language_arm.cc
index c816561..bc4ba3c 100644
--- a/runtime/vm/intermediate_language_arm.cc
+++ b/runtime/vm/intermediate_language_arm.cc
@@ -997,8 +997,7 @@
const Register char_code = locs()->in(0).reg();
const Register result = locs()->out(0).reg();
- ExternalLabel label(reinterpret_cast<uword>(Symbols::PredefinedAddress()));
- __ LoadExternalLabel(result, &label, kNotPatchable);
+ __ ldr(result, Address(THR, Thread::predefined_symbols_address_offset()));
__ AddImmediate(result, Symbols::kNullCharCodeSymbolOffset * kWordSize);
__ ldr(result, Address(result, char_code, LSL, 1)); // Char code is a smi.
}
@@ -2244,7 +2243,7 @@
void LoadStaticFieldInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
const Register field = locs()->in(0).reg();
const Register result = locs()->out(0).reg();
- __ LoadFieldFromOffset(kWord, result, field, Field::value_offset());
+ __ LoadFieldFromOffset(kWord, result, field, Field::static_value_offset());
}
@@ -2266,10 +2265,12 @@
__ LoadObject(temp, field());
if (this->value()->NeedsStoreBuffer()) {
__ StoreIntoObject(temp,
- FieldAddress(temp, Field::value_offset()), value, CanValueBeSmi());
+ FieldAddress(temp, Field::static_value_offset()),
+ value,
+ CanValueBeSmi());
} else {
__ StoreIntoObjectNoBarrier(
- temp, FieldAddress(temp, Field::value_offset()), value);
+ temp, FieldAddress(temp, Field::static_value_offset()), value);
}
}
@@ -2775,7 +2776,7 @@
Register temp = locs()->temp(0).reg();
Label call_runtime, no_call;
- __ ldr(temp, FieldAddress(field, Field::value_offset()));
+ __ ldr(temp, FieldAddress(field, Field::static_value_offset()));
__ CompareObject(temp, Object::sentinel());
__ b(&call_runtime, EQ);
diff --git a/runtime/vm/intermediate_language_arm64.cc b/runtime/vm/intermediate_language_arm64.cc
index 785d8f7..8c87b54 100644
--- a/runtime/vm/intermediate_language_arm64.cc
+++ b/runtime/vm/intermediate_language_arm64.cc
@@ -850,8 +850,7 @@
const Register char_code = locs()->in(0).reg();
const Register result = locs()->out(0).reg();
- ExternalLabel label(reinterpret_cast<uword>(Symbols::PredefinedAddress()));
- __ LoadExternalLabel(result, &label);
+ __ ldr(result, Address(THR, Thread::predefined_symbols_address_offset()));
__ AddImmediate(
result, result, Symbols::kNullCharCodeSymbolOffset * kWordSize);
__ SmiUntag(TMP, char_code); // Untag to use scaled adress mode.
@@ -1968,7 +1967,7 @@
void LoadStaticFieldInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
const Register field = locs()->in(0).reg();
const Register result = locs()->out(0).reg();
- __ LoadFieldFromOffset(result, field, Field::value_offset());
+ __ LoadFieldFromOffset(result, field, Field::static_value_offset());
}
@@ -1990,9 +1989,11 @@
__ LoadObject(temp, field());
if (this->value()->NeedsStoreBuffer()) {
__ StoreIntoObjectOffset(
- temp, Field::value_offset(), value, CanValueBeSmi());
+ temp, Field::static_value_offset(), value, CanValueBeSmi());
} else {
- __ StoreIntoObjectOffsetNoBarrier(temp, Field::value_offset(), value);
+ __ StoreIntoObjectOffsetNoBarrier(temp,
+ Field::static_value_offset(),
+ value);
}
}
@@ -2489,7 +2490,7 @@
Register temp = locs()->temp(0).reg();
Label call_runtime, no_call;
- __ ldr(temp, FieldAddress(field, Field::value_offset()));
+ __ ldr(temp, FieldAddress(field, Field::static_value_offset()));
__ CompareObject(temp, Object::sentinel());
__ b(&call_runtime, EQ);
diff --git a/runtime/vm/intermediate_language_ia32.cc b/runtime/vm/intermediate_language_ia32.cc
index af1185c1..35bc942 100644
--- a/runtime/vm/intermediate_language_ia32.cc
+++ b/runtime/vm/intermediate_language_ia32.cc
@@ -1971,7 +1971,7 @@
void LoadStaticFieldInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
Register field = locs()->in(0).reg();
Register result = locs()->out(0).reg();
- __ movl(result, FieldAddress(field, Field::value_offset()));
+ __ movl(result, FieldAddress(field, Field::static_value_offset()));
}
@@ -1993,10 +1993,12 @@
__ LoadObject(temp, field());
if (this->value()->NeedsStoreBuffer()) {
__ StoreIntoObject(temp,
- FieldAddress(temp, Field::value_offset()), value, CanValueBeSmi());
+ FieldAddress(temp, Field::static_value_offset()),
+ value,
+ CanValueBeSmi());
} else {
__ StoreIntoObjectNoBarrier(
- temp, FieldAddress(temp, Field::value_offset()), value);
+ temp, FieldAddress(temp, Field::static_value_offset()), value);
}
}
@@ -2493,7 +2495,7 @@
Label call_runtime, no_call;
- __ movl(temp, FieldAddress(field, Field::value_offset()));
+ __ movl(temp, FieldAddress(field, Field::static_value_offset()));
__ CompareObject(temp, Object::sentinel());
__ j(EQUAL, &call_runtime);
diff --git a/runtime/vm/intermediate_language_mips.cc b/runtime/vm/intermediate_language_mips.cc
index e888e84..eb7d932 100644
--- a/runtime/vm/intermediate_language_mips.cc
+++ b/runtime/vm/intermediate_language_mips.cc
@@ -1050,8 +1050,7 @@
__ Comment("StringFromCharCodeInstr");
- ExternalLabel label(reinterpret_cast<uword>(Symbols::PredefinedAddress()));
- __ LoadExternalLabel(result, &label, kNotPatchable);
+ __ lw(result, Address(THR, Thread::predefined_symbols_address_offset()));
__ AddImmediate(result, Symbols::kNullCharCodeSymbolOffset * kWordSize);
__ sll(TMP, char_code, 1); // Char code is a smi.
__ addu(TMP, TMP, result);
@@ -2092,7 +2091,9 @@
__ Comment("LoadStaticFieldInstr");
Register field = locs()->in(0).reg();
Register result = locs()->out(0).reg();
- __ LoadFromOffset(result, field, Field::value_offset() - kHeapObjectTag);
+ __ LoadFromOffset(result,
+ field,
+ Field::static_value_offset() - kHeapObjectTag);
}
@@ -2115,10 +2116,12 @@
__ LoadObject(temp, field());
if (this->value()->NeedsStoreBuffer()) {
__ StoreIntoObject(temp,
- FieldAddress(temp, Field::value_offset()), value, CanValueBeSmi());
+ FieldAddress(temp, Field::static_value_offset()),
+ value,
+ CanValueBeSmi());
} else {
__ StoreIntoObjectNoBarrier(
- temp, FieldAddress(temp, Field::value_offset()), value);
+ temp, FieldAddress(temp, Field::static_value_offset()), value);
}
}
@@ -2592,7 +2595,7 @@
Label call_runtime, no_call;
__ Comment("InitStaticFieldInstr");
- __ lw(temp, FieldAddress(field, Field::value_offset()));
+ __ lw(temp, FieldAddress(field, Field::static_value_offset()));
__ BranchEqual(temp, Object::sentinel(), &call_runtime);
__ BranchNotEqual(temp, Object::transition_sentinel(), &no_call);
diff --git a/runtime/vm/intermediate_language_x64.cc b/runtime/vm/intermediate_language_x64.cc
index 252bb13..4955df6 100644
--- a/runtime/vm/intermediate_language_x64.cc
+++ b/runtime/vm/intermediate_language_x64.cc
@@ -830,8 +830,7 @@
Register char_code = locs()->in(0).reg();
Register result = locs()->out(0).reg();
- ExternalLabel label(reinterpret_cast<uword>(Symbols::PredefinedAddress()));
- __ LoadExternalLabel(result, &label, kNotPatchable);
+ __ movq(result, Address(THR, Thread::predefined_symbols_address_offset()));
__ movq(result, Address(result,
char_code,
TIMES_HALF_WORD_SIZE, // Char code is a smi.
@@ -1973,7 +1972,7 @@
void LoadStaticFieldInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
Register field = locs()->in(0).reg();
Register result = locs()->out(0).reg();
- __ movq(result, FieldAddress(field, Field::value_offset()));
+ __ movq(result, FieldAddress(field, Field::static_value_offset()));
}
@@ -1995,10 +1994,12 @@
__ LoadObject(temp, field());
if (this->value()->NeedsStoreBuffer()) {
__ StoreIntoObject(temp,
- FieldAddress(temp, Field::value_offset()), value, CanValueBeSmi());
+ FieldAddress(temp, Field::static_value_offset()),
+ value,
+ CanValueBeSmi());
} else {
__ StoreIntoObjectNoBarrier(
- temp, FieldAddress(temp, Field::value_offset()), value);
+ temp, FieldAddress(temp, Field::static_value_offset()), value);
}
}
@@ -2489,7 +2490,7 @@
Label call_runtime, no_call;
- __ movq(temp, FieldAddress(field, Field::value_offset()));
+ __ movq(temp, FieldAddress(field, Field::static_value_offset()));
__ CompareObject(temp, Object::sentinel());
__ j(EQUAL, &call_runtime);
diff --git a/runtime/vm/intrinsifier_arm.cc b/runtime/vm/intrinsifier_arm.cc
index 4d37b8b..2618ef9 100644
--- a/runtime/vm/intrinsifier_arm.cc
+++ b/runtime/vm/intrinsifier_arm.cc
@@ -1536,14 +1536,16 @@
random_class.LookupStaticField(Symbols::_A()));
ASSERT(!random_A_field.IsNull());
ASSERT(random_A_field.is_const());
- const Instance& a_value = Instance::Handle(random_A_field.value());
+ const Instance& a_value = Instance::Handle(random_A_field.StaticValue());
const int64_t a_int_value = Integer::Cast(a_value).AsInt64Value();
// 'a_int_value' is a mask.
ASSERT(Utils::IsUint(32, a_int_value));
int32_t a_int32_value = static_cast<int32_t>(a_int_value);
- __ ldr(R0, Address(SP, 0 * kWordSize)); // Receiver.
- __ ldr(R1, FieldAddress(R0, state_field.Offset())); // Field '_state'.
+ // Receiver.
+ __ ldr(R0, Address(SP, 0 * kWordSize));
+ // Field '_state'.
+ __ ldr(R1, FieldAddress(R0, state_field.Offset()));
// Addresses of _state[0] and _state[1].
const int64_t disp_0 = Instance::DataOffsetFor(kTypedDataUint32ArrayCid);
@@ -1660,14 +1662,7 @@
__ ldrb(R1, Address(R0, R1));
__ CompareImmediate(R1, Symbols::kNumberOfOneCharCodeSymbols);
__ b(&fall_through, GE);
- const ExternalLabel symbols_label(
- reinterpret_cast<uword>(Symbols::PredefinedAddress()));
- __ Push(PP);
- __ LoadPoolPointer();
- assembler->set_constant_pool_allowed(true);
- __ LoadExternalLabel(R0, &symbols_label, kNotPatchable);
- assembler->set_constant_pool_allowed(false);
- __ Pop(PP);
+ __ ldr(R0, Address(THR, Thread::predefined_symbols_address_offset()));
__ AddImmediate(R0, Symbols::kNullCharCodeSymbolOffset * kWordSize);
__ ldr(R0, Address(R0, R1, LSL, 2));
__ Ret();
@@ -1680,12 +1675,7 @@
__ ldrh(R1, Address(R0, R1));
__ CompareImmediate(R1, Symbols::kNumberOfOneCharCodeSymbols);
__ b(&fall_through, GE);
- __ Push(PP);
- __ LoadPoolPointer();
- assembler->set_constant_pool_allowed(true);
- __ LoadExternalLabel(R0, &symbols_label, kNotPatchable);
- assembler->set_constant_pool_allowed(false);
- __ Pop(PP);
+ __ ldr(R0, Address(THR, Thread::predefined_symbols_address_offset()));
__ AddImmediate(R0, Symbols::kNullCharCodeSymbolOffset * kWordSize);
__ ldr(R0, Address(R0, R1, LSL, 2));
__ Ret();
@@ -2022,8 +2012,6 @@
// Registers are now set up for the lazy compile stub. It expects the function
// in R0, the argument descriptor in R4, and IC-Data in R5.
- static const intptr_t arg_count = RegExpMacroAssembler::kParamCount;
- __ LoadObject(R4, Array::Handle(ArgumentsDescriptor::New(arg_count)));
__ eor(R5, R5, Operand(R5));
// Tail-call the function.
diff --git a/runtime/vm/intrinsifier_arm64.cc b/runtime/vm/intrinsifier_arm64.cc
index c8b4df7..4524a54 100644
--- a/runtime/vm/intrinsifier_arm64.cc
+++ b/runtime/vm/intrinsifier_arm64.cc
@@ -1617,11 +1617,13 @@
random_class.LookupStaticField(Symbols::_A()));
ASSERT(!random_A_field.IsNull());
ASSERT(random_A_field.is_const());
- const Instance& a_value = Instance::Handle(random_A_field.value());
+ const Instance& a_value = Instance::Handle(random_A_field.StaticValue());
const int64_t a_int_value = Integer::Cast(a_value).AsInt64Value();
- __ ldr(R0, Address(SP, 0 * kWordSize)); // Receiver.
- __ ldr(R1, FieldAddress(R0, state_field.Offset())); // Field '_state'.
+ // Receiver.
+ __ ldr(R0, Address(SP, 0 * kWordSize));
+ // Field '_state'.
+ __ ldr(R1, FieldAddress(R0, state_field.Offset()));
// Addresses of _state[0].
const int64_t disp =
@@ -1736,12 +1738,7 @@
__ ldr(R1, Address(R0, R1), kUnsignedByte);
__ CompareImmediate(R1, Symbols::kNumberOfOneCharCodeSymbols);
__ b(&fall_through, GE);
- const ExternalLabel symbols_label(
- reinterpret_cast<uword>(Symbols::PredefinedAddress()));
- __ TagAndPushPP();
- __ LoadPoolPointer();
- __ LoadExternalLabel(R0, &symbols_label);
- __ PopAndUntagPP();
+ __ ldr(R0, Address(THR, Thread::predefined_symbols_address_offset()));
__ AddImmediate(
R0, R0, Symbols::kNullCharCodeSymbolOffset * kWordSize);
__ ldr(R0, Address(R0, R1, UXTX, Address::Scaled));
@@ -1755,10 +1752,7 @@
__ ldr(R1, Address(R0, R1), kUnsignedHalfword);
__ CompareImmediate(R1, Symbols::kNumberOfOneCharCodeSymbols);
__ b(&fall_through, GE);
- __ TagAndPushPP();
- __ LoadPoolPointer();
- __ LoadExternalLabel(R0, &symbols_label);
- __ PopAndUntagPP();
+ __ ldr(R0, Address(THR, Thread::predefined_symbols_address_offset()));
__ AddImmediate(
R0, R0, Symbols::kNullCharCodeSymbolOffset * kWordSize);
__ ldr(R0, Address(R0, R1, UXTX, Address::Scaled));
@@ -2097,8 +2091,6 @@
// Registers are now set up for the lazy compile stub. It expects the function
// in R0, the argument descriptor in R4, and IC-Data in R5.
- static const intptr_t arg_count = RegExpMacroAssembler::kParamCount;
- __ LoadObject(R4, Array::Handle(ArgumentsDescriptor::New(arg_count)));
__ eor(R5, R5, Operand(R5));
// Tail-call the function.
diff --git a/runtime/vm/intrinsifier_ia32.cc b/runtime/vm/intrinsifier_ia32.cc
index 1814d20..d39f3b6 100644
--- a/runtime/vm/intrinsifier_ia32.cc
+++ b/runtime/vm/intrinsifier_ia32.cc
@@ -1635,13 +1635,15 @@
random_class.LookupStaticField(Symbols::_A()));
ASSERT(!random_A_field.IsNull());
ASSERT(random_A_field.is_const());
- const Instance& a_value = Instance::Handle(random_A_field.value());
+ const Instance& a_value = Instance::Handle(random_A_field.StaticValue());
const int64_t a_int_value = Integer::Cast(a_value).AsInt64Value();
// 'a_int_value' is a mask.
ASSERT(Utils::IsUint(32, a_int_value));
int32_t a_int32_value = static_cast<int32_t>(a_int_value);
- __ movl(EAX, Address(ESP, + 1 * kWordSize)); // Receiver.
- __ movl(EBX, FieldAddress(EAX, state_field.Offset())); // Field '_state'.
+ // Receiver.
+ __ movl(EAX, Address(ESP, + 1 * kWordSize));
+ // Field '_state'.
+ __ movl(EBX, FieldAddress(EAX, state_field.Offset()));
// Addresses of _state[0] and _state[1].
const intptr_t scale = Instance::ElementSizeFor(kTypedDataUint32ArrayCid);
const intptr_t offset = Instance::DataOffsetFor(kTypedDataUint32ArrayCid);
@@ -2108,8 +2110,6 @@
// Registers are now set up for the lazy compile stub. It expects the function
// in EAX, the argument descriptor in EDX, and IC-Data in ECX.
- static const intptr_t arg_count = RegExpMacroAssembler::kParamCount;
- __ LoadObject(EDX, Array::ZoneHandle(ArgumentsDescriptor::New(arg_count)));
__ xorl(ECX, ECX);
// Tail-call the function.
diff --git a/runtime/vm/intrinsifier_mips.cc b/runtime/vm/intrinsifier_mips.cc
index 73d9ccd..d5df533 100644
--- a/runtime/vm/intrinsifier_mips.cc
+++ b/runtime/vm/intrinsifier_mips.cc
@@ -1634,14 +1634,16 @@
random_class.LookupStaticField(Symbols::_A()));
ASSERT(!random_A_field.IsNull());
ASSERT(random_A_field.is_const());
- const Instance& a_value = Instance::Handle(random_A_field.value());
+ const Instance& a_value = Instance::Handle(random_A_field.StaticValue());
const int64_t a_int_value = Integer::Cast(a_value).AsInt64Value();
// 'a_int_value' is a mask.
ASSERT(Utils::IsUint(32, a_int_value));
int32_t a_int32_value = static_cast<int32_t>(a_int_value);
- __ lw(T0, Address(SP, 0 * kWordSize)); // Receiver.
- __ lw(T1, FieldAddress(T0, state_field.Offset())); // Field '_state'.
+ // Receiver.
+ __ lw(T0, Address(SP, 0 * kWordSize));
+ // Field '_state'.
+ __ lw(T1, FieldAddress(T0, state_field.Offset()));
// Addresses of _state[0] and _state[1].
const intptr_t scale = Instance::ElementSizeFor(kTypedDataUint32ArrayCid);
@@ -1767,16 +1769,7 @@
__ lbu(T2, FieldAddress(T2, OneByteString::data_offset()));
__ BranchUnsignedGreaterEqual(
T2, Immediate(Symbols::kNumberOfOneCharCodeSymbols), &fall_through);
- const ExternalLabel symbols_label(
- reinterpret_cast<uword>(Symbols::PredefinedAddress()));
- __ Push(PP);
- __ Push(RA);
- __ LoadPoolPointer();
- assembler->set_constant_pool_allowed(true);
- __ LoadExternalLabel(V0, &symbols_label, kNotPatchable);
- assembler->set_constant_pool_allowed(false);
- __ Pop(RA);
- __ Pop(PP);
+ __ lw(V0, Address(THR, Thread::predefined_symbols_address_offset()));
__ AddImmediate(V0, Symbols::kNullCharCodeSymbolOffset * kWordSize);
__ sll(T2, T2, 2);
__ addu(T2, T2, V0);
@@ -1790,14 +1783,7 @@
__ lhu(T2, FieldAddress(T2, TwoByteString::data_offset()));
__ BranchUnsignedGreaterEqual(
T2, Immediate(Symbols::kNumberOfOneCharCodeSymbols), &fall_through);
- __ Push(PP);
- __ Push(RA);
- __ LoadPoolPointer();
- assembler->set_constant_pool_allowed(true);
- __ LoadExternalLabel(V0, &symbols_label, kNotPatchable);
- assembler->set_constant_pool_allowed(false);
- __ Pop(RA);
- __ Pop(PP);
+ __ lw(V0, Address(THR, Thread::predefined_symbols_address_offset()));
__ AddImmediate(V0, Symbols::kNullCharCodeSymbolOffset * kWordSize);
__ sll(T2, T2, 2);
__ addu(T2, T2, V0);
@@ -2136,8 +2122,6 @@
// Registers are now set up for the lazy compile stub. It expects the function
// in T0, the argument descriptor in S4, and IC-Data in S5.
- static const intptr_t arg_count = RegExpMacroAssembler::kParamCount;
- __ LoadObject(S4, Array::Handle(ArgumentsDescriptor::New(arg_count)));
__ mov(S5, ZR);
// Tail-call the function.
diff --git a/runtime/vm/intrinsifier_x64.cc b/runtime/vm/intrinsifier_x64.cc
index b911a99..d71abc3 100644
--- a/runtime/vm/intrinsifier_x64.cc
+++ b/runtime/vm/intrinsifier_x64.cc
@@ -1489,10 +1489,12 @@
random_class.LookupStaticField(Symbols::_A()));
ASSERT(!random_A_field.IsNull());
ASSERT(random_A_field.is_const());
- const Instance& a_value = Instance::Handle(random_A_field.value());
+ const Instance& a_value = Instance::Handle(random_A_field.StaticValue());
const int64_t a_int_value = Integer::Cast(a_value).AsInt64Value();
- __ movq(RAX, Address(RSP, + 1 * kWordSize)); // Receiver.
- __ movq(RBX, FieldAddress(RAX, state_field.Offset())); // Field '_state'.
+ // Receiver.
+ __ movq(RAX, Address(RSP, + 1 * kWordSize));
+ // Field '_state'.
+ __ movq(RBX, FieldAddress(RAX, state_field.Offset()));
// Addresses of _state[0] and _state[1].
const intptr_t scale = Instance::ElementSizeFor(kTypedDataUint32ArrayCid);
const intptr_t offset = Instance::DataOffsetFor(kTypedDataUint32ArrayCid);
@@ -1608,14 +1610,7 @@
__ movzxb(RCX, FieldAddress(RAX, RCX, TIMES_1, OneByteString::data_offset()));
__ cmpq(RCX, Immediate(Symbols::kNumberOfOneCharCodeSymbols));
__ j(GREATER_EQUAL, &fall_through);
- const ExternalLabel symbols_label(
- reinterpret_cast<uword>(Symbols::PredefinedAddress()));
- __ pushq(PP);
- __ LoadPoolPointer();
- assembler->set_constant_pool_allowed(true);
- __ LoadExternalLabel(RAX, &symbols_label, kNotPatchable);
- assembler->set_constant_pool_allowed(false);
- __ popq(PP);
+ __ movq(RAX, Address(THR, Thread::predefined_symbols_address_offset()));
__ movq(RAX, Address(RAX,
RCX,
TIMES_8,
@@ -1629,12 +1624,7 @@
__ movzxw(RCX, FieldAddress(RAX, RCX, TIMES_1, OneByteString::data_offset()));
__ cmpq(RCX, Immediate(Symbols::kNumberOfOneCharCodeSymbols));
__ j(GREATER_EQUAL, &fall_through);
- __ pushq(PP);
- __ LoadPoolPointer();
- assembler->set_constant_pool_allowed(true);
- __ LoadExternalLabel(RAX, &symbols_label, kNotPatchable);
- assembler->set_constant_pool_allowed(false);
- __ popq(PP);
+ __ movq(RAX, Address(THR, Thread::predefined_symbols_address_offset()));
__ movq(RAX, Address(RAX,
RCX,
TIMES_8,
@@ -1970,9 +1960,6 @@
// Registers are now set up for the lazy compile stub. It expects the function
// in RAX, the argument descriptor in R10, and IC-Data in RCX.
- static const intptr_t arg_count = RegExpMacroAssembler::kParamCount;
- __ LoadObject(R10,
- Array::ZoneHandle(ArgumentsDescriptor::New(arg_count)));
__ xorq(RCX, RCX);
// Tail-call the function.
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index bb1fe76..06dfad2 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -1531,7 +1531,7 @@
NoSafepointScope no_safepoint_scope;
if (compiler_stats_ != NULL) {
- compiler_stats()->Print();
+ OS::Print("%s", compiler_stats()->PrintToZone());
}
// Notify exit listeners that this isolate is shutting down.
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 86d461a..107ad5d 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -1511,7 +1511,7 @@
field_name = Symbols::New("cid"#clazz); \
field = Field::New(field_name, true, false, true, false, cls, 0); \
value = Smi::New(k##clazz##Cid); \
- field.set_value(value); \
+ field.SetStaticValue(value, true); \
field.set_type(Type::Handle(Type::IntType())); \
cls.AddField(field); \
@@ -2120,7 +2120,8 @@
void Class::AddFunction(const Function& function) const {
const Array& arr = Array::Handle(functions());
- const Array& new_arr = Array::Handle(Array::Grow(arr, arr.Length() + 1));
+ const Array& new_arr =
+ Array::Handle(Array::Grow(arr, arr.Length() + 1, Heap::kOld));
new_arr.SetAt(arr.Length(), function);
StorePointer(&raw_ptr()->functions_, new_arr.raw());
// Add to hash table, if any.
@@ -2280,12 +2281,12 @@
GrowableObjectArray& closures =
GrowableObjectArray::Handle(raw_ptr()->closure_functions_);
if (closures.IsNull()) {
- closures = GrowableObjectArray::New(4);
+ closures = GrowableObjectArray::New(4, Heap::kOld);
StorePointer(&raw_ptr()->closure_functions_, closures.raw());
}
ASSERT(function.IsNonImplicitClosureFunction());
ASSERT(function.Owner() == this->raw());
- closures.Add(function);
+ closures.Add(function, Heap::kOld);
}
@@ -3585,7 +3586,7 @@
ASSERT(direct_subclasses.At(i) != subclass.raw());
}
#endif
- direct_subclasses.Add(subclass);
+ direct_subclasses.Add(subclass, Heap::kOld);
}
@@ -3734,7 +3735,8 @@
const TypeArguments& type_arguments,
const Class& other,
const TypeArguments& other_type_arguments,
- Error* bound_error) {
+ Error* bound_error,
+ Heap::Space space) {
// Use the thsi object as if it was the receiver of this method, but instead
// of recursing reset it to the super class and loop.
Isolate* isolate = Isolate::Current();
@@ -3791,7 +3793,8 @@
other_type_arguments,
from_index,
num_type_params,
- bound_error);
+ bound_error,
+ space);
}
const bool other_is_function_class = other.IsFunctionClass();
if (other.IsSignatureClass() || other_is_function_class) {
@@ -3808,7 +3811,8 @@
type_arguments,
other_fun,
other_type_arguments,
- bound_error);
+ bound_error,
+ space);
}
// Check if type S has a call() method of function type T.
Function& function =
@@ -3828,7 +3832,8 @@
type_arguments,
other_fun,
other_type_arguments,
- bound_error)) {
+ bound_error,
+ space)) {
return true;
}
}
@@ -3868,7 +3873,8 @@
// after the type arguments of the super type of this type.
// The index of the type parameters is adjusted upon finalization.
error = Error::null();
- interface_args = interface_args.InstantiateFrom(type_arguments, &error);
+ interface_args =
+ interface_args.InstantiateFrom(type_arguments, &error, NULL, space);
if (!error.IsNull()) {
// Return the first bound error to the caller if it requests it.
if ((bound_error != NULL) && bound_error->IsNull()) {
@@ -3881,7 +3887,8 @@
interface_args,
other,
other_type_arguments,
- bound_error)) {
+ bound_error,
+ space)) {
return true;
}
}
@@ -3906,13 +3913,15 @@
const TypeArguments& type_arguments,
const Class& other,
const TypeArguments& other_type_arguments,
- Error* bound_error) const {
+ Error* bound_error,
+ Heap::Space space) const {
return TypeTestNonRecursive(*this,
test_kind,
type_arguments,
other,
other_type_arguments,
- bound_error);
+ bound_error,
+ space);
}
@@ -4229,13 +4238,15 @@
const char* Class::ToCString() const {
- const char* format = "%s Class: %s";
+ const char* format = "%s %sClass: %s";
const Library& lib = Library::Handle(library());
const char* library_name = lib.IsNull() ? "" : lib.ToCString();
+ const char* patch_prefix = is_patch() ? "Patch " : "";
const char* class_name = String::Handle(Name()).ToCString();
- intptr_t len = OS::SNPrint(NULL, 0, format, library_name, class_name) + 1;
+ intptr_t len =
+ OS::SNPrint(NULL, 0, format, library_name, patch_prefix, class_name) + 1;
char* chars = Thread::Current()->zone()->Alloc<char>(len);
- OS::SNPrint(chars, len, format, library_name, class_name);
+ OS::SNPrint(chars, len, format, library_name, patch_prefix, class_name);
return chars;
}
@@ -4548,7 +4559,8 @@
const TypeArguments& other,
intptr_t from_index,
intptr_t len,
- Error* bound_error) const {
+ Error* bound_error,
+ Heap::Space space) const {
ASSERT(Length() >= (from_index + len));
ASSERT(!other.IsNull());
ASSERT(other.Length() >= (from_index + len));
@@ -4559,7 +4571,7 @@
ASSERT(!type.IsNull());
other_type = other.TypeAt(from_index + i);
ASSERT(!other_type.IsNull());
- if (!type.TypeTest(test_kind, other_type, bound_error)) {
+ if (!type.TypeTest(test_kind, other_type, bound_error, space)) {
return false;
}
}
@@ -6044,7 +6056,8 @@
// Check that this function's signature type is a subtype of the other
// function's signature type.
if (!TypeTest(kIsSubtypeOf, Object::null_type_arguments(),
- other, Object::null_type_arguments(), bound_error)) {
+ other, Object::null_type_arguments(), bound_error,
+ Heap::kOld)) {
// For more informative error reporting, use the location of the other
// function here, since the caller will use the location of this function.
*bound_error = LanguageError::NewFormatted(
@@ -6086,12 +6099,15 @@
const TypeArguments& type_arguments,
const Function& other,
const TypeArguments& other_type_arguments,
- Error* bound_error) const {
+ Error* bound_error,
+ Heap::Space space) const {
AbstractType& other_param_type =
AbstractType::Handle(other.ParameterTypeAt(other_parameter_position));
if (!other_param_type.IsInstantiated()) {
other_param_type = other_param_type.InstantiateFrom(other_type_arguments,
- bound_error);
+ bound_error,
+ NULL, // trail
+ space);
ASSERT((bound_error == NULL) || bound_error->IsNull());
}
if (other_param_type.IsDynamicType()) {
@@ -6100,20 +6116,21 @@
AbstractType& param_type =
AbstractType::Handle(ParameterTypeAt(parameter_position));
if (!param_type.IsInstantiated()) {
- param_type = param_type.InstantiateFrom(type_arguments, bound_error);
+ param_type = param_type.InstantiateFrom(
+ type_arguments, bound_error, NULL /*trail*/, space);
ASSERT((bound_error == NULL) || bound_error->IsNull());
}
if (param_type.IsDynamicType()) {
return test_kind == kIsSubtypeOf;
}
if (test_kind == kIsSubtypeOf) {
- if (!param_type.IsSubtypeOf(other_param_type, bound_error) &&
- !other_param_type.IsSubtypeOf(param_type, bound_error)) {
+ if (!param_type.IsSubtypeOf(other_param_type, bound_error, space) &&
+ !other_param_type.IsSubtypeOf(param_type, bound_error, space)) {
return false;
}
} else {
ASSERT(test_kind == kIsMoreSpecificThan);
- if (!param_type.IsMoreSpecificThan(other_param_type, bound_error)) {
+ if (!param_type.IsMoreSpecificThan(other_param_type, bound_error, space)) {
return false;
}
}
@@ -6125,7 +6142,8 @@
const TypeArguments& type_arguments,
const Function& other,
const TypeArguments& other_type_arguments,
- Error* bound_error) const {
+ Error* bound_error,
+ Heap::Space space) const {
const intptr_t num_fixed_params = num_fixed_parameters();
const intptr_t num_opt_pos_params = NumOptionalPositionalParameters();
const intptr_t num_opt_named_params = NumOptionalNamedParameters();
@@ -6183,7 +6201,8 @@
if (!TestParameterType(test_kind,
i + num_ignored_params, i + other_num_ignored_params,
type_arguments, other, other_type_arguments,
- bound_error)) {
+ bound_error,
+ space)) {
return false;
}
}
@@ -6214,7 +6233,8 @@
if (!TestParameterType(test_kind,
j, i,
type_arguments, other, other_type_arguments,
- bound_error)) {
+ bound_error,
+ space)) {
return false;
}
break;
@@ -6760,9 +6780,9 @@
tmp = cls.PrettyName();
}
}
- tmp = String::Concat(tmp, Symbols::Dot());
+ tmp = String::Concat(tmp, Symbols::Dot(), Heap::kOld);
const String& suffix = String::Handle(PrettyName());
- return String::Concat(tmp, suffix);
+ return String::Concat(tmp, suffix, Heap::kOld);
}
@@ -6874,7 +6894,7 @@
set_ic_data_array(Object::empty_array());
} else {
const Array& a = Array::Handle(Array::New(count, Heap::kOld));
- INC_STAT(Isolate::Current(), total_code_size, count * sizeof(uword));
+ INC_STAT(Thread::Current(), total_code_size, count * sizeof(uword));
count = 0;
for (intptr_t i = 0; i < deopt_id_to_ic_data.length(); i++) {
if (deopt_id_to_ic_data[i] != NULL) {
@@ -7182,7 +7202,7 @@
RawString* Field::GetterName(const String& field_name) {
- return Field::GetterSymbol(field_name);
+ return String::Concat(Symbols::GetterPrefix(), field_name);
}
@@ -7191,6 +7211,11 @@
}
+RawString* Field::LookupGetterSymbol(const String& field_name) {
+ return Symbols::LookupFromConcat(Symbols::GetterPrefix(), field_name);
+}
+
+
RawString* Field::SetterName(const String& field_name) {
return String::Concat(Symbols::SetterPrefix(), field_name);
}
@@ -7201,6 +7226,11 @@
}
+RawString* Field::LookupSetterSymbol(const String& field_name) {
+ return Symbols::LookupFromConcat(Symbols::SetterPrefix(), field_name);
+}
+
+
RawString* Field::NameFromGetter(const String& getter_name) {
return Symbols::New(getter_name, kGetterPrefixLength,
getter_name.Length() - kGetterPrefixLength);
@@ -7249,18 +7279,6 @@
}
-RawInstance* Field::value() const {
- ASSERT(is_static()); // Valid only for static dart fields.
- return raw_ptr()->value_;
-}
-
-
-void Field::set_value(const Instance& value) const {
- ASSERT(is_static()); // Valid only for static dart fields.
- StorePointer(&raw_ptr()->value_, value.raw());
-}
-
-
void Field::set_type(const AbstractType& value) const {
ASSERT(!value.IsNull());
StorePointer(&raw_ptr()->type_, value.raw());
@@ -7287,9 +7305,7 @@
const Field& result = Field::Handle(Field::New());
result.set_name(name);
result.set_is_static(is_static);
- if (is_static) {
- result.set_value(Object::null_instance());
- } else {
+ if (!is_static) {
result.SetOffset(0);
}
result.set_is_final(is_final);
@@ -7431,7 +7447,7 @@
return;
}
if (is_static()) {
- const Instance& valueObj = Instance::Handle(value());
+ const Instance& valueObj = Instance::Handle(StaticValue());
jsobj.AddProperty("staticValue", valueObj);
}
@@ -7460,6 +7476,7 @@
}
}
+
// Build a closure object that gets (or sets) the contents of a static
// field f and cache the closure in a newly created static field
// named #f (or #f= in case of a setter).
@@ -7477,7 +7494,8 @@
closure_field = field_owner.LookupStaticField(closure_name);
if (!closure_field.IsNull()) {
ASSERT(closure_field.is_static());
- const Instance& closure = Instance::Handle(closure_field.value());
+ const Instance& closure =
+ Instance::Handle(closure_field.StaticValue());
ASSERT(!closure.IsNull());
ASSERT(closure.IsClosure());
return closure.raw();
@@ -7510,7 +7528,7 @@
false, // is_reflectable
field_owner,
this->token_pos());
- closure_field.set_value(Instance::Cast(result));
+ closure_field.SetStaticValue(Instance::Cast(result), true);
closure_field.set_type(Type::Handle(Type::DynamicType()));
field_owner.AddField(closure_field);
@@ -7586,33 +7604,43 @@
bool Field::IsUninitialized() const {
- const Instance& value = Instance::Handle(raw_ptr()->value_);
+ const Instance& value = Instance::Handle(raw_ptr()->value_.static_value_);
ASSERT(value.raw() != Object::transition_sentinel().raw());
return value.raw() == Object::sentinel().raw();
}
-void Field::set_initializer(const Function& initializer) const {
- StorePointer(&raw_ptr()->initializer_, initializer.raw());
+void Field::SetPrecompiledInitializer(const Function& initializer) const {
+ StorePointer(&raw_ptr()->initializer_.precompiled_, initializer.raw());
+}
+
+
+bool Field::HasPrecompiledInitializer() const {
+ return raw_ptr()->initializer_.precompiled_->IsFunction();
+}
+
+
+void Field::SetSavedInitialStaticValue(const Instance& value) const {
+ StorePointer(&raw_ptr()->initializer_.saved_value_, value.raw());
}
void Field::EvaluateInitializer() const {
ASSERT(is_static());
- if (value() == Object::sentinel().raw()) {
- set_value(Object::transition_sentinel());
+ if (StaticValue() == Object::sentinel().raw()) {
+ SetStaticValue(Object::transition_sentinel());
Object& value = Object::Handle(Compiler::EvaluateStaticInitializer(*this));
if (value.IsError()) {
- set_value(Object::null_instance());
+ SetStaticValue(Object::null_instance());
Exceptions::PropagateError(Error::Cast(value));
UNREACHABLE();
}
ASSERT(value.IsNull() || value.IsInstance());
- set_value(value.IsNull() ? Instance::null_instance()
+ SetStaticValue(value.IsNull() ? Instance::null_instance()
: Instance::Cast(value));
return;
- } else if (value() == Object::transition_sentinel().raw()) {
- set_value(Object::null_instance());
+ } else if (StaticValue() == Object::transition_sentinel().raw()) {
+ SetStaticValue(Object::null_instance());
const Array& ctor_args = Array::Handle(Array::New(1));
const String& field_name = String::Handle(name());
ctor_args.SetAt(0, field_name);
@@ -8564,7 +8592,6 @@
void Script::Tokenize(const String& private_key) const {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
- Isolate* isolate = thread->isolate();
const TokenStream& tkns = TokenStream::Handle(zone, tokens());
if (!tkns.IsNull()) {
// Already tokenized.
@@ -8578,7 +8605,7 @@
set_tokens(TokenStream::Handle(zone,
TokenStream::New(scanner.GetStream(),
private_key)));
- INC_STAT(isolate, src_length, src.Length());
+ INC_STAT(thread, src_length, src.Length());
}
@@ -9058,35 +9085,40 @@
static RawString* MakeClassMetaName(const Class& cls) {
- String& cname = String::Handle(cls.Name());
- return String::Concat(Symbols::At(), cname);
+ return Symbols::FromConcat(Symbols::At(), String::Handle(cls.Name()));
}
static RawString* MakeFieldMetaName(const Field& field) {
const String& cname =
String::Handle(MakeClassMetaName(Class::Handle(field.origin())));
- String& fname = String::Handle(field.name());
- fname = String::Concat(Symbols::At(), fname);
- return String::Concat(cname, fname);
+ GrowableHandlePtrArray<const String> pieces(Thread::Current()->zone(), 3);
+ pieces.Add(cname);
+ pieces.Add(Symbols::At());
+ pieces.Add(String::Handle(field.name()));
+ return Symbols::FromConcatAll(pieces);
}
static RawString* MakeFunctionMetaName(const Function& func) {
const String& cname =
String::Handle(MakeClassMetaName(Class::Handle(func.origin())));
- String& fname = String::Handle(func.QualifiedPrettyName());
- fname = String::Concat(Symbols::At(), fname);
- return String::Concat(cname, fname);
+ GrowableHandlePtrArray<const String> pieces(Thread::Current()->zone(), 3);
+ pieces.Add(cname);
+ pieces.Add(Symbols::At());
+ pieces.Add(String::Handle(func.QualifiedPrettyName()));
+ return Symbols::FromConcatAll(pieces);
}
static RawString* MakeTypeParameterMetaName(const TypeParameter& param) {
const String& cname = String::Handle(
MakeClassMetaName(Class::Handle(param.parameterized_class())));
- String& pname = String::Handle(param.name());
- pname = String::Concat(Symbols::At(), pname);
- return String::Concat(cname, pname);
+ GrowableHandlePtrArray<const String> pieces(Thread::Current()->zone(), 3);
+ pieces.Add(cname);
+ pieces.Add(Symbols::At());
+ pieces.Add(String::Handle(param.name()));
+ return Symbols::FromConcatAll(pieces);
}
@@ -9102,7 +9134,7 @@
cls,
token_pos));
field.set_type(Type::Handle(Type::DynamicType()));
- field.set_value(Array::empty_array());
+ field.SetStaticValue(Array::empty_array(), true);
GrowableObjectArray& metadata =
GrowableObjectArray::Handle(this->metadata());
metadata.Add(field, Heap::kOld);
@@ -9196,13 +9228,13 @@
return Object::empty_array().raw();
}
Object& metadata = Object::Handle();
- metadata = field.value();
- if (field.value() == Object::empty_array().raw()) {
+ metadata = field.StaticValue();
+ if (field.StaticValue() == Object::empty_array().raw()) {
metadata = Parser::ParseMetadata(Class::Handle(field.owner()),
field.token_pos());
if (metadata.IsArray()) {
ASSERT(Array::Cast(metadata).raw() != Object::empty_array().raw());
- field.set_value(Array::Cast(metadata));
+ field.SetStaticValue(Array::Cast(metadata), true);
}
}
return metadata.raw();
@@ -10611,7 +10643,7 @@
owner_class,
token_pos));
field.set_type(Type::Handle(Type::DynamicType()));
- field.set_value(Array::empty_array());
+ field.SetStaticValue(Array::empty_array(), true);
set_metadata_field(field);
owner_class.AddField(field);
}
@@ -10624,13 +10656,13 @@
return Object::empty_array().raw();
}
Object& metadata = Object::Handle();
- metadata = field.value();
- if (field.value() == Object::empty_array().raw()) {
+ metadata = field.StaticValue();
+ if (field.StaticValue() == Object::empty_array().raw()) {
metadata = Parser::ParseMetadata(Class::Handle(field.owner()),
field.token_pos());
if (metadata.IsArray()) {
ASSERT(Array::Cast(metadata).raw() != Object::empty_array().raw());
- field.set_value(Array::Cast(metadata));
+ field.SetStaticValue(Array::Cast(metadata), true);
}
}
return metadata.raw();
@@ -10709,9 +10741,16 @@
if (!Field::IsGetterName(name) &&
!Field::IsSetterName(name) &&
(obj.IsNull() || obj.IsLibraryPrefix())) {
- obj = lib.LookupEntry(String::Handle(Field::GetterName(name)), &ignore);
+ const String& getter_name = String::Handle(Field::LookupGetterSymbol(name));
+ if (!getter_name.IsNull()) {
+ obj = lib.LookupEntry(getter_name, &ignore);
+ }
if (obj.IsNull()) {
- obj = lib.LookupEntry(String::Handle(Field::SetterName(name)), &ignore);
+ const String& setter_name =
+ String::Handle(Field::LookupSetterSymbol(name));
+ if (!setter_name.IsNull()) {
+ obj = lib.LookupEntry(setter_name, &ignore);
+ }
}
}
@@ -11040,10 +11079,6 @@
return "link native";
}
- if (addr == reinterpret_cast<uword>(Symbols::PredefinedAddress())) {
- return "predefined symbols";
- }
-
return "UNKNOWN";
}
@@ -11092,15 +11127,14 @@
RawPcDescriptors* PcDescriptors::New(GrowableArray<uint8_t>* data) {
ASSERT(Object::pc_descriptors_class() != Class::null());
Thread* thread = Thread::Current();
- Isolate* isolate = thread->isolate();
PcDescriptors& result = PcDescriptors::Handle(thread->zone());
{
uword size = PcDescriptors::InstanceSize(data->length());
RawObject* raw = Object::Allocate(PcDescriptors::kClassId,
size,
Heap::kOld);
- INC_STAT(isolate, total_code_size, size);
- INC_STAT(isolate, pc_desc_size, size);
+ INC_STAT(thread, total_code_size, size);
+ INC_STAT(thread, pc_desc_size, size);
NoSafepointScope no_safepoint;
result ^= raw;
result.SetLength(data->length());
@@ -11113,15 +11147,14 @@
RawPcDescriptors* PcDescriptors::New(intptr_t length) {
ASSERT(Object::pc_descriptors_class() != Class::null());
Thread* thread = Thread::Current();
- Isolate* isolate = thread->isolate();
PcDescriptors& result = PcDescriptors::Handle(thread->zone());
{
uword size = PcDescriptors::InstanceSize(length);
RawObject* raw = Object::Allocate(PcDescriptors::kClassId,
size,
Heap::kOld);
- INC_STAT(isolate, total_code_size, size);
- INC_STAT(isolate, pc_desc_size, size);
+ INC_STAT(thread, total_code_size, size);
+ INC_STAT(thread, pc_desc_size, size);
NoSafepointScope no_safepoint;
result ^= raw;
result.SetLength(length);
@@ -11571,8 +11604,8 @@
RawObject* raw = Object::Allocate(LocalVarDescriptors::kClassId,
size,
Heap::kOld);
- INC_STAT(Isolate::Current(), total_code_size, size);
- INC_STAT(Isolate::Current(), vardesc_size, size);
+ INC_STAT(Thread::Current(), total_code_size, size);
+ INC_STAT(Thread::Current(), vardesc_size, size);
NoSafepointScope no_safepoint;
result ^= raw;
result.StoreNonPointer(&result.raw_ptr()->num_entries_, num_variables);
@@ -12713,7 +12746,7 @@
void Code::set_stackmaps(const Array& maps) const {
ASSERT(maps.IsOld());
StorePointer(&raw_ptr()->stackmaps_, maps.raw());
- INC_STAT(Isolate::Current(),
+ INC_STAT(Thread::Current(),
total_code_size,
maps.IsNull() ? 0 : maps.Length() * sizeof(uword));
}
@@ -13017,8 +13050,8 @@
Code& code = Code::ZoneHandle(Code::New(pointer_offset_count));
Instructions& instrs =
Instructions::ZoneHandle(Instructions::New(assembler->CodeSize()));
- INC_STAT(isolate, total_instr_size, assembler->CodeSize());
- INC_STAT(isolate, total_code_size, assembler->CodeSize());
+ INC_STAT(Thread::Current(), total_instr_size, assembler->CodeSize());
+ INC_STAT(Thread::Current(), total_code_size, assembler->CodeSize());
// Copy the instructions into the instruction area and apply all fixups.
// Embedded pointers are still in handles at this point.
@@ -13058,7 +13091,7 @@
code.set_is_alive(true);
// Set object pool in Instructions object.
- INC_STAT(isolate,
+ INC_STAT(Thread::Current(),
total_code_size, object_pool.Length() * sizeof(uintptr_t));
instrs.set_object_pool(object_pool.raw());
@@ -13079,7 +13112,7 @@
// pushed onto the stack.
code.SetPrologueOffset(assembler->CodeSize());
}
- INC_STAT(isolate,
+ INC_STAT(Thread::Current(),
total_code_size, code.comments().comments_.Length());
return code.raw();
}
@@ -14531,7 +14564,7 @@
other_type_arguments = other.arguments();
}
return cls.IsSubtypeOf(type_arguments, other_class, other_type_arguments,
- bound_error);
+ bound_error, Heap::kOld);
}
@@ -15192,7 +15225,8 @@
bool AbstractType::TypeTest(TypeTestKind test_kind,
const AbstractType& other,
- Error* bound_error) const {
+ Error* bound_error,
+ Heap::Space space) const {
ASSERT(IsResolved());
ASSERT(other.IsResolved());
if (IsMalformed() || other.IsMalformed()) {
@@ -15261,7 +15295,8 @@
TypeArguments::Handle(arguments()),
Class::Handle(other.type_class()),
TypeArguments::Handle(other.arguments()),
- bound_error);
+ bound_error,
+ space);
}
@@ -16973,7 +17008,8 @@
}
-RawInteger* Integer::BitOp(Token::Kind kind, const Integer& other) const {
+RawInteger* Integer::BitOp(
+ Token::Kind kind, const Integer& other, Heap::Space space) const {
if (IsSmi() && other.IsSmi()) {
intptr_t op1_value = Smi::Value(Smi::RawCast(raw()));
intptr_t op2_value = Smi::Value(Smi::RawCast(other.raw()));
@@ -16998,11 +17034,11 @@
int64_t b = other.AsInt64Value();
switch (kind) {
case Token::kBIT_AND:
- return Integer::New(a & b);
+ return Integer::New(a & b, space);
case Token::kBIT_OR:
- return Integer::New(a | b);
+ return Integer::New(a | b, space);
case Token::kBIT_XOR:
- return Integer::New(a ^ b);
+ return Integer::New(a ^ b, space);
default:
UNIMPLEMENTED();
}
@@ -17014,6 +17050,7 @@
// TODO(srdjan): Clarify handling of negative right operand in a shift op.
RawInteger* Smi::ShiftOp(Token::Kind kind,
const Smi& other,
+ Heap::Space space,
const bool silent) const {
intptr_t result = 0;
const intptr_t left_value = Value();
@@ -17028,10 +17065,10 @@
int cnt = Utils::BitLength(left_value);
if ((cnt + right_value) > Smi::kBits) {
if ((cnt + right_value) > Mint::kBits) {
- return Bigint::NewFromShiftedInt64(left_value, right_value);
+ return Bigint::NewFromShiftedInt64(left_value, right_value, space);
} else {
int64_t left_64 = left_value;
- return Integer::New(left_64 << right_value, Heap::kNew, silent);
+ return Integer::New(left_64 << right_value, space, silent);
}
}
}
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 369f87a..9d1326e 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -1133,24 +1133,28 @@
bool IsSubtypeOf(const TypeArguments& type_arguments,
const Class& other,
const TypeArguments& other_type_arguments,
- Error* bound_error) const {
+ Error* bound_error,
+ Heap::Space space = Heap::kNew) const {
return TypeTest(kIsSubtypeOf,
type_arguments,
other,
other_type_arguments,
- bound_error);
+ bound_error,
+ space);
}
// Check the 'more specific' relationship.
bool IsMoreSpecificThan(const TypeArguments& type_arguments,
const Class& other,
const TypeArguments& other_type_arguments,
- Error* bound_error) const {
+ Error* bound_error,
+ Heap::Space space = Heap::kNew) const {
return TypeTest(kIsMoreSpecificThan,
type_arguments,
other,
other_type_arguments,
- bound_error);
+ bound_error,
+ space);
}
// Check if this is the top level class.
@@ -1490,7 +1494,8 @@
const TypeArguments& type_arguments,
const Class& other,
const TypeArguments& other_type_arguments,
- Error* bound_error) const;
+ Error* bound_error,
+ Heap::Space space) const;
static bool TypeTestNonRecursive(
const Class& cls,
@@ -1498,7 +1503,8 @@
const TypeArguments& type_arguments,
const Class& other,
const TypeArguments& other_type_arguments,
- Error* bound_error);
+ Error* bound_error,
+ Heap::Space space);
FINAL_HEAP_OBJECT_IMPLEMENTATION(Class, Object);
friend class AbstractType;
@@ -1591,8 +1597,9 @@
bool IsSubtypeOf(const TypeArguments& other,
intptr_t from_index,
intptr_t len,
- Error* bound_error) const {
- return TypeTest(kIsSubtypeOf, other, from_index, len, bound_error);
+ Error* bound_error,
+ Heap::Space space = Heap::kNew) const {
+ return TypeTest(kIsSubtypeOf, other, from_index, len, bound_error, space);
}
// Check the 'more specific' relationship, considering only a subvector of
@@ -1600,8 +1607,10 @@
bool IsMoreSpecificThan(const TypeArguments& other,
intptr_t from_index,
intptr_t len,
- Error* bound_error) const {
- return TypeTest(kIsMoreSpecificThan, other, from_index, len, bound_error);
+ Error* bound_error,
+ Heap::Space space = Heap::kNew) const {
+ return TypeTest(kIsMoreSpecificThan,
+ other, from_index, len, bound_error, space);
}
// Check if the vectors are equal (they may be null).
@@ -1713,7 +1722,8 @@
const TypeArguments& other,
intptr_t from_index,
intptr_t len,
- Error* bound_error) const;
+ Error* bound_error,
+ Heap::Space space) const;
// Return the internal or public name of a subvector of this type argument
// vector, e.g. "<T, dynamic, List<T>, int>".
@@ -2424,12 +2434,14 @@
bool IsSubtypeOf(const TypeArguments& type_arguments,
const Function& other,
const TypeArguments& other_type_arguments,
- Error* bound_error) const {
+ Error* bound_error,
+ Heap::Space space = Heap::kNew) const {
return TypeTest(kIsSubtypeOf,
type_arguments,
other,
other_type_arguments,
- bound_error);
+ bound_error,
+ space);
}
// Returns true if the type of this function is more specific than the type of
@@ -2437,12 +2449,14 @@
bool IsMoreSpecificThan(const TypeArguments& type_arguments,
const Function& other,
const TypeArguments& other_type_arguments,
- Error* bound_error) const {
+ Error* bound_error,
+ Heap::Space space = Heap::kNew) const {
return TypeTest(kIsMoreSpecificThan,
type_arguments,
other,
other_type_arguments,
- bound_error);
+ bound_error,
+ space);
}
// Returns true if this function represents an explicit getter function.
@@ -2727,7 +2741,8 @@
const TypeArguments& type_arguments,
const Function& other,
const TypeArguments& other_type_arguments,
- Error* bound_error) const;
+ Error* bound_error,
+ Heap::Space space) const;
// Checks the type of the formal parameter at the given position for
// subtyping or 'more specific' relationship between the type of this function
@@ -2738,7 +2753,8 @@
const TypeArguments& type_arguments,
const Function& other,
const TypeArguments& other_type_arguments,
- Error* bound_error) const;
+ Error* bound_error,
+ Heap::Space space) const;
FINAL_HEAP_OBJECT_IMPLEMENTATION(Function, Object);
friend class Class;
@@ -2831,10 +2847,11 @@
}
inline intptr_t Offset() const;
- inline void SetOffset(intptr_t value_in_bytes) const;
+ inline void SetOffset(intptr_t offset_in_bytes) const;
- RawInstance* value() const;
- void set_value(const Instance& value) const;
+ inline RawInstance* StaticValue() const;
+ inline void SetStaticValue(const Instance& value,
+ bool save_initial_value = false) const;
RawClass* owner() const;
RawClass* origin() const; // Either mixin class, or same as owner().
@@ -2858,7 +2875,12 @@
// owner of the clone is new_owner.
RawField* Clone(const Class& new_owner) const;
- static intptr_t value_offset() { return OFFSET_OF(RawField, value_); }
+ static intptr_t instance_field_offset() {
+ return OFFSET_OF(RawField, value_.offset_);
+ }
+ static intptr_t static_value_offset() {
+ return OFFSET_OF(RawField, value_.static_value_);
+ }
static intptr_t kind_bits_offset() { return OFFSET_OF(RawField, kind_bits_); }
@@ -2969,10 +2991,16 @@
void EvaluateInitializer() const;
- RawFunction* initializer() const {
- return raw_ptr()->initializer_;
+ RawFunction* PrecompiledInitializer() const {
+ return raw_ptr()->initializer_.precompiled_;
}
- void set_initializer(const Function& initializer) const;
+ void SetPrecompiledInitializer(const Function& initializer) const;
+ bool HasPrecompiledInitializer() const;
+
+ RawInstance* SavedInitialStaticValue() const {
+ return raw_ptr()->initializer_.saved_value_;
+ }
+ void SetSavedInitialStaticValue(const Instance& value) const;
// For static fields only. Constructs a closure that gets/sets the
// field value.
@@ -2983,8 +3011,12 @@
// Constructs getter and setter names for fields and vice versa.
static RawString* GetterName(const String& field_name);
static RawString* GetterSymbol(const String& field_name);
+ // Returns String::null() if getter symbol does not exist.
+ static RawString* LookupGetterSymbol(const String& field_name);
static RawString* SetterName(const String& field_name);
static RawString* SetterSymbol(const String& field_name);
+ // Returns String::null() if setter symbol does not exist.
+ static RawString* LookupSetterSymbol(const String& field_name);
static RawString* NameFromGetter(const String& getter_name);
static RawString* NameFromSetter(const String& setter_name);
static bool IsGetterName(const String& function_name);
@@ -3044,6 +3076,7 @@
FINAL_HEAP_OBJECT_IMPLEMENTATION(Field, Object);
friend class Class;
friend class HeapProfiler;
+ friend class RawField;
};
@@ -5115,21 +5148,25 @@
bool IsFunctionType() const;
// Check the subtype relationship.
- bool IsSubtypeOf(const AbstractType& other, Error* bound_error) const {
- return TypeTest(kIsSubtypeOf, other, bound_error);
+ bool IsSubtypeOf(const AbstractType& other,
+ Error* bound_error,
+ Heap::Space space = Heap::kNew) const {
+ return TypeTest(kIsSubtypeOf, other, bound_error, space);
}
// Check the 'more specific' relationship.
bool IsMoreSpecificThan(const AbstractType& other,
- Error* bound_error) const {
- return TypeTest(kIsMoreSpecificThan, other, bound_error);
+ Error* bound_error,
+ Heap::Space space = Heap::kNew) const {
+ return TypeTest(kIsMoreSpecificThan, other, bound_error, space);
}
private:
// Check the subtype or 'more specific' relationship.
bool TypeTest(TypeTestKind test_kind,
const AbstractType& other,
- Error* bound_error) const;
+ Error* bound_error,
+ Heap::Space space) const;
// Return the internal or public name of this type, including the names of its
// type arguments, if any.
@@ -5616,7 +5653,9 @@
RawInteger* ArithmeticOp(Token::Kind operation,
const Integer& other,
Heap::Space space = Heap::kNew) const;
- RawInteger* BitOp(Token::Kind operation, const Integer& other) const;
+ RawInteger* BitOp(Token::Kind operation,
+ const Integer& other,
+ Heap::Space space = Heap::kNew) const;
// Returns true if the Integer does not fit in a Javascript integer.
bool CheckJavascriptIntegerOverflow() const;
@@ -5677,6 +5716,7 @@
RawInteger* ShiftOp(Token::Kind kind,
const Smi& other,
+ Heap::Space space = Heap::kNew,
const bool silent = false) const;
void operator=(RawSmi* value) {
@@ -7968,17 +8008,33 @@
intptr_t Field::Offset() const {
- ASSERT(!is_static()); // Offset is valid only for instance fields.
- intptr_t value = Smi::Value(reinterpret_cast<RawSmi*>(raw_ptr()->value_));
+ ASSERT(!is_static()); // Valid only for dart instance fields.
+ intptr_t value = Smi::Value(raw_ptr()->value_.offset_);
return (value * kWordSize);
}
-void Field::SetOffset(intptr_t value_in_bytes) const {
- ASSERT(!is_static()); // SetOffset is valid only for instance fields.
+void Field::SetOffset(intptr_t offset_in_bytes) const {
+ ASSERT(!is_static()); // Valid only for dart instance fields.
ASSERT(kWordSize != 0);
- StorePointer(&raw_ptr()->value_,
- static_cast<RawInstance*>(Smi::New(value_in_bytes / kWordSize)));
+ StorePointer(&raw_ptr()->value_.offset_,
+ Smi::New(offset_in_bytes / kWordSize));
+}
+
+
+RawInstance* Field::StaticValue() const {
+ ASSERT(is_static()); // Valid only for static dart fields.
+ return raw_ptr()->value_.static_value_;
+}
+
+
+void Field::SetStaticValue(const Instance& value,
+ bool save_initial_value) const {
+ ASSERT(is_static()); // Valid only for static dart fields.
+ StorePointer(&raw_ptr()->value_.static_value_, value.raw());
+ if (save_initial_value) {
+ StorePointer(&raw_ptr()->initializer_.saved_value_, value.raw());
+ }
}
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 5e006a0..692a03b 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -4779,6 +4779,14 @@
const String* data[3] = { &str1, &Symbols::Dot(), &str2 };
CheckConcatAll(data, 3);
}
+
+ {
+ const String& empty = String::Handle(String::New(""));
+ const String* data[3] = { &Symbols::FallThroughError(),
+ &empty,
+ &Symbols::isPaused() };
+ CheckConcatAll(data, 3);
+ }
}
} // namespace dart
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index 604e453..d837830 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -466,8 +466,6 @@
Token::Kind Parser::LookaheadToken(int num_tokens) {
- INC_STAT(I, num_tokens_lookahead, 1);
- INC_STAT(I, num_token_checks, 1);
return tokens_iterator_.LookaheadTokenKind(num_tokens);
}
@@ -804,25 +802,26 @@
void Parser::ParseClass(const Class& cls) {
+ Thread* thread = Thread::Current();
+ Zone* zone = thread->zone();
+ const int64_t num_tokes_before = STAT_VALUE(thread, num_tokens_consumed);
if (!cls.is_synthesized_class()) {
- Thread* thread = Thread::Current();
- Zone* zone = thread->zone();
- CSTAT_TIMER_SCOPE(thread, parser_timer);
ASSERT(thread->long_jump_base()->IsSafeToJump());
+ CSTAT_TIMER_SCOPE(thread, parser_timer);
const Script& script = Script::Handle(zone, cls.script());
const Library& lib = Library::Handle(zone, cls.library());
Parser parser(script, lib, cls.token_pos());
parser.ParseClassDefinition(cls);
} else if (cls.is_enum_class()) {
- Thread* thread = Thread::Current();
- Zone* zone = thread->zone();
- CSTAT_TIMER_SCOPE(thread, parser_timer);
ASSERT(thread->long_jump_base()->IsSafeToJump());
+ CSTAT_TIMER_SCOPE(thread, parser_timer);
const Script& script = Script::Handle(zone, cls.script());
const Library& lib = Library::Handle(zone, cls.library());
Parser parser(script, lib, cls.token_pos());
parser.ParseEnumDefinition(cls);
}
+ const int64_t num_tokes_after = STAT_VALUE(thread, num_tokens_consumed);
+ INC_STAT(thread, num_class_tokens, num_tokes_after - num_tokes_before);
}
@@ -909,10 +908,9 @@
void Parser::ParseFunction(ParsedFunction* parsed_function) {
Thread* thread = parsed_function->thread();
ASSERT(thread == Thread::Current());
- Isolate* isolate = thread->isolate();
Zone* zone = thread->zone();
CSTAT_TIMER_SCOPE(thread, parser_timer);
- INC_STAT(isolate, num_functions_compiled, 1);
+ INC_STAT(thread, num_functions_parsed, 1);
VMTagScope tagScope(thread, VMTag::kCompileParseFunctionTagId,
FLAG_profile_vm);
@@ -956,10 +954,11 @@
break;
case RawFunction::kImplicitStaticFinalGetter:
node_sequence = parser.ParseStaticFinalGetter(func);
- INC_STAT(isolate, num_implicit_final_getters, 1);
+ INC_STAT(thread, num_implicit_final_getters, 1);
break;
case RawFunction::kMethodExtractor:
node_sequence = parser.ParseMethodExtractor(func);
+ INC_STAT(thread, num_method_extractors, 1);
break;
case RawFunction::kNoSuchMethodDispatcher:
node_sequence =
@@ -1051,7 +1050,7 @@
RawArray* Parser::EvaluateMetadata() {
CheckToken(Token::kAT, "Metadata character '@' expected");
GrowableObjectArray& meta_values =
- GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
+ GrowableObjectArray::Handle(Z, GrowableObjectArray::New(Heap::kOld));
while (CurrentToken() == Token::kAT) {
ConsumeToken();
intptr_t expr_pos = TokenPos();
@@ -1120,7 +1119,7 @@
ReportError(expr_pos, "expression must be a compile-time constant");
}
const Instance& val = EvaluateConstExpr(expr_pos, expr);
- meta_values.Add(val);
+ meta_values.Add(val, Heap::kOld);
}
return Array::MakeArray(meta_values);
}
@@ -1152,15 +1151,21 @@
String& init_name = String::Handle(zone,
Symbols::FromConcat(Symbols::InitPrefix(), field_name));
+ Object& initializer_owner = Object::Handle(field.owner());
+ if (field.owner() != field.origin()) {
+ initializer_owner =
+ PatchClass::New(Class::Handle(field.owner()), script_cls);
+ }
+
const Function& initializer = Function::ZoneHandle(zone,
Function::New(init_name,
- RawFunction::kRegularFunction,
+ RawFunction::kImplicitStaticFinalGetter,
true, // static
false, // !const
false, // !abstract
false, // !external
false, // !native
- Class::Handle(field.owner()),
+ initializer_owner,
field.token_pos()));
initializer.set_result_type(AbstractType::Handle(zone, field.type()));
// Static initializer functions are hidden from the user.
@@ -1192,47 +1197,6 @@
}
-RawObject* Parser::ParseFunctionFromSource(const Class& owning_class,
- const String& source) {
- Thread* thread = Thread::Current();
- Isolate* isolate = thread->isolate();
- StackZone stack_zone(thread);
- LongJumpScope jump;
- if (setjmp(*jump.Set()) == 0) {
- const String& uri = String::Handle(Symbols::New("dynamically-added"));
- const Script& script = Script::Handle(
- Script::New(uri, source, RawScript::kSourceTag));
- const Library& owning_library = Library::Handle(owning_class.library());
- const String& private_key = String::Handle(owning_library.private_key());
- script.Tokenize(private_key);
- const intptr_t token_pos = 0;
- Parser parser(script, owning_library, token_pos);
- parser.is_top_level_ = true;
- parser.set_current_class(owning_class);
- const String& class_name = String::Handle(owning_class.Name());
- ClassDesc members(stack_zone.GetZone(),
- owning_class,
- class_name,
- false, /* is_interface */
- token_pos);
- const intptr_t metadata_pos = parser.SkipMetadata();
- parser.ParseClassMemberDefinition(&members, metadata_pos);
- ASSERT(members.functions().length() == 1);
- const Function& func = *members.functions().At(0);
- func.set_eval_script(script);
- ParsedFunction* parsed_function = new ParsedFunction(thread, func);
- Parser::ParseFunction(parsed_function);
- return func.raw();
- } else {
- const Error& error = Error::Handle(isolate->object_store()->sticky_error());
- isolate->object_store()->clear_sticky_error();
- return error.raw();
- }
- UNREACHABLE();
- return Object::null();
-}
-
-
SequenceNode* Parser::ParseStaticFinalGetter(const Function& func) {
TRACE_PARSER("ParseStaticFinalGetter");
ParamList params;
@@ -1249,6 +1213,7 @@
const Class& field_class = Class::Handle(Z, func.Owner());
const Field& field =
Field::ZoneHandle(Z, field_class.LookupStaticField(field_name));
+ ASSERT(!field.IsNull());
// Static final fields must have an initializer.
ExpectToken(Token::kASSIGN);
@@ -1576,7 +1541,7 @@
if (desc.NamedCount() > 0) {
const Array& arg_names =
- Array::ZoneHandle(Z, Array::New(desc.NamedCount()));
+ Array::ZoneHandle(Z, Array::New(desc.NamedCount(), Heap::kOld));
for (intptr_t i = 0; i < arg_names.Length(); ++i) {
arg_names.SetAt(i, String::Handle(Z, desc.NameAt(i)));
}
@@ -1640,7 +1605,7 @@
function_object = receiver;
} else {
const String& getter_name = String::ZoneHandle(Z,
- Symbols::New(String::Handle(Z, Field::GetterName(name))));
+ Symbols::New(String::Handle(Z, Field::GetterSymbol(name))));
function_object = new(Z) InstanceCallNode(
token_pos, receiver, getter_name, no_args);
}
@@ -2330,14 +2295,18 @@
AstNode* implicit_argument = LoadReceiver(field_pos);
const String& getter_name =
- String::ZoneHandle(Z, Field::GetterName(field_name));
- const Function& super_getter = Function::ZoneHandle(Z,
- Resolver::ResolveDynamicAnyArgs(super_class, getter_name));
+ String::ZoneHandle(Z, Field::LookupGetterSymbol(field_name));
+ Function& super_getter = Function::ZoneHandle(Z);
+ if (!getter_name.IsNull()) {
+ super_getter = Resolver::ResolveDynamicAnyArgs(super_class, getter_name);
+ }
if (super_getter.IsNull()) {
const String& setter_name =
- String::ZoneHandle(Z, Field::SetterName(field_name));
- const Function& super_setter = Function::ZoneHandle(Z,
- Resolver::ResolveDynamicAnyArgs(super_class, setter_name));
+ String::ZoneHandle(Z, Field::LookupSetterSymbol(field_name));
+ Function& super_setter = Function::ZoneHandle(Z);
+ if (!setter_name.IsNull()) {
+ super_setter = Resolver::ResolveDynamicAnyArgs(super_class, setter_name);
+ }
if (super_setter.IsNull()) {
// Check if this is an access to an implicit closure using 'super'.
// If a function exists of the specified field_name then try
@@ -2584,9 +2553,12 @@
} else {
init_expr = ParseExpr(kAllowConst, kConsumeCascades);
if (init_expr->EvalConstExpr() != NULL) {
- init_expr =
- new LiteralNode(field.token_pos(),
- EvaluateConstExpr(expr_pos, init_expr));
+ Instance& expr_value = Instance::ZoneHandle(Z);
+ if (!GetCachedConstant(expr_pos, &expr_value)) {
+ expr_value = EvaluateConstExpr(expr_pos, init_expr).raw();
+ CacheConstantValue(expr_pos, expr_value);
+ }
+ init_expr = new(Z) LiteralNode(field.token_pos(), expr_value);
}
}
set_current_class(saved_class);
@@ -2630,8 +2602,12 @@
intptr_t expr_pos = TokenPos();
init_expr = ParseExpr(kAllowConst, kConsumeCascades);
if (init_expr->EvalConstExpr() != NULL) {
- init_expr = new LiteralNode(field.token_pos(),
- EvaluateConstExpr(expr_pos, init_expr));
+ Instance& expr_value = Instance::ZoneHandle(Z);
+ if (!GetCachedConstant(expr_pos, &expr_value)) {
+ expr_value = EvaluateConstExpr(expr_pos, init_expr).raw();
+ CacheConstantValue(expr_pos, expr_value);
+ }
+ init_expr = new(Z) LiteralNode(field.token_pos(), expr_value);
}
}
}
@@ -3769,13 +3745,16 @@
}
ConsumeToken(); // Colon.
ExpectToken(Token::kTHIS);
- String& redir_name = String::ZoneHandle(Z,
- String::Concat(members->class_name(), Symbols::Dot()));
+ GrowableHandlePtrArray<const String> pieces(Z, 3);
+ pieces.Add(members->class_name());
+ pieces.Add(Symbols::Dot());
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
- redir_name = String::Concat(redir_name,
- *ExpectIdentifier("constructor name expected"));
+ pieces.Add(*ExpectIdentifier("constructor name expected"));
}
+ String& redir_name =
+ String::ZoneHandle(Z, Symbols::FromConcatAll(pieces));
+
method->redirect_name = &redir_name;
CheckToken(Token::kLPAREN);
SkipToMatchingParenthesis();
@@ -4050,7 +4029,7 @@
// For static final fields (this includes static const fields), set value to
// "uninitialized" and create a kImplicitStaticFinalGetter getter method.
if (field->has_static && has_initializer) {
- class_field.set_value(init_value);
+ class_field.SetStaticValue(init_value, true);
if (!has_simple_literal) {
String& getter_name =
String::Handle(Z, Field::GetterSymbol(*field->name));
@@ -4632,7 +4611,7 @@
void Parser::ParseClassDefinition(const Class& cls) {
TRACE_PARSER("ParseClassDefinition");
- INC_STAT(I, num_classes_compiled, 1);
+ INC_STAT(thread(), num_classes_parsed, 1);
set_current_class(cls);
is_top_level_ = true;
String& class_name = String::Handle(Z, cls.Name());
@@ -4692,7 +4671,7 @@
void Parser::ParseEnumDefinition(const Class& cls) {
TRACE_PARSER("ParseEnumDefinition");
- INC_STAT(I, num_classes_compiled, 1);
+ INC_STAT(thread(), num_classes_parsed, 1);
SkipMetadata();
ExpectToken(Token::kENUM);
@@ -4789,7 +4768,7 @@
// Initialize the field with the ordinal value. It will be patched
// later with the enum constant instance.
const Smi& ordinal_value = Smi::Handle(Z, Smi::New(i));
- enum_value.set_value(ordinal_value);
+ enum_value.SetStaticValue(ordinal_value, true);
enum_value.RecordStore(ordinal_value);
i++;
@@ -4827,7 +4806,7 @@
// Allocate the immutable array containing the enumeration values.
// The actual enum instance values will be patched in later.
const Array& values_array = Array::Handle(Z, Array::New(i, Heap::kOld));
- values_field.set_value(values_array);
+ values_field.SetStaticValue(values_array, true);
values_field.RecordStore(values_array);
// Create a static field that contains the list of enumeration names.
@@ -4838,7 +4817,7 @@
names_field = names_field.Clone(cls);
enum_members.AddField(names_field);
const Array& names_array = Array::Handle(Array::MakeArray(enum_names));
- names_field.set_value(names_array);
+ names_field.SetStaticValue(names_array, true);
names_field.RecordStore(names_array);
// Clone the toString() function from the helper class.
@@ -5431,7 +5410,7 @@
field = Field::New(var_name, is_static, is_final, is_const, is_reflectable,
current_class(), name_pos);
field.set_type(type);
- field.set_value(Object::null_instance());
+ field.SetStaticValue(Object::null_instance(), true);
top_level->AddField(field);
library_.AddObject(field, var_name);
if (metadata_pos >= 0) {
@@ -5445,7 +5424,7 @@
has_simple_literal = IsSimpleLiteral(type, &field_value);
}
SkipExpr();
- field.set_value(field_value);
+ field.SetStaticValue(field_value, true);
field.set_has_initializer(true);
if (!has_simple_literal) {
@@ -5818,13 +5797,14 @@
ConsumeToken();
String& lib_name = *ExpectIdentifier("library name expected");
if (CurrentToken() == Token::kPERIOD) {
+ GrowableHandlePtrArray<const String> pieces(Z, 3);
+ pieces.Add(lib_name);
while (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
- lib_name = String::Concat(lib_name, Symbols::Dot());
- lib_name = String::Concat(lib_name,
- *ExpectIdentifier("malformed library name"));
+ pieces.Add(Symbols::Dot());
+ pieces.Add(*ExpectIdentifier("malformed library name"));
}
- lib_name = Symbols::New(lib_name);
+ lib_name = Symbols::FromConcatAll(pieces);
}
library_.SetName(lib_name);
ExpectSemicolon();
@@ -6347,7 +6327,7 @@
}
const GrowableObjectArray& handler_types =
- GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
+ GrowableObjectArray::Handle(Z, GrowableObjectArray::New(Heap::kOld));
handler_types.Add(dynamic_type); // Catch block handles all exceptions.
CatchClauseNode* catch_clause = new(Z) CatchClauseNode(
@@ -6464,7 +6444,7 @@
SequenceNode* catch_handler_list = CloseBlock();
const GrowableObjectArray& handler_types =
- GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
+ GrowableObjectArray::Handle(Z, GrowableObjectArray::New(Heap::kOld));
handler_types.SetLength(0);
handler_types.Add(*exception_param.type);
@@ -6709,9 +6689,9 @@
const String& async_func_name =
String::Handle(Z, innermost_function().name());
String& closure_name = String::Handle(Z,
- String::NewFormatted("<%s_async_body>", async_func_name.ToCString()));
+ Symbols::NewFormatted("<%s_async_body>", async_func_name.ToCString()));
closure = Function::NewClosureFunction(
- String::Handle(Z, Symbols::New(closure_name)),
+ closure_name,
innermost_function(),
async_func_pos);
closure.set_is_generated_body(true);
@@ -7670,6 +7650,7 @@
// Parse the local function.
SequenceNode* statements = Parser::ParseFunc(function);
+ INC_STAT(thread(), num_functions_parsed, 1);
// Now that the local function has formal parameters, lookup the signature
// class in the current library (but not in its imports) and only create a new
@@ -8497,11 +8478,10 @@
static LocalVariable* LookupAsyncSavedTryContextVar(LocalScope* scope,
uint16_t try_index) {
const String& async_saved_try_ctx_name =
- String::ZoneHandle(Symbols::New(String::Handle(
- String::NewFormatted(
- "%s%d",
- Symbols::AsyncSavedTryCtxVarPrefix().ToCString(),
- try_index))));
+ String::ZoneHandle(Symbols::NewFormatted(
+ "%s%d",
+ Symbols::AsyncSavedTryCtxVarPrefix().ToCString(),
+ try_index));
LocalVariable* var = scope->LocalLookupVariable(async_saved_try_ctx_name);
ASSERT(var != NULL);
return var;
@@ -8573,9 +8553,8 @@
Z, lib.LookupFunctionAllowPrivate(Symbols::print()));
ASSERT(!print_fn.IsNull());
ArgumentListNode* one_arg = new(Z) ArgumentListNode(Scanner::kNoSourcePos);
- String& msg = String::Handle(String::NewFormatted("%s", str));
- one_arg->Add(new(Z) LiteralNode(Scanner::kNoSourcePos,
- String::ZoneHandle(Symbols::New(msg))));
+ String& msg = String::ZoneHandle(Symbols::NewFormatted("%s", str));
+ one_arg->Add(new(Z) LiteralNode(Scanner::kNoSourcePos, msg));
AstNode* print_call =
new(Z) StaticCallNode(Scanner::kNoSourcePos, print_fn, one_arg);
return print_call;
@@ -9525,10 +9504,9 @@
void Parser::SetupSavedTryContext(LocalVariable* saved_try_context) {
const String& async_saved_try_ctx_name = String::ZoneHandle(Z,
- Symbols::New(String::Handle(Z,
- String::NewFormatted("%s%d",
- Symbols::AsyncSavedTryCtxVarPrefix().ToCString(),
- last_used_try_index_ - 1))));
+ Symbols::NewFormatted("%s%d",
+ Symbols::AsyncSavedTryCtxVarPrefix().ToCString(),
+ last_used_try_index_ - 1));
LocalVariable* async_saved_try_ctx = new (Z) LocalVariable(
Scanner::kNoSourcePos,
async_saved_try_ctx_name,
@@ -9670,7 +9648,7 @@
try_stack_->enter_catch();
const intptr_t handler_pos = TokenPos();
const GrowableObjectArray& handler_types =
- GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
+ GrowableObjectArray::Handle(Z, GrowableObjectArray::New(Heap::kOld));
bool needs_stack_trace = false;
SequenceNode* catch_handler_list =
ParseCatchClauses(handler_pos,
@@ -10789,10 +10767,11 @@
const Field& field = expr->AsLoadStaticFieldNode()->field();
if (field.is_const() &&
!expr->AsLoadStaticFieldNode()->is_deferred_reference()) {
- ASSERT(field.value() != Object::sentinel().raw());
- ASSERT(field.value() != Object::transition_sentinel().raw());
- return new(zone) LiteralNode(expr->token_pos(),
- Instance::ZoneHandle(zone, field.value()));
+ ASSERT(field.StaticValue() != Object::sentinel().raw());
+ ASSERT(field.StaticValue() != Object::transition_sentinel().raw());
+ return new(zone) LiteralNode(
+ expr->token_pos(),
+ Instance::ZoneHandle(zone, field.StaticValue()));
}
}
return expr;
@@ -11161,7 +11140,8 @@
ASSERT(field.is_static());
const Class& field_owner = Class::ZoneHandle(Z, field.owner());
const String& field_name = String::ZoneHandle(Z, field.name());
- const String& getter_name = String::Handle(Z, Field::GetterName(field_name));
+ const String& getter_name =
+ String::Handle(Z, Field::GetterSymbol(field_name));
const Function& getter = Function::Handle(Z,
field_owner.LookupStaticFunction(getter_name));
// Never load field directly if there is a getter (deterministic AST).
@@ -11556,7 +11536,7 @@
is_setter_name = true;
}
} else if (Token::CanBeOverloaded(CurrentToken())) {
- extractor_name = String::New(Token::Str(CurrentToken()));
+ extractor_name = Symbols::New(Token::Str(CurrentToken()));
ConsumeToken();
} else {
ReportError("identifier or operator expected");
@@ -11670,10 +11650,13 @@
}
// Closurization of instance getter, setter, method or operator.
+ GrowableHandlePtrArray<const String> pieces(Z, 3);
+ pieces.Add(Symbols::HashMark());
if (is_setter_name) {
- extractor_name = String::Concat(Symbols::SetterPrefix(), extractor_name);
+ pieces.Add(Symbols::SetterPrefix());
}
- extractor_name = Symbols::FromConcat(Symbols::HashMark(), extractor_name);
+ pieces.Add(extractor_name);
+ extractor_name = Symbols::FromConcatAll(pieces);
return new(Z) InstanceGetterNode(property_pos, primary, extractor_name);
}
@@ -11928,12 +11911,17 @@
// We don't want to allocate anything in the heap here since this code
// is called from the optimizing compiler in the background thread. Allocate
// the key value in the zone instead.
- const char* key = Z->PrintToString("%s_%" Pd "",
- String::Handle(Z, script_.url()).ToCString(),
- token_pos);
+ // const char* key = Z->PrintToString("%s_%" Pd "",
+ // String::Handle(Z, script_.url()).ToCString(),
+ // token_pos);
+
+ const String& key = String::Handle(Z,
+ Symbols::NewFormatted("%s_%" Pd "",
+ String::Handle(Z, script_.url()).ToCString(),
+ token_pos));
ConstantsMap constants(isolate()->object_store()->compile_time_constants());
bool is_present = false;
- *value ^= constants.GetOrNull<const char *>(key, &is_present);
+ *value ^= constants.GetOrNull(key, &is_present);
ASSERT(constants.Release().raw() ==
isolate()->object_store()->compile_time_constants());
if (FLAG_compiler_stats && is_present) {
@@ -11966,10 +11954,11 @@
ASSERT(field.is_static());
const Class& field_owner = Class::ZoneHandle(Z, field.owner());
const String& field_name = String::ZoneHandle(Z, field.name());
- const String& getter_name = String::Handle(Z, Field::GetterName(field_name));
+ const String& getter_name =
+ String::Handle(Z, Field::GetterSymbol(field_name));
const Function& getter = Function::Handle(Z,
field_owner.LookupStaticFunction(getter_name));
- const Instance& value = Instance::Handle(Z, field.value());
+ const Instance& value = Instance::Handle(Z, field.StaticValue());
if (value.raw() == Object::transition_sentinel().raw()) {
if (field.is_const()) {
ReportError("circular dependency while initializing static field '%s'",
@@ -11984,7 +11973,7 @@
// not been evaluated. If the field is const, call the static getter method
// to evaluate the expression and canonicalize the value.
if (field.is_const()) {
- field.set_value(Object::transition_sentinel());
+ field.SetStaticValue(Object::transition_sentinel());
const int kNumArguments = 0; // no arguments.
const Function& func = Function::Handle(Z,
Resolver::ResolveStatic(field_owner,
@@ -12005,7 +11994,7 @@
// generated AST is not deterministic. Therefore mark the function as
// not optimizable.
current_function().SetIsOptimizable(false);
- field.set_value(Object::null_instance());
+ field.SetStaticValue(Object::null_instance());
// It is a compile-time error if evaluation of a compile-time constant
// would raise an exception.
const String& field_name = String::Handle(Z, field.name());
@@ -12022,7 +12011,7 @@
Instance& instance = Instance::Handle(Z);
instance ^= const_value.raw();
instance = TryCanonicalize(instance, field_ref_pos);
- field.set_value(instance);
+ field.SetStaticValue(instance);
return NULL; // Constant
} else {
return new(Z) StaticGetterNode(
@@ -12048,7 +12037,8 @@
// Constructors have 2 extra arguments: rcvr and construction phase.
const int kNumExtraArgs = constructor.IsFactory() ? 1 : 2;
const int num_arguments = arguments->length() + kNumExtraArgs;
- const Array& arg_values = Array::Handle(Z, Array::New(num_arguments));
+ const Array& arg_values =
+ Array::Handle(Z, Array::New(num_arguments, Heap::kOld));
Instance& instance = Instance::Handle(Z);
if (!constructor.IsFactory()) {
instance = Instance::New(type_class, Heap::kOld);
@@ -13016,29 +13006,36 @@
ASSERT(CurrentToken() == Token::kHASH);
ConsumeToken();
intptr_t symbol_pos = TokenPos();
- String& symbol = String::Handle(Z);
+ String& symbol = String::ZoneHandle(Z);
if (IsIdentifier()) {
symbol = CurrentLiteral()->raw();
ConsumeToken();
+ GrowableHandlePtrArray<const String> pieces(Z, 3);
+ pieces.Add(symbol);
while (CurrentToken() == Token::kPERIOD) {
- symbol = String::Concat(symbol, Symbols::Dot());
+ pieces.Add(Symbols::Dot());
ConsumeToken();
- symbol = String::Concat(symbol,
- *ExpectIdentifier("identifier expected"));
+ pieces.Add(*ExpectIdentifier("identifier expected"));
}
+ symbol = Symbols::FromConcatAll(pieces);
} else if (Token::CanBeOverloaded(CurrentToken())) {
- symbol = String::New(Token::Str(CurrentToken()));
+ symbol = Symbols::New(Token::Str(CurrentToken()));
ConsumeToken();
} else {
ReportError("illegal symbol literal");
}
+ ASSERT(symbol.IsSymbol());
+
+ Instance& symbol_instance = Instance::ZoneHandle(Z);
+ if (GetCachedConstant(symbol_pos, &symbol_instance)) {
+ return new(Z) LiteralNode(symbol_pos, symbol_instance);
+ }
// Call Symbol class constructor to create a symbol instance.
const Class& symbol_class = Class::Handle(I->object_store()->symbol_class());
ASSERT(!symbol_class.IsNull());
ArgumentListNode* constr_args = new(Z) ArgumentListNode(symbol_pos);
- constr_args->Add(new(Z) LiteralNode(
- symbol_pos, String::ZoneHandle(Z, Symbols::New(symbol))));
+ constr_args->Add(new(Z) LiteralNode(symbol_pos, symbol));
const Function& constr = Function::ZoneHandle(Z,
symbol_class.LookupConstructor(Symbols::SymbolCtor()));
ASSERT(!constr.IsNull());
@@ -13052,9 +13049,9 @@
script_, symbol_pos,
"error executing const Symbol constructor");
}
- const Instance& instance = Instance::Cast(result);
- return new(Z) LiteralNode(symbol_pos,
- Instance::ZoneHandle(Z, instance.raw()));
+ symbol_instance ^= result.raw();
+ CacheConstantValue(symbol_pos, symbol_instance);
+ return new(Z) LiteralNode(symbol_pos, symbol_instance);
}
@@ -13333,7 +13330,11 @@
// The type arguments of the redirection type are instantiated from the
// type arguments of the parsed type of the 'new' or 'const' expression.
Error& error = Error::Handle(Z);
- redirect_type ^= redirect_type.InstantiateFrom(type_arguments, &error);
+ redirect_type ^= redirect_type.InstantiateFrom(
+ type_arguments,
+ &error,
+ NULL, // trail
+ Heap::kOld);
if (!error.IsNull()) {
redirect_type = ClassFinalizer::NewFinalizedMalformedType(
error,
@@ -13545,14 +13546,15 @@
ASSERT(!func.IsNull());
// Build the array of literal values to interpolate.
- const Array& value_arr = Array::Handle(Z, Array::New(values.length()));
+ const Array& value_arr = Array::Handle(Z,
+ Array::New(values.length(), Heap::kOld));
for (int i = 0; i < values.length(); i++) {
ASSERT(values[i]->IsLiteralNode());
value_arr.SetAt(i, values[i]->AsLiteralNode()->literal());
}
// Build argument array to pass to the interpolation function.
- const Array& interpolate_arg = Array::Handle(Z, Array::New(1));
+ const Array& interpolate_arg = Array::Handle(Z, Array::New(1, Heap::kOld));
interpolate_arg.SetAt(0, value_arr);
// Call interpolation function.
@@ -13592,6 +13594,16 @@
return primary;
}
// String interpolation needed.
+
+ // First, check whether we've cached a compile-time constant for this
+ // string interpolation.
+ Instance& cached_string = Instance::Handle(Z);
+ if (GetCachedConstant(literal_start, &cached_string)) {
+ SkipStringLiteral();
+ return new(Z) LiteralNode(literal_start,
+ Instance::ZoneHandle(Z, cached_string.raw()));
+ }
+
bool is_compiletime_const = true;
bool has_interpolation = false;
GrowableArray<AstNode*> values_list;
@@ -13644,7 +13656,9 @@
}
if (is_compiletime_const) {
if (has_interpolation) {
- primary = new(Z) LiteralNode(literal_start, Interpolate(values_list));
+ const String& interpolated_string = Interpolate(values_list);
+ primary = new(Z) LiteralNode(literal_start, interpolated_string);
+ CacheConstantValue(literal_start, interpolated_string);
} else {
GrowableHandlePtrArray<const String> pieces(Z, values_list.length());
for (int i = 0; i < values_list.length(); i++) {
@@ -13654,6 +13668,8 @@
}
const String& lit = String::ZoneHandle(Z, Symbols::FromConcatAll(pieces));
primary = new(Z) LiteralNode(literal_start, lit);
+ // Caching of constant not necessary because the symbol lookup will
+ // find the value next time.
}
} else {
ArrayNode* values = new(Z) ArrayNode(
@@ -13735,9 +13751,12 @@
// is used. We cheat a little here by looking at the next token
// to determine whether we have an unresolved method call or
// field access.
- String& qualified_name = String::ZoneHandle(Z, prefix.name());
- qualified_name = String::Concat(qualified_name, Symbols::Dot());
- qualified_name = Symbols::FromConcat(qualified_name, ident);
+ GrowableHandlePtrArray<const String> pieces(Z, 3);
+ pieces.Add(String::Handle(Z, prefix.name()));
+ pieces.Add(Symbols::Dot());
+ pieces.Add(ident);
+ const String& qualified_name = String::ZoneHandle(Z,
+ Symbols::FromConcatAll(pieces));
InvocationMirror::Type call_type =
CurrentToken() == Token::kLPAREN ?
InvocationMirror::kMethod : InvocationMirror::kGetter;
@@ -13884,9 +13903,9 @@
// We already checked that this field is const and has been
// initialized.
ASSERT(field.is_const());
- ASSERT(field.value() != Object::sentinel().raw());
- ASSERT(field.value() != Object::transition_sentinel().raw());
- return Instance::ZoneHandle(Z, field.value());
+ ASSERT(field.StaticValue() != Object::sentinel().raw());
+ ASSERT(field.StaticValue() != Object::transition_sentinel().raw());
+ return Instance::ZoneHandle(Z, field.StaticValue());
} else {
ASSERT(expr->EvalConstExpr() != NULL);
ReturnNode* ret = new(Z) ReturnNode(expr->token_pos(), expr);
diff --git a/runtime/vm/parser.h b/runtime/vm/parser.h
index f081a7a..24ff1ac 100644
--- a/runtime/vm/parser.h
+++ b/runtime/vm/parser.h
@@ -211,10 +211,6 @@
// given static field.
static ParsedFunction* ParseStaticFieldInitializer(const Field& field);
- // Returns a RawFunction or RawError.
- static RawObject* ParseFunctionFromSource(const Class& owning_class,
- const String& source);
-
// Parse a function to retrieve parameter information that is not retained in
// the dart::Function object. Returns either an error if the parse fails
// (which could be the case for local functions), or a flat array of entries
@@ -303,7 +299,6 @@
if (token_kind_ == Token::kILLEGAL) {
ComputeCurrentToken();
}
- INC_STAT(isolate_, num_token_checks, 1);
return token_kind_;
}
@@ -323,7 +318,7 @@
// Reset cache and advance the token.
token_kind_ = Token::kILLEGAL;
tokens_iterator_.Advance();
- INC_STAT(isolate_, num_tokens_consumed, 1);
+ INC_STAT(thread(), num_tokens_consumed, 1);
}
void ConsumeRightAngleBracket();
void CheckToken(Token::Kind token_expected, const char* msg = NULL);
diff --git a/runtime/vm/precompiler.cc b/runtime/vm/precompiler.cc
index bd9b73f..c49e68c 100644
--- a/runtime/vm/precompiler.cc
+++ b/runtime/vm/precompiler.cc
@@ -434,19 +434,20 @@
if (field.is_static()) {
// Potential const object. Uninitialized field will harmlessly do a
// redundant add of the Null class.
- const Object& value = Object::Handle(Z, field.value());
+ const Object& value = Object::Handle(Z, field.StaticValue());
const Class& cls = Class::Handle(Z, value.clazz());
AddClass(cls);
if (field.has_initializer()) {
- if (field.initializer() != Function::null()) return;
+ if (field.PrecompiledInitializer() != Function::null()) return;
if (FLAG_trace_precompiler) {
ISL_Print("Precompiling initializer for %s\n", field.ToCString());
}
Compiler::CompileStaticInitializer(field);
- const Function& function = Function::Handle(Z, field.initializer());
+ const Function& function =
+ Function::Handle(Z, field.PrecompiledInitializer());
AddCalleesOf(function);
}
}
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 0c4fcf6..bacbfe8 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -830,9 +830,20 @@
RawObject* owner_; // Class or patch class or mixin class
// where this field is defined.
RawAbstractType* type_;
- RawInstance* value_; // Offset in words for instance and value for static.
+ union {
+ RawInstance* static_value_; // Value for static fields.
+ RawSmi* offset_; // Offset in words for instance fields.
+ } value_;
RawArray* dependent_code_;
- RawFunction* initializer_;
+ union {
+ // When precompiling we need to save the static initializer function here
+ // so that code for it can be generated.
+ RawFunction* precompiled_; // Static initializer function - precompiling.
+ // When generating script snapshots after running the application it is
+ // necessary to save the initial value of static fields so that we can
+ // restore the value back to the original initial value.
+ RawInstance* saved_value_; // Saved initial value - static fields.
+ } initializer_;
RawSmi* guarded_list_length_;
RawObject** to() {
return reinterpret_cast<RawObject**>(&ptr()->guarded_list_length_);
diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc
index ff8c438..13791de 100644
--- a/runtime/vm/raw_object_snapshot.cc
+++ b/runtime/vm/raw_object_snapshot.cc
@@ -747,9 +747,25 @@
writer->Write<int32_t>(ptr()->is_nullable_);
writer->Write<uint8_t>(ptr()->kind_bits_);
- // Write out all the object pointer fields.
- SnapshotWriterVisitor visitor(writer);
- visitor.VisitPointers(from(), to());
+ // Write out the name.
+ writer->WriteObjectImpl(ptr()->name_, kAsReference);
+ // Write out the owner.
+ writer->WriteObjectImpl(ptr()->owner_, kAsReference);
+ // Write out the type.
+ writer->WriteObjectImpl(ptr()->type_, kAsReference);
+ // Write out the initial static value or field offset.
+ if (Field::StaticBit::decode(ptr()->kind_bits_)) {
+ // For static field we write out the initial static value.
+ writer->WriteObjectImpl(ptr()->initializer_.saved_value_, kAsReference);
+ } else {
+ writer->WriteObjectImpl(ptr()->value_.offset_, kAsReference);
+ }
+ // Write out the dependent code.
+ writer->WriteObjectImpl(ptr()->dependent_code_, kAsReference);
+ // Write out the initializer value.
+ writer->WriteObjectImpl(ptr()->initializer_.saved_value_, kAsReference);
+ // Write out the guarded list length.
+ writer->WriteObjectImpl(ptr()->guarded_list_length_, kAsReference);
}
@@ -1180,6 +1196,8 @@
// Write out all the object pointer fields.
SnapshotWriterVisitor visitor(writer);
visitor.VisitPointers(from(), to());
+
+ writer->SetInstructionsCode(ptr()->instructions_, this);
}
diff --git a/runtime/vm/regexp.cc b/runtime/vm/regexp.cc
index f20e015..f58137c 100644
--- a/runtime/vm/regexp.cc
+++ b/runtime/vm/regexp.cc
@@ -5275,10 +5275,18 @@
Heap::kOld)));
fn.set_parameter_names(Array::Handle(zone, Array::New(kParamCount,
Heap::kOld)));
- fn.SetParameterTypeAt(0, Type::Handle(zone, Type::DynamicType()));
- fn.SetParameterNameAt(0, Symbols::string_param());
- fn.SetParameterTypeAt(1, Type::Handle(zone, Type::DynamicType()));
- fn.SetParameterNameAt(1, Symbols::start_index_param());
+ fn.SetParameterTypeAt(RegExpMacroAssembler::kParamRegExpIndex,
+ Type::Handle(zone, Type::DynamicType()));
+ fn.SetParameterNameAt(RegExpMacroAssembler::kParamRegExpIndex,
+ Symbols::This());
+ fn.SetParameterTypeAt(RegExpMacroAssembler::kParamStringIndex,
+ Type::Handle(zone, Type::DynamicType()));
+ fn.SetParameterNameAt(RegExpMacroAssembler::kParamStringIndex,
+ Symbols::string_param());
+ fn.SetParameterTypeAt(RegExpMacroAssembler::kParamStartOffsetIndex,
+ Type::Handle(zone, Type::DynamicType()));
+ fn.SetParameterNameAt(RegExpMacroAssembler::kParamStartOffsetIndex,
+ Symbols::start_index_param());
fn.set_result_type(Type::Handle(zone, Type::ArrayType()));
// Cache the result.
diff --git a/runtime/vm/regexp_assembler.h b/runtime/vm/regexp_assembler.h
index 821f054..5d0ee47 100644
--- a/runtime/vm/regexp_assembler.h
+++ b/runtime/vm/regexp_assembler.h
@@ -99,7 +99,8 @@
static const intptr_t kTableMask = kTableSize - 1;
enum {
- kParamStringIndex = 0,
+ kParamRegExpIndex = 0,
+ kParamStringIndex,
kParamStartOffsetIndex,
kParamCount
};
diff --git a/runtime/vm/regexp_assembler_ir.cc b/runtime/vm/regexp_assembler_ir.cc
index 5704b12..63ce272 100644
--- a/runtime/vm/regexp_assembler_ir.cc
+++ b/runtime/vm/regexp_assembler_ir.cc
@@ -162,8 +162,10 @@
index_temp_ = Local(Symbols::index_temp());
result_ = Local(Symbols::result());
- string_param_ = Parameter(Symbols::string_param(), 0);
- start_index_param_ = Parameter(Symbols::start_index_param(), 1);
+ string_param_ = Parameter(Symbols::string_param(),
+ RegExpMacroAssembler::kParamStringIndex);
+ start_index_param_ = Parameter(Symbols::start_index_param(),
+ RegExpMacroAssembler::kParamStartOffsetIndex);
}
@@ -307,19 +309,24 @@
RawArray* IRRegExpMacroAssembler::Execute(
- const Function& function,
+ const JSRegExp& regexp,
const String& input,
const Smi& start_offset,
Zone* zone) {
+ const intptr_t cid = input.GetClassId();
+ const Function& fun = Function::Handle(regexp.function(cid));
+ ASSERT(!fun.IsNull());
// Create the argument list.
- const Array& args = Array::Handle(Array::New(2));
- args.SetAt(0, input);
- args.SetAt(1, start_offset);
+ const Array& args =
+ Array::Handle(Array::New(RegExpMacroAssembler::kParamCount));
+ args.SetAt(RegExpMacroAssembler::kParamRegExpIndex, regexp);
+ args.SetAt(RegExpMacroAssembler::kParamStringIndex, input);
+ args.SetAt(RegExpMacroAssembler::kParamStartOffsetIndex, start_offset);
// And finally call the generated code.
const Object& retval =
- Object::Handle(zone, DartEntry::InvokeFunction(function, args));
+ Object::Handle(zone, DartEntry::InvokeFunction(fun, args));
if (retval.IsError()) {
const Error& error = Error::Cast(retval);
OS::Print("%s\n", error.ToErrorCString());
@@ -433,7 +440,7 @@
ASSERT(!word_character_field.IsUninitialized());
return new(Z) ConstantInstr(
- Instance::ZoneHandle(Z, word_character_field.value()));
+ Instance::ZoneHandle(Z, word_character_field.StaticValue()));
}
diff --git a/runtime/vm/regexp_assembler_ir.h b/runtime/vm/regexp_assembler_ir.h
index 1bd3930..eeb9f26 100644
--- a/runtime/vm/regexp_assembler_ir.h
+++ b/runtime/vm/regexp_assembler_ir.h
@@ -37,7 +37,7 @@
virtual bool CanReadUnaligned();
- static RawArray* Execute(const Function& function,
+ static RawArray* Execute(const JSRegExp& regexp,
const String& input,
const Smi& start_offset,
Zone* zone);
diff --git a/runtime/vm/regexp_test.cc b/runtime/vm/regexp_test.cc
index 7b33982..d75dac4 100644
--- a/runtime/vm/regexp_test.cc
+++ b/runtime/vm/regexp_test.cc
@@ -16,11 +16,8 @@
Zone* zone = Thread::Current()->zone();
const JSRegExp& regexp = JSRegExp::Handle(
RegExpEngine::CreateJSRegExp(zone, pat, false, false));
- const intptr_t cid = str.GetClassId();
- const Function& fn = Function::Handle(regexp.function(cid));
- EXPECT(!fn.IsNull());
const Smi& idx = Smi::Handle(Smi::New(0));
- return IRRegExpMacroAssembler::Execute(fn, str, idx, zone);
+ return IRRegExpMacroAssembler::Execute(regexp, str, idx, zone);
}
TEST_CASE(RegExp_OneByteString) {
diff --git a/runtime/vm/scanner.cc b/runtime/vm/scanner.cc
index d5533eb..50073cd 100644
--- a/runtime/vm/scanner.cc
+++ b/runtime/vm/scanner.cc
@@ -385,9 +385,8 @@
}
if (current_token_.kind != Token::kILLEGAL) {
intptr_t len = lookahead_pos_ - token_start_;
- String& str = String::ZoneHandle(Z,
- String::SubString(source_, token_start_, len, Heap::kOld));
- str = Symbols::New(str);
+ const String& str =
+ String::ZoneHandle(Z, Symbols::New(source_, token_start_, len));
current_token_.literal = &str;
}
}
diff --git a/runtime/vm/snapshot.cc b/runtime/vm/snapshot.cc
index 5ab4697..b336512 100644
--- a/runtime/vm/snapshot.cc
+++ b/runtime/vm/snapshot.cc
@@ -264,7 +264,7 @@
RawObject* SnapshotReader::ReadStaticImplicitClosure(intptr_t object_id,
intptr_t class_header) {
- ASSERT(kind_ == Snapshot::kMessage);
+ ASSERT(kind_ != Snapshot::kFull);
// First create a function object and associate it with the specified
// 'object_id'.
@@ -1138,18 +1138,106 @@
int32_t InstructionsWriter::GetOffsetFor(RawInstructions* instructions) {
- // Instructions are allocated with the code alignment and we don't write
- // anything else in the text section.
- ASSERT(Utils::IsAligned(stream_.bytes_written(),
- OS::PreferredCodeAlignment()));
-
- intptr_t offset = stream_.bytes_written();
- stream_.WriteBytes(reinterpret_cast<uint8_t*>(instructions) - kHeapObjectTag,
- instructions->Size());
+ intptr_t offset = next_offset_;
+ next_offset_ += instructions->Size();
+ instructions_.Add(InstructionsData(instructions));
return offset;
}
+static void EnsureIdentifier(char* label) {
+ for (char c = *label; c != '\0'; c = *++label) {
+ if (((c >= 'a') && (c <= 'z')) ||
+ ((c >= 'A') && (c <= 'Z')) ||
+ ((c >= '0') && (c <= '9'))) {
+ continue;
+ }
+ *label = '_';
+ }
+}
+
+
+void InstructionsWriter::WriteAssembly() {
+ Zone* Z = Thread::Current()->zone();
+
+ // Handlify collected raw pointers as building the names below
+ // will allocate on the Dart heap.
+ for (intptr_t i = 0; i < instructions_.length(); i++) {
+ InstructionsData& data = instructions_[i];
+ data.insns_ = &Instructions::Handle(Z, data.raw_insns_);
+ ASSERT(data.raw_code_ != NULL);
+ data.code_ = &Code::Handle(Z, data.raw_code_);
+ }
+
+ stream_.Print(".text\n");
+ stream_.Print(".globl _kInstructionsSnapshot\n");
+ stream_.Print(".balign %" Pd ", 0\n", OS::PreferredCodeAlignment());
+ stream_.Print("_kInstructionsSnapshot:\n");
+
+ Object& owner = Object::Handle(Z);
+ String& str = String::Handle(Z);
+
+ for (intptr_t i = 0; i < instructions_.length(); i++) {
+ const Instructions& insns = *instructions_[i].insns_;
+ const Code& code = *instructions_[i].code_;
+
+ ASSERT(insns.raw()->Size() % sizeof(uint64_t) == 0);
+
+ {
+ // 1. Write from the header to the entry point.
+ NoSafepointScope no_safepoint;
+ uword beginning = reinterpret_cast<uword>(insns.raw()) - kHeapObjectTag;
+ uword entry = beginning + Instructions::HeaderSize();
+
+ ASSERT(Utils::IsAligned(beginning, sizeof(uint64_t)));
+ ASSERT(Utils::IsAligned(entry, sizeof(uint64_t)));
+
+ for (uint64_t* cursor = reinterpret_cast<uint64_t*>(beginning);
+ cursor < reinterpret_cast<uint64_t*>(entry);
+ cursor++) {
+ stream_.Print(".quad 0x%0.16" Px64 "\n", *cursor);
+ }
+ }
+
+ // 2. Write a label at the entry point.
+ owner = code.owner();
+ if (owner.IsNull()) {
+ const char* name = StubCode::NameOfStub(insns.EntryPoint());
+ stream_.Print("Precompiled_Stub_%s:\n", name);
+ } else if (owner.IsClass()) {
+ str = Class::Cast(owner).Name();
+ const char* name = str.ToCString();
+ EnsureIdentifier(const_cast<char*>(name));
+ stream_.Print("Precompiled_AllocationStub_%s_%" Pd ":\n", name, i);
+ } else if (owner.IsFunction()) {
+ const char* name = Function::Cast(owner).ToQualifiedCString();
+ EnsureIdentifier(const_cast<char*>(name));
+ stream_.Print("Precompiled_%s_%" Pd ":\n", name, i);
+ } else {
+ UNREACHABLE();
+ }
+
+ {
+ // 3. Write from the entry point to the end.
+ NoSafepointScope no_safepoint;
+ uword beginning = reinterpret_cast<uword>(insns.raw()) - kHeapObjectTag;
+ uword entry = beginning + Instructions::HeaderSize();
+ uword end = beginning + insns.raw()->Size();
+
+ ASSERT(Utils::IsAligned(beginning, sizeof(uint64_t)));
+ ASSERT(Utils::IsAligned(entry, sizeof(uint64_t)));
+ ASSERT(Utils::IsAligned(end, sizeof(uint64_t)));
+
+ for (uint64_t* cursor = reinterpret_cast<uint64_t*>(entry);
+ cursor < reinterpret_cast<uint64_t*>(end);
+ cursor++) {
+ stream_.Print(".quad 0x%0.16" Px64 "\n", *cursor);
+ }
+ }
+ }
+}
+
+
RawInstructions* InstructionsReader::GetInstructionsAt(int32_t offset,
uword expected_tags) {
ASSERT(Utils::IsAligned(offset, OS::PreferredCodeAlignment()));
@@ -1944,6 +2032,7 @@
}
WriteIsolateFullSnapshot();
+ instructions_writer_->WriteAssembly();
instructions_snapshot_size_ = instructions_writer_->BytesWritten();
} else {
if (vm_isolate_snapshot_buffer() != NULL) {
diff --git a/runtime/vm/snapshot.h b/runtime/vm/snapshot.h
index d84d3fe..f612073 100644
--- a/runtime/vm/snapshot.h
+++ b/runtime/vm/snapshot.h
@@ -22,9 +22,11 @@
class Array;
class Class;
class ClassTable;
+class Code;
class ExternalTypedData;
class GrowableObjectArray;
class Heap;
+class Instructions;
class LanguageError;
class Library;
class Object;
@@ -777,7 +779,9 @@
InstructionsWriter(uint8_t** buffer,
ReAlloc alloc,
intptr_t initial_size)
- : stream_(buffer, alloc, initial_size) {
+ : stream_(buffer, alloc, initial_size),
+ next_offset_(0),
+ instructions_() {
ASSERT(buffer != NULL);
ASSERT(alloc != NULL);
}
@@ -787,8 +791,37 @@
int32_t GetOffsetFor(RawInstructions* instructions);
+ void SetInstructionsCode(RawInstructions* insns, RawCode* code) {
+ for (intptr_t i = 0; i < instructions_.length(); i++) {
+ if (instructions_[i].raw_insns_ == insns) {
+ instructions_[i].raw_code_ = code;
+ return;
+ }
+ }
+ UNREACHABLE();
+ }
+
+ void WriteAssembly();
+
private:
+ struct InstructionsData {
+ explicit InstructionsData(RawInstructions* insns)
+ : raw_insns_(insns), raw_code_(NULL) { }
+
+ union {
+ RawInstructions* raw_insns_;
+ const Instructions* insns_;
+ };
+ union {
+ RawCode* raw_code_;
+ const Code* code_;
+ };
+ };
+
+
WriteStream stream_;
+ intptr_t next_offset_;
+ GrowableArray<InstructionsData> instructions_;
DISALLOW_COPY_AND_ASSIGN(InstructionsWriter);
};
@@ -839,6 +872,10 @@
return instructions_writer_->GetOffsetFor(instructions);
}
+ void SetInstructionsCode(RawInstructions* instructions, RawCode* code) {
+ return instructions_writer_->SetInstructionsCode(instructions, code);
+ }
+
protected:
void UnmarkAll() {
if (!unmarked_objects_ && forward_list_ != NULL) {
@@ -909,6 +946,7 @@
friend class RawClosureData;
friend class RawContextScope;
friend class RawExceptionHandlers;
+ friend class RawField;
friend class RawGrowableObjectArray;
friend class RawImmutableArray;
friend class RawInstructions;
diff --git a/runtime/vm/symbols.cc b/runtime/vm/symbols.cc
index c827704..c9cbcf3 100644
--- a/runtime/vm/symbols.cc
+++ b/runtime/vm/symbols.cc
@@ -492,6 +492,41 @@
}
+template<typename StringType>
+RawString* Symbols::Lookup(const StringType& str) {
+ Thread* thread = Thread::Current();
+ Isolate* isolate = thread->isolate();
+ Zone* zone = thread->zone();
+ String& symbol = String::Handle(zone);
+ {
+ Isolate* vm_isolate = Dart::vm_isolate();
+ SymbolTable table(zone, vm_isolate->object_store()->symbol_table());
+ symbol ^= table.GetOrNull(str);
+ table.Release();
+ }
+ if (symbol.IsNull()) {
+ SymbolTable table(zone, isolate->object_store()->symbol_table());
+ symbol ^= table.GetOrNull(str);
+ table.Release();
+ }
+
+ ASSERT(symbol.IsNull() || symbol.IsSymbol());
+ ASSERT(symbol.IsNull() || symbol.HasHash());
+ return symbol.raw();
+}
+
+
+RawString* Symbols::LookupFromConcat(const String& str1, const String& str2) {
+ if (str1.Length() == 0) {
+ return Lookup(str2);
+ } else if (str2.Length() == 0) {
+ return Lookup(str1);
+ } else {
+ return Lookup(ConcatString(str1, str2));
+ }
+}
+
+
RawString* Symbols::New(const String& str) {
if (str.IsSymbol()) {
return str.raw();
@@ -505,6 +540,31 @@
}
+
+RawString* Symbols::NewFormatted(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ RawString* result = NewFormattedV(format, args);
+ NoSafepointScope no_safepoint;
+ va_end(args);
+ return result;
+}
+
+
+RawString* Symbols::NewFormattedV(const char* format, va_list args) {
+ va_list args_copy;
+ va_copy(args_copy, args);
+ intptr_t len = OS::VSNPrint(NULL, 0, format, args_copy);
+ va_end(args_copy);
+
+ Zone* zone = Thread::Current()->zone();
+ char* buffer = zone->Alloc<char>(len + 1);
+ OS::VSNPrint(buffer, (len + 1), format, args);
+
+ return Symbols::New(buffer);
+}
+
+
RawString* Symbols::FromCharCode(int32_t char_code) {
if (char_code > kMaxOneCharCodeSymbol) {
return FromUTF32(&char_code, 1);
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index f5f7bbf..8a91de7 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -595,6 +595,10 @@
intptr_t begin_index,
intptr_t length);
+ static RawString* NewFormatted(const char* format, ...)
+ PRINTF_ATTRIBUTE(1, 2);
+ static RawString* NewFormattedV(const char* format, va_list args);
+
static RawString* FromConcat(const String& str1, const String& str2);
static RawString* FromConcatAll(
@@ -611,6 +615,13 @@
static void DumpStats();
+ // Returns Symbol::Null if no symbol is found.
+ template<typename StringType>
+ static RawString* Lookup(const StringType& str);
+
+ // Returns Symbol::Null if no symbol is found.
+ static RawString* LookupFromConcat(const String& str1, const String& str2);
+
private:
enum {
kInitialVMIsolateSymtabSize = 1024,
diff --git a/runtime/vm/thread.cc b/runtime/vm/thread.cc
index d0c4651..7d817d3 100644
--- a/runtime/vm/thread.cc
+++ b/runtime/vm/thread.cc
@@ -12,6 +12,7 @@
#include "vm/profiler.h"
#include "vm/runtime_entry.h"
#include "vm/stub_code.h"
+#include "vm/symbols.h"
#include "vm/thread_interrupter.h"
#include "vm/thread_registry.h"
diff --git a/runtime/vm/thread.h b/runtime/vm/thread.h
index 9643c33..16eade4 100644
--- a/runtime/vm/thread.h
+++ b/runtime/vm/thread.h
@@ -20,6 +20,7 @@
class Object;
class RawBool;
class RawObject;
+class RawString;
class RuntimeEntry;
class StackResource;
class TimelineEventBlock;
@@ -34,6 +35,8 @@
#define CACHED_ADDRESSES_LIST(V) \
V(uword, update_store_buffer_entry_point_, \
StubCode::UpdateStoreBuffer_entry()->EntryPoint(), 0) \
+ V(RawString**, predefined_symbols_address_, \
+ Symbols::PredefinedAddress(), NULL) \
#define CACHED_CONSTANTS_LIST(V) \
CACHED_VM_OBJECTS_LIST(V) \
diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
index 2a91a2e..91049cd 100644
--- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart
+++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
@@ -2396,8 +2396,12 @@
jsArguments,
String propertyName) {
JS_EFFECT(() {
- BoundClosure.receiverOf(JS('BoundClosure', 'void 0'));
- BoundClosure.selfOf(JS('BoundClosure', 'void 0'));
+ // The functions are called here to model the calls from JS forms below.
+ // The types in the JS forms in the arguments are propagated in type
+ // inference.
+ BoundClosure.receiverOf(JS('BoundClosure', '0'));
+ BoundClosure.selfOf(JS('BoundClosure', '0'));
+ getType(JS('int', '0'));
});
// TODO(ahe): All the place below using \$ should be rewritten to go
// through the namer.
diff --git a/sdk/lib/io/file_impl.dart b/sdk/lib/io/file_impl.dart
index 8ed6c58..2164bc9 100644
--- a/sdk/lib/io/file_impl.dart
+++ b/sdk/lib/io/file_impl.dart
@@ -566,56 +566,43 @@
class _RandomAccessFile
- extends Object with _ServiceObject
implements RandomAccessFile {
- // Use default Map so we keep order.
- static Map<int, _RandomAccessFile> _files = new Map<int, _RandomAccessFile>();
+ static bool _connectedResourceHandler = false;
final String path;
int _id;
bool _asyncDispatched = false;
SendPort _fileService;
- int _totalRead = 0;
- int _totalWritten = 0;
- int _readCount = 0;
- int _writeCount = 0;
-
+ _FileResourceInfo _resourceInfo;
_RandomAccessFile(this._id, this.path) {
- _files[_serviceId] = this;
- }
-
- String get _serviceTypePath => 'io/file/randomaccessfiles';
- String get _serviceTypeName => 'RandomAccessFile';
-
- Map _toJSON(bool ref) {
- var r = {
- 'id': _servicePath,
- 'type': _serviceType(ref),
- 'name': '$path',
- 'user_name': '$path',
- };
- if (ref) {
- return r;
- }
- r['asyncDispatched'] = _asyncDispatched;
- r['fd'] = _getFD(_id);
- r['totalRead'] = _totalRead;
- r['totalWritten'] = _totalWritten;
- r['readCount'] = _totalWritten;
- r['writeCount'] = _writeCount;
- return r;
+ _resourceInfo = new _FileResourceInfo(this);
+ _maybeConnectHandler();
}
void _maybePerformCleanup() {
if (closed) {
- _files.remove(_serviceId);
+ _FileResourceInfo.FileClosed(_resourceInfo);
}
}
external static int _getFD(int id);
+ _maybeConnectHandler() {
+ if (!_connectedResourceHandler) {
+ // TODO(ricow): we probably need set these in some initialization code.
+ // We need to make sure that these are always awailable from the
+ // observatory even if no files (or sockets for the socket ones) are
+ // open.
+ registerExtension('__getOpenFiles',
+ _FileResourceInfo.getOpenFiles);
+ registerExtension('__getFileByID',
+ _FileResourceInfo.getFileInfoMapByID);
+ _connectedResourceHandler = true;
+ }
+ }
+
Future<RandomAccessFile> close() {
return _dispatch(_FILE_CLOSE, [_id], markClosed: true).then((result) {
if (result != -1) {
@@ -645,8 +632,8 @@
if (_isErrorResponse(response)) {
throw _exceptionFromResponse(response, "readByte failed", path);
}
- _readCount++;
- _totalRead++;
+ _resourceInfo.readCount++;
+ _resourceInfo.totalRead++;
return response;
});
}
@@ -659,8 +646,8 @@
if (result is OSError) {
throw new FileSystemException("readByte failed", path, result);
}
- _readCount++;
- _totalRead++;
+ _resourceInfo.readCount++;
+ _resourceInfo.totalRead++;
return result;
}
@@ -672,8 +659,8 @@
if (_isErrorResponse(response)) {
throw _exceptionFromResponse(response, "read failed", path);
}
- _readCount++;
- _totalRead += response[1].length;
+ _resourceInfo.readCount++;
+ _resourceInfo.totalRead += response[1].length;
return response[1];
});
}
@@ -689,8 +676,8 @@
if (result is OSError) {
throw new FileSystemException("readSync failed", path, result);
}
- _readCount++;
- _totalRead += result.length;
+ _resourceInfo.readCount++;
+ _resourceInfo.totalRead += result.length;
return result;
}
@@ -710,8 +697,8 @@
var read = response[1];
var data = response[2];
buffer.setRange(start, start + read, data);
- _readCount++;
- _totalRead += read;
+ _resourceInfo.readCount++;
+ _resourceInfo.totalRead += read;
return read;
});
}
@@ -731,8 +718,8 @@
if (result is OSError) {
throw new FileSystemException("readInto failed", path, result);
}
- _readCount++;
- _totalRead += result;
+ _resourceInfo.readCount++;
+ _resourceInfo.totalRead += result;
return result;
}
@@ -744,8 +731,8 @@
if (_isErrorResponse(response)) {
throw _exceptionFromResponse(response, "writeByte failed", path);
}
- _writeCount++;
- _totalWritten++;
+ _resourceInfo.writeCount++;
+ _resourceInfo.totalWritten++;
return this;
});
}
@@ -761,8 +748,8 @@
if (result is OSError) {
throw new FileSystemException("writeByte failed", path, result);
}
- _writeCount++;
- _totalWritten++;
+ _resourceInfo.writeCount++;
+ _resourceInfo.totalWritten++;
return result;
}
@@ -791,8 +778,8 @@
if (_isErrorResponse(response)) {
throw _exceptionFromResponse(response, "writeFrom failed", path);
}
- _writeCount++;
- _totalWritten += end - (start - result.start);
+ _resourceInfo.writeCount++;
+ _resourceInfo.totalWritten += end - (start - result.start);
return this;
});
}
@@ -817,8 +804,8 @@
if (result is OSError) {
throw new FileSystemException("writeFrom failed", path, result);
}
- _writeCount++;
- _totalWritten += end - (start - bufferAndStart.start);
+ _resourceInfo.writeCount++;
+ _resourceInfo.totalWritten += end - (start - bufferAndStart.start);
}
Future<RandomAccessFile> writeString(String string,
diff --git a/sdk/lib/io/io.dart b/sdk/lib/io/io.dart
index b3ca214..aea1b5b 100644
--- a/sdk/lib/io/io.dart
+++ b/sdk/lib/io/io.dart
@@ -206,6 +206,7 @@
LinkedListEntry,
UnmodifiableMapView;
import 'dart:convert';
+import 'dart:developer';
import 'dart:isolate';
import 'dart:math';
import 'dart:typed_data';
@@ -226,6 +227,7 @@
part 'http_impl.dart';
part 'http_parser.dart';
part 'http_session.dart';
+part 'io_resource_info.dart';
part 'io_sink.dart';
part 'io_service.dart';
part 'link.dart';
diff --git a/sdk/lib/io/io_resource_info.dart b/sdk/lib/io/io_resource_info.dart
new file mode 100644
index 0000000..8c30c64
--- /dev/null
+++ b/sdk/lib/io/io_resource_info.dart
@@ -0,0 +1,205 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for 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 _IOResourceInfo {
+ final String type;
+ final int id;
+ String get name;
+ static int _count = 0;
+
+ _IOResourceInfo(this.type) : id = _IOResourceInfo.getNextID();
+
+ String toJSON();
+
+ /// Get the full set of values for a specific implementation. This is normally
+ /// looked up based on an id from a referenceValueMap.
+ Map<String, String> get fullValueMap;
+
+ /// The reference map, used to return a list of values, e.g., getting
+ /// all open sockets. The structure of this is shared among all subclasses.
+ Map<String, String> get referenceValueMap =>
+ {
+ // The type for a reference object is prefixed with @ in observatory.
+ 'type': '@$type',
+ 'id': id,
+ 'name': name,
+ };
+
+ static int getNextID() => _count++;
+}
+
+// TODO(ricow): Move stopwatch into this class and use it for both files
+// and sockets (by using setters on totalRead/totalWritten). Also, consider
+// setting readCount and writeCount in those setters.
+abstract class _ReadWriteResourceInfo extends _IOResourceInfo {
+ int totalRead;
+ int totalWritten;
+ int readCount;
+ int writeCount;
+ double lastRead;
+ double lastWrite;
+
+ _ReadWriteResourceInfo(String type) :
+ totalRead = 0,
+ totalWritten = 0,
+ readCount = 0,
+ writeCount = 0,
+ lastRead = 0.0,
+ lastWrite = 0.0,
+ super(type);
+
+ Map<String, String> get fullValueMap =>
+ {
+ 'type': type,
+ 'id': id,
+ 'name': name,
+ 'total_read': totalRead,
+ 'total_written': totalWritten,
+ 'read_count': readCount,
+ 'write_count': writeCount,
+ 'last_read': lastRead,
+ 'last_write': lastWrite
+ };
+
+ String toJSON() {
+ return JSON.encode(fullValueMap);
+ }
+}
+
+class _FileResourceInfo extends _ReadWriteResourceInfo {
+ static const String TYPE = '_file';
+
+ final file;
+
+ static Map<int, _FileResourceInfo> openFiles =
+ new Map<int, _FileResourceInfo>();
+
+ _FileResourceInfo(this.file) : super(TYPE) {
+ FileOpened(this);
+ }
+
+ static FileOpened(_FileResourceInfo info) {
+ assert(!openFiles.containsKey(info.id));
+ openFiles[info.id] = info;
+ }
+
+ static FileClosed(_FileResourceInfo info) {
+ assert(openFiles.containsKey(info.id));
+ openFiles.remove(info.id);
+ }
+
+ static Iterable<Map<String, String>> getOpenFilesList() {
+ return new List.from(openFiles.values.map((e) => e.referenceValueMap));
+ }
+
+ static Future<ServiceExtensionResponse> getOpenFiles(function, params) {
+ assert(function == '__getOpenFiles');
+ var data = {'type': '_openfiles', 'data': getOpenFilesList()};
+ var json = JSON.encode(data);
+ return new Future.value(new ServiceExtensionResponse.result(json));
+ }
+
+ Map<String, String> getFileInfoMap() {
+ var result = fullValueMap;
+ return result;
+ }
+
+ static Future<ServiceExtensionResponse> getFileInfoMapByID(function, params) {
+ assert(params.containsKey('id'));
+ var id = int.parse(params['id']);
+ var result =
+ openFiles.containsKey(id) ? openFiles[id].getFileInfoMap() : {};
+ var json = JSON.encode(result);
+ return new Future.value(new ServiceExtensionResponse.result(json));
+ }
+
+ String get name {
+ return '${file.path}';
+ }
+}
+
+class _SocketResourceInfo extends _ReadWriteResourceInfo {
+ static const String TCP_STRING = 'TCP';
+ static const String UDP_STRING = 'UDP';
+ static const String TYPE = '_socket';
+
+ final socket;
+
+ static Map<int, _SocketResourceInfo> openSockets =
+ new Map<int, _SocketResourceInfo>();
+
+ _SocketResourceInfo(this.socket) : super(TYPE) {
+ SocketOpened(this);
+ }
+
+ String get name {
+ if (socket.isListening) {
+ return 'listening:${socket.address.host}:${socket.port}';
+ }
+ var remote = '';
+ try {
+ var remoteHost = socket.remoteAddress.host;
+ var remotePort = socket.remotePort;
+ remote = ' -> $remoteHost:$remotePort';
+ } catch (e) { } // ignored if we can't get the information
+ return '${socket.address.host}:${socket.port}$remote';
+ }
+
+ static Iterable<Map<String, String>> getOpenSocketsList() {
+ return new List.from(openSockets.values.map((e) => e.referenceValueMap));
+ }
+
+ Map<String, String> getSocketInfoMap() {
+ var result = fullValueMap;
+ result['socket_type'] = socket.isTcp ? TCP_STRING : UDP_STRING;
+ result['listening'] = socket.isListening;
+ result['host'] = socket.address.host;
+ result['port'] = socket.port;
+ if (!socket.isListening) {
+ try {
+ result['remote_host'] = socket.remoteAddress.host;
+ result['remote_port'] = socket.remotePort;
+ } catch (e) {
+ // UDP.
+ result['remote_port'] = 'NA';
+ result['remote_host'] = 'NA';
+ }
+ } else {
+ result['remote_port'] = 'NA';
+ result['remote_host'] = 'NA';
+ }
+ result['address_type'] = socket.address.type.name;
+ return result;
+ }
+
+ static Future<ServiceExtensionResponse> getSocketInfoMapByID(
+ String function, Map<String, String> params) {
+ assert(params.containsKey('id'));
+ var id = int.parse(params['id']);
+ var result =
+ openSockets.containsKey(id) ? openSockets[id].getSocketInfoMap() : {};
+ var json = JSON.encode(result);
+ return new Future.value(new ServiceExtensionResponse.result(json));
+ }
+
+ static Future<ServiceExtensionResponse> getOpenSockets(function, params) {
+ assert(function == '__getOpenSockets');
+ var data = {'type': '_opensockets', 'data': getOpenSocketsList()};
+ var json = JSON.encode(data);
+ return new Future.value(new ServiceExtensionResponse.result(json));
+ }
+
+ static SocketOpened(_SocketResourceInfo info) {
+ assert(!openSockets.containsKey(info.id));
+ openSockets[info.id] = info;
+ }
+
+ static SocketClosed(_SocketResourceInfo info) {
+ assert(openSockets.containsKey(info.id));
+ openSockets.remove(info.id);
+ }
+
+}
diff --git a/sdk/lib/io/iolib_sources.gypi b/sdk/lib/io/iolib_sources.gypi
index bb8848c..ad2110f 100644
--- a/sdk/lib/io/iolib_sources.gypi
+++ b/sdk/lib/io/iolib_sources.gypi
@@ -20,6 +20,7 @@
'http_impl.dart',
'http_parser.dart',
'http_session.dart',
+ 'io_resource_info.dart',
'io_sink.dart',
'io_service.dart',
'link.dart',
diff --git a/tests/co19/co19-dart2js.status b/tests/co19/co19-dart2js.status
index 019e273..224f620 100644
--- a/tests/co19/co19-dart2js.status
+++ b/tests/co19/co19-dart2js.status
@@ -1536,6 +1536,20 @@
LayoutTests/fast/text/regional-indicator-symobls_t01: RuntimeError # Please triage this failure
LayoutTests/fast/text/text-combine-shrink-to-fit_t01: RuntimeError # Please triage this failure
+[ $compiler == dart2js && $runtime == chrome && $system != linux ]
+# These are failures in Chrome 45, which is not yet on Linux.
+# This will be merged with the earlier blocks when Linux is updated.
+LayoutTests/fast/css/background-position-serialize_t01: RuntimeError # Please triage this failure
+LayoutTests/fast/css/font-shorthand-from-longhands_t01: RuntimeError # Please triage this failure
+LayoutTests/fast/css/getComputedStyle/computed-style-font_t01: RuntimeError # Please triage this failure
+LayoutTests/fast/css/getPropertyValue-columns_t01: RuntimeError # Please triage this failure
+LayoutTests/fast/css/parse-color-int-or-percent-crash_t01: RuntimeError # Please triage this failure
+LayoutTests/fast/forms/ValidityState-customError_t01: RuntimeError # Please triage this failure
+LayoutTests/fast/multicol/balance-short-trailing-empty-block_t01: Pass
+LayoutTests/fast/multicol/balance-trailing-border_t01: Pass
+LayoutTests/fast/multicol/columns-shorthand-parsing_t02: RuntimeError # Please triage this failure
+LayoutTests/fast/multicol/widows_t02: Pass
+
[ $compiler == dart2js && $runtime == ff ]
Language/12_Expressions/28_Postfix_Expressions_A07_t02: Skip # Times out. Please triage this failure
LayoutTests/fast/alignment/parse-align-items_t01: RuntimeError # Please triage this failure
diff --git a/tests/compiler/dart2js/dart2js.status b/tests/compiler/dart2js/dart2js.status
index d2c606b..3518280 100644
--- a/tests/compiler/dart2js/dart2js.status
+++ b/tests/compiler/dart2js/dart2js.status
@@ -20,6 +20,12 @@
simple_inferrer_const_closure_test: Fail # Issue 16507
simple_inferrer_const_closure2_test: Fail # Issue 16507
simple_inferrer_global_field_closure_test: Fail # Issue 16507
+simple_inferrer_const_closure_default_test/05: Fail # Issue 24297
+simple_inferrer_const_closure_default_test/06: Fail # Issue 24297
+simple_inferrer_const_closure_default_test/09: Fail # Issue 24297
+simple_inferrer_const_closure_default_test/10: Fail # Issue 24297
+simple_inferrer_const_closure_default_test/11: Fail # Issue 24297
+simple_inferrer_const_closure_default_test/12: Fail # Issue 24297
logical_expression_test: Fail # Issue 17027
diff --git a/tests/compiler/dart2js/js_backend_cps_ir_basic_test.dart b/tests/compiler/dart2js/js_backend_cps_ir_basic_test.dart
index e8e5ebc..51d2762 100644
--- a/tests/compiler/dart2js/js_backend_cps_ir_basic_test.dart
+++ b/tests/compiler/dart2js/js_backend_cps_ir_basic_test.dart
@@ -25,14 +25,15 @@
print('($m)');
}""",r"""
function() {
+ var l = [1, 2, 3], m = P.LinkedHashMap_LinkedHashMap$_literal(["s", 1]);
P.print("()");
P.print("(true)");
P.print("(1)");
P.print("(" + H.S([1, 2, 3]) + ")");
P.print("(" + H.S(P.LinkedHashMap_LinkedHashMap$_literal(["s", 1])) + ")");
P.print("(1)");
- P.print("(" + H.S([1, 2, 3]) + ")");
- P.print("(" + H.S(P.LinkedHashMap_LinkedHashMap$_literal(["s", 1])) + ")");
+ P.print("(" + H.S(l) + ")");
+ P.print("(" + H.S(m) + ")");
}"""),
const TestEntry("""
foo(a, [b = "b"]) { print(b); return b; }
diff --git a/tests/compiler/dart2js/js_backend_cps_ir_interceptors_test.dart b/tests/compiler/dart2js/js_backend_cps_ir_interceptors_test.dart
index bb5e902..cafe812 100644
--- a/tests/compiler/dart2js/js_backend_cps_ir_interceptors_test.dart
+++ b/tests/compiler/dart2js/js_backend_cps_ir_interceptors_test.dart
@@ -38,8 +38,11 @@
if (i < 0 || i >= l.length)
H.ioore(l, i);
x_ = J.getInterceptor$as(x = l[i]);
- for (j = 0; j < x_.get$length(x); j = j + 1)
- P.print(x_.$index(x, j));
+ for (j = 0; j < x_.get$length(x); j = j + 1) {
+ if (j < 0 || j >= x.length)
+ H.ioore(x, j);
+ P.print(x[j]);
+ }
}
}"""),
];
diff --git a/tests/compiler/dart2js/lookup_map_test.dart b/tests/compiler/dart2js/lookup_map_test.dart
index d73bc75..d3893b0 100644
--- a/tests/compiler/dart2js/lookup_map_test.dart
+++ b/tests/compiler/dart2js/lookup_map_test.dart
@@ -8,10 +8,50 @@
import 'compiler_helper.dart';
main() {
+ Map<String, String> testDeclarations = {
+ 'types': r'''
+ import 'package:lookup_map/lookup_map.dart';
+ class A {}
+ class B {}
+ class C {}
+ class D {}
+ class E {}''',
+
+ 'const keys': r'''
+ import 'package:lookup_map/lookup_map.dart';
+ class Key { final name; const Key(this.name); }
+ const A = const Key("A");
+ const B = const Key("B");
+ const C = const Key("C");
+ const D = const Key("D");
+ const E = const Key("E");''',
+
+ 'mixed keys': r'''
+ import 'package:lookup_map/lookup_map.dart';
+ class Key { final name; const Key(this.name); }
+ const A = const Key("A");
+ class B {}
+ const C = const Key("C");
+ class D {}
+ const E = const Key("E");''',
+ };
+
+ testDeclarations.forEach((name, declarations) {
+ group(name, () => _commonTests(declarations));
+ });
+ group('generic', _genericTests);
+ group('metadata', _metadataTests);
+ group('unsupported', _unsupportedKeysTests);
+ group('mirrors', _mirrorsTests);
+}
+
+/// Common tests for both declarations that use Types or other const expressions
+/// as keys. The argument [declaration] should contain a declaration for
+/// constant keys named `A`, `B`, `C`, `D`, and `E`.
+_commonTests(String declarations) {
test('live entries are kept', () async {
- String generated = await compileAll(r"""
- import 'package:lookup_map/lookup_map.dart';
- class A{}
+ String generated = await compileAll("""
+ $declarations
const map = const LookupMap(const [
A, "the-text-for-A",
]);
@@ -21,9 +61,8 @@
});
test('live entries are kept - single-pair', () async {
- String generated = await compileAll(r"""
- import 'package:lookup_map/lookup_map.dart';
- class A{}
+ String generated = await compileAll("""
+ $declarations
const map = const LookupMap.pair(A, "the-text-for-A");
main() => print(map[A]);
""");
@@ -31,10 +70,8 @@
});
test('unused entries are removed', () async {
- String generated = await compileAll(r"""
- import 'package:lookup_map/lookup_map.dart';
- class A{}
- class B{}
+ String generated = await compileAll("""
+ $declarations
const map = const LookupMap(const [
A, "the-text-for-A",
B, "the-text-for-B",
@@ -45,10 +82,8 @@
});
test('unused entries are removed - nested maps', () async {
- String generated = await compileAll(r"""
- import 'package:lookup_map/lookup_map.dart';
- class A{}
- class B{}
+ String generated = await compileAll("""
+ $declarations
const map = const LookupMap(const [], const [
const LookupMap(const [
A, "the-text-for-A",
@@ -61,10 +96,8 @@
});
test('unused entries are removed - single-pair', () async {
- String generated = await compileAll(r"""
- import 'package:lookup_map/lookup_map.dart';
- class A{}
- class B{}
+ String generated = await compileAll("""
+ $declarations
const map = const LookupMap.pair(A, "the-text-for-A");
main() => print(map[A]);
""");
@@ -72,10 +105,9 @@
});
test('unused entries are removed - nested single-pair', () async {
- String generated = await compileAll(r"""
+ String generated = await compileAll("""
import 'package:lookup_map/lookup_map.dart';
- class A{}
- class B{}
+ $declarations
const map = const LookupMap(const [], const [
const LookupMap.pair(A, "the-text-for-A"),
const LookupMap.pair(B, "the-text-for-B"),
@@ -86,10 +118,8 @@
});
test('works if entries are declared separate from map', () async {
- String generated = await compileAll(r"""
- import 'package:lookup_map/lookup_map.dart';
- class A{}
- class B{}
+ String generated = await compileAll("""
+ $declarations
const entries = const [
A, "the-text-for-A",
B, "the-text-for-B",
@@ -101,10 +131,8 @@
});
test('escaping entries disable tree-shaking', () async {
- String generated = await compileAll(r"""
- import 'package:lookup_map/lookup_map.dart';
- class A{}
- class B{}
+ String generated = await compileAll("""
+ $declarations
const entries = const [
A, "the-text-for-A",
B, "the-text-for-B",
@@ -119,13 +147,8 @@
});
test('uses include recursively reachable data', () async {
- String generated = await compileAll(r"""
- import 'package:lookup_map/lookup_map.dart';
- class A{}
- class B{}
- class C{}
- class D{}
- class E{}
+ String generated = await compileAll("""
+ $declarations
const map = const LookupMap(const [
A, const ["the-text-for-A", B],
B, const ["the-text-for-B", C],
@@ -143,27 +166,62 @@
});
test('uses are found through newly discovered code', () async {
- String generated = await compileAll(r"""
- import 'package:lookup_map/lookup_map.dart';
- class A{ A(B x);}
- class B{}
- class C{}
- class D{}
- class E{}
- createA() => new A(map[B][1]());
- createB() => new B();
+ String generated = await compileAll("""
+ $declarations
+ f1() => map[B][1]();
+ f2() => E;
const map = const LookupMap(const [
- A, const ["the-text-for-A", createA],
- B, const ["the-text-for-B", createB],
+ A, const ["the-text-for-A", f1],
+ B, const ["the-text-for-B", f2],
C, const ["the-text-for-C"],
+ D, const ["the-text-for-D"],
+ E, const ["the-text-for-E"],
]);
main() => print(map[A][1]());
""");
expect(generated, contains("the-text-for-A"));
expect(generated, contains("the-text-for-B"));
expect(generated, isNot(contains("the-text-for-C")));
+ expect(generated, isNot(contains("the-text-for-C")));
+ expect(generated, contains("the-text-for-E"));
});
+ test('support subclassing LookupMap', () async {
+ String generated = await compileAll("""
+ $declarations
+ class S extends LookupMap {
+ const S(list) : super(list);
+ }
+ const map = const S(const [
+ A, "the-text-for-A",
+ B, "the-text-for-B",
+ ]);
+
+ main() => print(map[A]);
+ """);
+ expect(generated, contains("the-text-for-A"));
+ expect(generated, isNot(contains("the-text-for-B")));
+ });
+
+ test('constants keys are processed recursively', () async {
+ String generated = await compileAll("""
+ $declarations
+
+ const nested = const [ B ];
+ const map = const LookupMap(const [
+ A, "the-text-for-A",
+ B, "the-text-for-B",
+ ]);
+ main() => print(map[nested]);
+ """);
+ expect(generated, isNot(contains("the-text-for-A")));
+ expect(generated, contains("the-text-for-B"));
+ });
+}
+
+/// Tests specific to type keys, we ensure that generic type arguments are
+/// considered.
+_genericTests() {
test('generic type allocations are considered used', () async {
String generated = await compileAll(r"""
import 'package:lookup_map/lookup_map.dart';
@@ -193,6 +251,23 @@
expect(generated, isNot(contains("the-text-for-B")));
});
+ // regression test for a failure when looking up `dynamic` in a generic.
+ test('do not choke with dynamic type arguments', () async {
+ await compileAll(r"""
+ import 'package:lookup_map/lookup_map.dart';
+ class A{}
+ class M<T>{ get type => T; }
+ const map = const LookupMap(const [
+ A, "the-text-for-A",
+ ]);
+ main() => print(map[new M<dynamic>().type]);
+ """);
+ });
+}
+
+/// Sanity checks about metadata: it is ignored for codegen even though it is
+/// visited during resolution.
+_metadataTests() {
test('metadata is ignored', () async {
String generated = await compileAll(r"""
import 'package:lookup_map/lookup_map.dart';
@@ -225,37 +300,95 @@
""");
expect(generated, isNot(contains("the-text-for-A")));
});
+}
- // regression test for a failure when looking up `dynamic` in a generic.
- test('do not choke on dynamic types', () async {
- await compileAll(r"""
+_unsupportedKeysTests() {
+ test('primitive and string keys are always kept', () async {
+ String generated = await compileAll("""
import 'package:lookup_map/lookup_map.dart';
- class A{}
- class M<T>{ get type => T; }
+ const A = "A";
+ const B = "B";
const map = const LookupMap(const [
A, "the-text-for-A",
- ]);
- main() => print(map[new M<dynamic>().type]);
- """);
- });
-
-
- test('support subclassing LookupMap', () async {
- String generated = await compileAll(r"""
- import 'package:lookup_map/lookup_map.dart';
- class A{}
- class B{}
- class S extends LookupMap {
- const S(list) : super(list);
- }
- const map = const S(const [
- A, "the-text-for-A",
B, "the-text-for-B",
+ 3, "the-text-for-3",
+ 1.1, "the-text-for-1.1",
+ false, "the-text-for-false",
]);
-
main() => print(map[A]);
""");
expect(generated, contains("the-text-for-A"));
- expect(generated, isNot(contains("the-text-for-B")));
+ expect(generated, contains("the-text-for-B"));
+ expect(generated, contains("the-text-for-3"));
+ expect(generated, contains("the-text-for-1.1"));
+ expect(generated, contains("the-text-for-false"));
+ });
+
+ test('non-type const keys implementing equals are not removed', () async {
+ String generated = await compileAll("""
+ import 'package:lookup_map/lookup_map.dart';
+ class Key {
+ final name;
+ const Key(this.name);
+ int get hashCode => name.hashCode * 13;
+ operator ==(other) => other is Key && name == other.name;
+ }
+ const A = const Key("A");
+ const B = const Key("B");
+ const map = const LookupMap(const [
+ A, "the-text-for-A",
+ B, "the-text-for-B",
+ ]);
+ main() => print(map[A]);
+ """);
+ expect(generated, contains("the-text-for-B"));
+ });
+}
+
+_mirrorsTests() {
+ test('retain entries if mirrors keep the type', () async {
+ String generated = await compileAll("""
+ import 'dart:mirrors';
+ import 'package:lookup_map/lookup_map.dart';
+ class A {}
+ class B {}
+ class C {}
+ const map = const LookupMap(const [
+ A, "the-text-for-A",
+ B, "the-text-for-B",
+ C, "the-text-for-C",
+ ]);
+ main() {
+ reflectType(A);
+ print(map[A]);
+ }
+ """);
+ expect(generated, contains("the-text-for-A"));
+ expect(generated, contains("the-text-for-B"));
+ expect(generated, contains("the-text-for-C"));
+ });
+
+ test('exclude entries if MirrorsUsed also exclude the type', () async {
+ String generated = await compileAll("""
+ library foo;
+ @MirrorsUsed(targets: const [B])
+ import 'dart:mirrors';
+ import 'package:lookup_map/lookup_map.dart';
+ class A {}
+ class B {}
+ class C {}
+ const map = const LookupMap(const [
+ A, "the-text-for-A",
+ B, "the-text-for-B",
+ C, "the-text-for-C",
+ ]);
+ main() {
+ reflectType(A);
+ print(map[A]);
+ }
+ """);
+ expect(generated, contains("the-text-for-A"));
+ expect(generated, contains("the-text-for-B"));
+ expect(generated, isNot(contains("the-text-for-C")));
});
}
diff --git a/tests/compiler/dart2js/mock_compiler.dart b/tests/compiler/dart2js/mock_compiler.dart
index e185712..cda5aa6 100644
--- a/tests/compiler/dart2js/mock_compiler.dart
+++ b/tests/compiler/dart2js/mock_compiler.dart
@@ -124,8 +124,9 @@
buildLibrarySource(DEFAULT_INTERCEPTORS_LIBRARY));
registerSource(JavaScriptBackend.DART_ISOLATE_HELPER,
buildLibrarySource(DEFAULT_ISOLATE_HELPER_LIBRARY));
- registerSource(Uris.dart_mirrors,
- buildLibrarySource(DEFAULT_MIRRORS_LIBRARY));
+ registerSource(Uris.dart_mirrors, DEFAULT_MIRRORS_SOURCE);
+ registerSource(JavaScriptBackend.DART_JS_MIRRORS,
+ DEFAULT_JS_MIRRORS_SOURCE);
Map<String, String> asyncLibrarySource = <String, String>{};
asyncLibrarySource.addAll(DEFAULT_ASYNC_LIBRARY);
diff --git a/tests/compiler/dart2js/mock_libraries.dart b/tests/compiler/dart2js/mock_libraries.dart
index 2031cf0..b10aedb 100644
--- a/tests/compiler/dart2js/mock_libraries.dart
+++ b/tests/compiler/dart2js/mock_libraries.dart
@@ -39,7 +39,10 @@
static var NAN = 0;
static parse(s) {}
}''',
- 'Function': 'class Function {}',
+ 'Function': r'''
+ class Function {
+ static apply(Function fn, List positional, [Map named]) => null;
+ }''',
'identical': 'bool identical(Object a, Object b) { return true; }',
'int': 'abstract class int extends num { }',
'Iterable': 'abstract class Iterable {}',
@@ -228,7 +231,12 @@
'throwRuntimeError': 'throwRuntimeError(message) {}',
'throwTypeError': 'throwTypeError(message) {}',
'TypeImpl': 'class TypeImpl {}',
- 'TypeVariable': 'class TypeVariable {}',
+ 'TypeVariable': '''class TypeVariable {
+ final Type owner;
+ final String name;
+ final int bound;
+ TypeVariable(this.owner, this.name, this.bound);
+ }''',
'unwrapException': 'unwrapException(e) {}',
'voidTypeCheck': 'voidTypeCheck(value) {}',
'wrapException': 'wrapException(x) { return x; }',
@@ -393,15 +401,27 @@
'_asyncHelper': '_asyncHelper(o, f, c) {}',
};
-const Map<String, String> DEFAULT_MIRRORS_LIBRARY = const <String, String>{
- 'Comment': 'class Comment {}',
- 'MirrorSystem': 'class MirrorSystem {}',
- 'MirrorsUsed': 'class MirrorsUsed {}',
-};
+const String DEFAULT_MIRRORS_SOURCE = r'''
+import 'dart:_js_mirrors' as js;
+class Comment {}
+class MirrorSystem {}
+class MirrorsUsed {
+ final targets;
+ const MirrorsUsed({this.targets});
+}
+void reflectType(Type t) => js.disableTreeShaking();
+''';
+
+const String DEFAULT_JS_MIRRORS_SOURCE = r'''
+disableTreeShaking(){}
+preserveMetadata(){}
+preserveUris(){}
+preserveLibraryNames(){}
+''';
const Map<String, String> DEFAULT_LOOKUP_MAP_LIBRARY = const <String, String>{
'LookupMap': r'''
- class LookupMap<T> {
+ class LookupMap<K, V> {
final _key;
final _value;
final _entries;
@@ -412,5 +432,7 @@
const LookupMap.pair(this._key, this._value)
: _entries = const [], _nestedMaps = const [];
+ V operator[](K k) => null;
}''',
+ '_version': 'const _version = "0.0.1";',
};
diff --git a/tests/compiler/dart2js/simple_inferrer_const_closure_default_test.dart b/tests/compiler/dart2js/simple_inferrer_const_closure_default_test.dart
new file mode 100644
index 0000000..e6d2e92
--- /dev/null
+++ b/tests/compiler/dart2js/simple_inferrer_const_closure_default_test.dart
@@ -0,0 +1,84 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:expect/expect.dart';
+import "package:async_helper/async_helper.dart";
+import 'compiler_helper.dart';
+import 'type_mask_test_helper.dart';
+
+
+
+const String TEST = """
+
+// [defaultFn_i] is called only via [foo_i]'s default value with a small integer.
+
+defaultFn1(a) => a;
+defaultFn2(a) => a;
+defaultFn3(a) => a;
+defaultFn4(a) => a;
+defaultFn5(a) => a;
+defaultFn6(a) => a;
+
+foo1([fn = defaultFn1]) => fn(54);
+foo2({fn: defaultFn2}) => fn(54);
+foo3([fn = defaultFn3]) => fn(54);
+foo4({fn: defaultFn4}) => fn(54);
+foo5([fn = defaultFn5]) => fn(54);
+foo6({fn: defaultFn6}) => fn(54);
+
+main() {
+ // Direct calls.
+ foo1();
+ foo2();
+ // Indirect calls.
+ (foo3)();
+ (foo4)();
+ // Calls via Function.apply.
+ Function.apply(foo5, []);
+ Function.apply(foo6, []);
+}
+""";
+
+
+void main() {
+ Uri uri = new Uri(scheme: 'source');
+ var compiler = compilerFor(TEST, uri);
+ asyncTest(() => compiler.runCompiler(uri).then((_) {
+ var typesInferrer = compiler.typesTask.typesInferrer;
+
+ checkArgument(String functionName, type) {
+ var functionElement = findElement(compiler, functionName);
+ var signature = functionElement.functionSignature;
+ var element = signature.requiredParameterCount > 0
+ ? signature.requiredParameters.first
+ : signature.optionalParameters.first;
+ Expect.equals(type,
+ simplify(typesInferrer.getTypeOfElement(element), compiler),
+ functionName);
+ }
+
+ checkOptionalArgument(String functionName, type) {
+ var functionElement = findElement(compiler, functionName);
+ var signature = functionElement.functionSignature;
+ var element = signature.optionalParameters.first;
+ Expect.equals(type,
+ simplify(typesInferrer.getTypeOfElement(element), compiler),
+ functionName);
+ }
+
+ checkArgument('foo1', compiler.typesTask.functionType); /// 01: ok
+ checkArgument('foo2', compiler.typesTask.functionType); /// 02: ok
+ checkArgument('foo3', compiler.typesTask.functionType); /// 03: ok
+ checkArgument('foo4', compiler.typesTask.functionType); /// 04: ok
+ checkArgument('foo5', compiler.typesTask.functionType); /// 05: ok
+ checkArgument('foo6', compiler.typesTask.functionType); /// 06: ok
+
+ checkArgument('defaultFn1', compiler.typesTask.uint31Type); /// 07: ok
+ checkArgument('defaultFn2', compiler.typesTask.uint31Type); /// 08: ok
+ checkArgument('defaultFn3', compiler.typesTask.uint31Type); /// 09: ok
+ checkArgument('defaultFn4', compiler.typesTask.uint31Type); /// 10: ok
+ checkArgument('defaultFn5', compiler.typesTask.uint31Type); /// 11: ok
+ checkArgument('defaultFn6', compiler.typesTask.uint31Type); /// 12: ok
+ }));
+}
diff --git a/tests/compiler/dart2js_extra/dart2js_extra.status b/tests/compiler/dart2js_extra/dart2js_extra.status
index b2d49f9..e0321e9 100644
--- a/tests/compiler/dart2js_extra/dart2js_extra.status
+++ b/tests/compiler/dart2js_extra/dart2js_extra.status
@@ -65,11 +65,11 @@
[ $compiler == none && $runtime == vm ]
invalid_annotation_test/01: MissingCompileTimeError, OK # vm is lazy
+lookup_map/dead_entry_through_mirrors_test: SkipByDesign # Test for tree-shaking, vm never tree-shakes
[ $compiler == dart2js && $cps_ir ]
16407_test: Pass # Please triage this failure.
22868_test: RuntimeError # Issue 23997
-22895_test: RuntimeError # Issue 23997
23432_test: RuntimeError # Issue 23432
async_stacktrace_test/asyncStar: Crash # (foo()async*{try {tr... cannot handle sync*/async* functions
async_stacktrace_test/none: RuntimeError # $async$temp1.Tracer$ is not a function
@@ -84,6 +84,7 @@
deferred_fail_and_retry_test: Crash # (lib.foo()): deferred access is not implemented
deferred_fail_and_retry_worker_test: Crash # (lib.foo()): deferred access is not implemented
deferred_split_test: Crash # (b.createA()): deferred access is not implemented
+lookup_map/live_entry_through_mirrors_test: Crash # (static Iterable<Str... cannot handle sync*/async* functions
mirror_printer_test: Crash # (static Iterable<Str... cannot handle sync*/async* functions
mirror_test: Crash # (static Iterable<Str... cannot handle sync*/async* functions
reflect_native_types_test: Crash # (static Iterable<Str... cannot handle sync*/async* functions
diff --git a/tests/compiler/dart2js_extra/lookup_map/dead_entry_through_mirrors_test.dart b/tests/compiler/dart2js_extra/lookup_map/dead_entry_through_mirrors_test.dart
new file mode 100644
index 0000000..6de44b4
--- /dev/null
+++ b/tests/compiler/dart2js_extra/lookup_map/dead_entry_through_mirrors_test.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for 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 dead_entry_through_mirrors_test;
+
+import 'package:lookup_map/lookup_map.dart';
+import 'package:expect/expect.dart';
+
+@MirrorsUsed(targets: const [A])
+import 'dart:mirrors';
+
+class A{}
+class B{}
+const map = const LookupMap(const [
+ A, "the-text-for-A",
+ B, "the-text-for-B",
+]);
+
+main() {
+ LibraryMirror lib = currentMirrorSystem().findLibrary(
+ #dead_entry_through_mirrors_test);
+
+ // `A` is included by @MirrorsUsed, so its entry is retained too.
+ ClassMirror aClass = lib.declarations[#A];
+ Expect.equals(map[aClass.reflectedType], "the-text-for-A");
+
+ // `B` is not included altogether.
+ Expect.equals(lib.declarations[#B], null);
+}
diff --git a/tests/compiler/dart2js_extra/lookup_map/live_entry_through_mirrors_test.dart b/tests/compiler/dart2js_extra/lookup_map/live_entry_through_mirrors_test.dart
new file mode 100644
index 0000000..9ad99cf
--- /dev/null
+++ b/tests/compiler/dart2js_extra/lookup_map/live_entry_through_mirrors_test.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for 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 live_entry_through_mirrors_test;
+
+import 'package:lookup_map/lookup_map.dart';
+import 'package:expect/expect.dart';
+import 'dart:mirrors';
+
+class A{}
+class B{}
+const map = const LookupMap(const [
+ A, "the-text-for-A",
+ B, "the-text-for-B",
+]);
+
+main() {
+ // `A` is referenced explicitly, so its entry should be retained regardless.
+ ClassMirror aClass = reflectClass(A);
+ Expect.equals(map[aClass.reflectedType], "the-text-for-A");
+
+ // `B` is used via mirrors. Because no @MirrorsUsed was found that's enough to
+ // retain the entry.
+ LibraryMirror lib = currentMirrorSystem().findLibrary(
+ #live_entry_through_mirrors_test);
+ ClassMirror bClass = lib.declarations[#B];
+ Expect.equals(map[bClass.reflectedType], "the-text-for-B");
+}
diff --git a/tests/compiler/dart2js_extra/lookup_map/live_entry_through_mirrors_used_test.dart b/tests/compiler/dart2js_extra/lookup_map/live_entry_through_mirrors_used_test.dart
new file mode 100644
index 0000000..c5b752c
--- /dev/null
+++ b/tests/compiler/dart2js_extra/lookup_map/live_entry_through_mirrors_used_test.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Subset of dead_entry_through_mirrors_test that is not affected by
+// tree-shaking. This subset can be run in the VM.
+library live_entry_through_mirrors_used_test;
+
+import 'package:lookup_map/lookup_map.dart';
+import 'package:expect/expect.dart';
+
+@MirrorsUsed(targets: const [A])
+import 'dart:mirrors';
+
+class A{}
+class B{}
+const map = const LookupMap(const [
+ A, "the-text-for-A",
+ B, "the-text-for-B",
+]);
+
+main() {
+ // `A` is included by @MirrorsUsed, so its entry is retained too.
+ LibraryMirror lib = currentMirrorSystem().findLibrary(
+ #live_entry_through_mirrors_used_test);
+ ClassMirror aClass = lib.declarations[#A];
+ Expect.equals(map[aClass.reflectedType], "the-text-for-A");
+}
diff --git a/tests/compiler/dart2js_extra/lookup_map/reachable_data2_test.dart b/tests/compiler/dart2js_extra/lookup_map/reachable_data2_test.dart
new file mode 100644
index 0000000..9c82430
--- /dev/null
+++ b/tests/compiler/dart2js_extra/lookup_map/reachable_data2_test.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:lookup_map/lookup_map.dart';
+import 'package:expect/expect.dart';
+
+class Key { final x; const Key(this.x); }
+const A = const Key(1);
+const B = const Key(2);
+const C = const Key(3);
+const D = const Key(4);
+const E = const Key(5);
+const map = const LookupMap(const [
+ A, const ["the-text-for-A", B],
+ B, const ["the-text-for-B", C],
+ C, const ["the-text-for-C"],
+ D, const ["the-text-for-D", E],
+ E, const ["the-text-for-E"],
+]);
+main() {
+ Expect.equals(map[map[A][1]][0], 'the-text-for-B');
+}
diff --git a/tests/compiler/dart2js_native/dart2js_native.status b/tests/compiler/dart2js_native/dart2js_native.status
index 1ace12b..9d325b2 100644
--- a/tests/compiler/dart2js_native/dart2js_native.status
+++ b/tests/compiler/dart2js_native/dart2js_native.status
@@ -22,7 +22,7 @@
[ $compiler == dart2js && $cps_ir ]
mirror_intercepted_field_test: Crash # (static Iterable<Str... cannot handle sync*/async* functions
-native_exception_test: RuntimeError # value_.toString$0 is not a function
+native_exception_test: RuntimeError # J.getInterceptor(...).toString$0 is not a function
native_method_inlining_test: RuntimeError # Please triage this failure.
native_mirror_test: Crash # (static Iterable<Str... cannot handle sync*/async* functions
native_no_such_method_exception3_frog_test: RuntimeError # Please triage this failure.
diff --git a/tests/corelib/corelib.status b/tests/corelib/corelib.status
index 092e399..56f3f17 100644
--- a/tests/corelib/corelib.status
+++ b/tests/corelib/corelib.status
@@ -218,7 +218,7 @@
big_integer_parsed_mul_div_vm_test: Pass, Slow
[ $compiler == dart2js && $cps_ir ]
-data_resource_test: Crash # bailout: (await for(var byteSlice in resource.openRead()){streamBytes.addAll(byteSlice);}): await for
+data_resource_test: Crash # (await for(var byteSlice in resource.openRead()){streamBytes.addAll(byteSlice);}): await for
error_stack_trace1_test: Pass # H.unwrapException(...).get$stackTrace is not a function
growable_list_test: RuntimeError # Typed lists
iterable_empty_test: RuntimeError # Please triage this failure.
@@ -231,7 +231,6 @@
map_values3_test: RuntimeError # Please triage this failure.
map_values4_test: RuntimeError # Please triage this failure.
package_resource_test: Crash # (await for(var byteSlice in resource.openRead()){streamBytes.addAll(byteSlice);}): await for
-regexp/pcre_test: Crash # Stack Overflow
symbol_operator_test/03: RuntimeError # Please triage this failure.
symbol_reserved_word_test/03: Pass # Please triage this failure.
symbol_reserved_word_test/06: RuntimeError # Please triage this failure.
diff --git a/tests/html/html.status b/tests/html/html.status
index 4d21887..aba5566 100644
--- a/tests/html/html.status
+++ b/tests/html/html.status
@@ -116,6 +116,11 @@
transition_event_test/functional: Skip # Times out. Issue 22167
request_animation_frame_test: Skip # Times out. Issue 22167
+[ $runtime == chrome && $system != linux ]
+# These are failures in Chrome 45, which is not yet on Linux.
+# This will be merged with the earlier blocks when Linux is updated.
+element_animate_test/simple_timing: RuntimeError # Please triage this failure
+
[$runtime == drt || $runtime == dartium || $runtime == chrome || $runtime == chromeOnAndroid || $runtime == ContentShellOnAndroid ]
webgl_1_test: Pass, Fail # Issue 8219
@@ -201,9 +206,6 @@
css_test/supportsPointConversions: Fail # Issues 21710
css_test/functional: Fail # Issues 21710
-[ $compiler == dart2js && $cps_ir ]
-resource_http_test: Crash # Cannot handle async functions.
-
[ $runtime == ie11 ]
custom/document_register_type_extensions_test/single-parameter: Fail # Issue 13193.
canvasrenderingcontext2d_test/arc: Pass, Fail # Pixel unexpected value. Please triage this failure.
@@ -421,3 +423,6 @@
typing_test: StaticWarning
webgl_1_test: StaticWarning
window_nosuchmethod_test: StaticWarning
+
+[ $compiler == dart2js && $cps_ir ]
+resource_http_test: Crash # (await for(var b in r.openRead()){bytes.addAll(b);}): await for
diff --git a/tests/language/language_dart2js.status b/tests/language/language_dart2js.status
index 05eec1c..1ced213 100644
--- a/tests/language/language_dart2js.status
+++ b/tests/language/language_dart2js.status
@@ -287,7 +287,7 @@
await_for_test: Crash # (await for(var x in infiniteStream()){i++ ;if(i>10)break;t4.record(x);}): await for
await_for_use_local_test: Crash # (await for(var v in s){accum+= v;}): await for
await_future_test: RuntimeError # Please triage this failure.
-await_regression_test: RuntimeError # Please triage this failure.
+await_regression_test: RuntimeError # "Obelix".then$1 is not a function
cha_deopt1_test: Crash # (d.make_u()): deferred access is not implemented
cha_deopt2_test: Crash # (d.make_u()): deferred access is not implemented
cha_deopt3_test: Crash # (d.make_u()): deferred access is not implemented
diff --git a/tests/standalone/full_coverage_test.dart b/tests/standalone/full_coverage_test.dart
index 2ee1979..9eab81d 100644
--- a/tests/standalone/full_coverage_test.dart
+++ b/tests/standalone/full_coverage_test.dart
@@ -49,8 +49,8 @@
}
}
''',
- 'expectedHits': [-1, 0, 0, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1,
- 0, -1, 1, -1, -1]
+ 'expectedHits': [-1, 0, 0, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1,
+ -1, 0, -1, 1, -1, -1]
},{
'name': 'closures',
'program': '''
@@ -64,7 +64,7 @@
});
}
''',
- 'expectedHits': [1, -1, 1, -1, -1, 1, 1, -1, -1]
+ 'expectedHits': [-1, -1, 1, -1, -1, 1, 1, -1, -1]
}
];
diff --git a/tests/standalone/io/http_cookie_date_test.dart b/tests/standalone/io/http_cookie_date_test.dart
index 85385b4..bb7a1a2 100644
--- a/tests/standalone/io/http_cookie_date_test.dart
+++ b/tests/standalone/io/http_cookie_date_test.dart
@@ -8,6 +8,7 @@
import "dart:async";
import "dart:collection";
import "dart:convert";
+import "dart:developer";
import "dart:math";
import "dart:typed_data";
import "dart:isolate";
@@ -28,6 +29,7 @@
part "../../../sdk/lib/io/http_parser.dart";
part "../../../sdk/lib/io/http_headers.dart";
part "../../../sdk/lib/io/http_session.dart";
+part "../../../sdk/lib/io/io_resource_info.dart";
part "../../../sdk/lib/io/io_service.dart";
part "../../../sdk/lib/io/io_sink.dart";
part "../../../sdk/lib/io/platform.dart";
diff --git a/tests/standalone/io/http_headers_test.dart b/tests/standalone/io/http_headers_test.dart
index 6e2a4ef..6dbdcd4 100644
--- a/tests/standalone/io/http_headers_test.dart
+++ b/tests/standalone/io/http_headers_test.dart
@@ -8,6 +8,7 @@
import "dart:async";
import "dart:collection";
import "dart:convert";
+import "dart:developer";
import "dart:math";
import "dart:typed_data";
import "dart:isolate";
@@ -28,6 +29,7 @@
part "../../../sdk/lib/io/http_parser.dart";
part "../../../sdk/lib/io/http_headers.dart";
part "../../../sdk/lib/io/http_session.dart";
+part "../../../sdk/lib/io/io_resource_info.dart";
part "../../../sdk/lib/io/io_service.dart";
part "../../../sdk/lib/io/io_sink.dart";
part "../../../sdk/lib/io/platform.dart";
diff --git a/tests/standalone/io/http_parser_test.dart b/tests/standalone/io/http_parser_test.dart
index 9d8e014..95edf3b 100644
--- a/tests/standalone/io/http_parser_test.dart
+++ b/tests/standalone/io/http_parser_test.dart
@@ -8,6 +8,7 @@
import "dart:async";
import "dart:collection";
import "dart:convert";
+import "dart:developer";
import "dart:math";
import "dart:typed_data";
import "dart:isolate";
@@ -28,6 +29,7 @@
part "../../../sdk/lib/io/http_parser.dart";
part "../../../sdk/lib/io/http_headers.dart";
part "../../../sdk/lib/io/http_session.dart";
+part "../../../sdk/lib/io/io_resource_info.dart";
part "../../../sdk/lib/io/io_service.dart";
part "../../../sdk/lib/io/io_sink.dart";
part "../../../sdk/lib/io/platform.dart";
diff --git a/tests/standalone/io/web_socket_protocol_processor_test.dart b/tests/standalone/io/web_socket_protocol_processor_test.dart
index ba75622..0f7c1fa 100644
--- a/tests/standalone/io/web_socket_protocol_processor_test.dart
+++ b/tests/standalone/io/web_socket_protocol_processor_test.dart
@@ -9,6 +9,7 @@
import "dart:async";
import "dart:collection";
import "dart:convert";
+import "dart:developer";
import "dart:math";
import "dart:typed_data";
import "dart:isolate";
@@ -29,6 +30,7 @@
part "../../../sdk/lib/io/http_parser.dart";
part "../../../sdk/lib/io/http_headers.dart";
part "../../../sdk/lib/io/http_session.dart";
+part "../../../sdk/lib/io/io_resource_info.dart";
part "../../../sdk/lib/io/io_service.dart";
part "../../../sdk/lib/io/io_sink.dart";
part "../../../sdk/lib/io/platform.dart";
diff --git a/tests/standalone/standalone.status b/tests/standalone/standalone.status
index 9bb7ec3..b6e570b 100644
--- a/tests/standalone/standalone.status
+++ b/tests/standalone/standalone.status
@@ -186,16 +186,8 @@
io/test_runner_test: Skip # Timeout.
io/http_client_stays_alive_test: Skip # Timeout.
-[ $compiler == dart2js && $cps_ir ]
-coverage_test: Crash # (static Iterable<Str... cannot handle sync*/async* functions
-io/addlatexhash_test: Crash # (static Iterable<Str... cannot handle sync*/async* functions
-io/file_error_test: Crash # (static Iterable<Str... cannot handle sync*/async* functions
-io/file_read_encoded_test: Crash # (static Iterable<Str... cannot handle sync*/async* functions
-io/file_test: Crash # (static Iterable<Str... cannot handle sync*/async* functions
-io/http_client_connect_test: Crash # (static Iterable<Str... cannot handle sync*/async* functions
-io/observatory_test: Crash # (static Iterable<Str... cannot handle sync*/async* functions
-io/skipping_dart2js_compilations_test: Crash # (static Iterable<Str... cannot handle sync*/async* functions
-priority_queue_stress_test: RuntimeError # Cannot read property 'length' of undefined
+[ $compiler == dart2js ]
+io/observatory_test: Crash # Issue 24291
[ $runtime == vm ]
# Failures in secure networking while NSS is replaced with BoringSSL
@@ -204,3 +196,13 @@
io/secure_server_client_no_certificate_test: RuntimeError # Issue 24069
io/secure_socket_renegotiate_test: RuntimeError
io/secure_socket_bad_data_test: RuntimeError # An error in a secure connection just puts a READ_CLOSED on the stream, rather than signaling an error on the stream.
+
+[ $compiler == dart2js && $cps_ir ]
+coverage_test: Crash # (static Iterable<Str... cannot handle sync*/async* functions
+io/addlatexhash_test: Crash # (static Iterable<Str... cannot handle sync*/async* functions
+io/file_error_test: Crash # (static Iterable<Str... cannot handle sync*/async* functions
+io/file_read_encoded_test: Crash # (static Iterable<Str... cannot handle sync*/async* functions
+io/file_test: Crash # (static Iterable<Str... cannot handle sync*/async* functions
+io/http_client_connect_test: Crash # (static Iterable<Str... cannot handle sync*/async* functions
+io/skipping_dart2js_compilations_test: Crash # (static Iterable<Str... cannot handle sync*/async* functions
+priority_queue_stress_test: RuntimeError # Cannot read property 'length' of undefined
diff --git a/tools/VERSION b/tools/VERSION
index 5ef9ccf..03c8deb 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 1
MINOR 13
PATCH 0
-PRERELEASE 1
+PRERELEASE 2
PRERELEASE_PATCH 0
diff --git a/tools/precompilation/create_instructions_snapshot_assembly.dart b/tools/precompilation/create_instructions_snapshot_assembly.dart
deleted file mode 100644
index 98cccb2..0000000
--- a/tools/precompilation/create_instructions_snapshot_assembly.dart
+++ /dev/null
@@ -1,21 +0,0 @@
-import 'dart:io';
-
-void main(List<String> args) {
- print(args[0]);
- print(args[1]);
-
- var bytes = new File(args[0]).readAsBytesSync();
- print(bytes.length);
-
- var out = new StringBuffer();
- out.writeln(".text");
- out.writeln(" .globl _kInstructionsSnapshot");
- out.writeln("_kInstructionsSnapshot:");
- out.writeln(" .balign 32, 0");
- for (var i = 0; i < bytes.length; i++) {
- var byte = bytes[i];
- out.writeln(" .byte $byte");
- }
-
- new File(args[1]).writeAsString(out.toString());
-}
diff --git a/tools/precompilation/test_linux.sh b/tools/precompilation/test_linux.sh
index 14f7fa4..e8839e1 100755
--- a/tools/precompilation/test_linux.sh
+++ b/tools/precompilation/test_linux.sh
@@ -8,10 +8,6 @@
./out/DebugX64/dart_no_snapshot --gen-precompiled-snapshot ~/hello.dart
-./out/DebugX64/dart ./tools/precompilation/create_instructions_snapshot_assembly.dart precompiled.instructions precompiled.S
-
-gcc -m64 -c -o precompiled.o precompiled.S
-
-gcc -m64 -shared -Wl,-soname,libprecompiled.so -o libprecompiled.so precompiled.o
+gcc -m64 -shared -Wl,-soname,libprecompiled.so -o libprecompiled.so precompiled.S
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$PWD" gdb -ex run --args ./out/DebugX64/dart --run-precompiled-snapshot not_used.dart
diff --git a/tools/precompilation/test_linux_simarm.sh b/tools/precompilation/test_linux_simarm.sh
new file mode 100755
index 0000000..b55a5e9
--- /dev/null
+++ b/tools/precompilation/test_linux_simarm.sh
@@ -0,0 +1,13 @@
+#!/bin/bash -ex
+
+# Usage:
+# cd sdk
+# ./tools/precompilation/test_linux.sh
+
+./tools/build.py -mdebug -asimarm runtime
+
+./out/DebugSIMARM/dart_no_snapshot --gen-precompiled-snapshot ~/hello.dart
+
+gcc -m32 -shared -Wl,-soname,libprecompiled.so -o libprecompiled.so precompiled.S
+
+LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$PWD" gdb -ex run --args ./out/DebugSIMARM/dart --run-precompiled-snapshot not_used.dart
diff --git a/tools/precompilation/test_macos.sh b/tools/precompilation/test_macos.sh
index a233004..48e318d 100755
--- a/tools/precompilation/test_macos.sh
+++ b/tools/precompilation/test_macos.sh
@@ -8,8 +8,6 @@
./xcodebuild/DebugX64/dart_no_snapshot --gen-precompiled-snapshot ~/hello.dart
-./xcodebuild/DebugX64/dart ./tools/precompilation/create_instructions_snapshot_assembly.dart precompiled.instructions precompiled.S
-
clang -m64 -dynamiclib -o libprecompiled.dylib precompiled.S
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$PWD" lldb -- ./xcodebuild/DebugX64/dart --run-precompiled-snapshot not_used.dart