Merge remote-tracking branch 'origin/master' into analyzer-breaking-0.27

This commit is contained in:
Paul Berry 2015-12-03 14:19:29 -08:00
commit 7d3c4743db
70 changed files with 1307 additions and 313 deletions

View file

@ -19,6 +19,33 @@
* `Platform` added an `isiOS` getter and `Platform.operatingSystem` may now
return `ios`.
### Tool changes
* Pub
* **Breaking:** Pub now eagerly emits an error when a pubspec's "name" field
is not a valid Dart identifier. Since packages with non-identifier names
were never allowed to be published, and some of them already caused crashes
when being written to a `.packages` file, this is unlikely to break many
people in practice.
* `pub serve` now GZIPs the assets it serves to make load times more similar
to real-world use-cases.
* `pub deps` now supports a `--no-dev` flag, which causes it to emit the
dependency tree as it would be if no `dev_dependencies` were in use. This
makes it easier to see your package's dependency footprint as your users
will experience it.
* `pub global run` now detects when a global executable's SDK constraint is no
longer met and errors out, rather than trying to run the executable anyway.
* Fixed a crashing bug when using `pub global run` on a global script that
didn't exist.
* Fixed a crashing bug when a pubspec contains a dependency without a source
declared.
## 1.13.0 - 2015-11-18
### Core library changes

8
DEPS
View file

@ -67,14 +67,14 @@ vars = {
"intl_rev": "@32047558bd220a53c1f4d93a26d54b83533b1475",
"jinja2_rev": "@2222b31554f03e62600cd7e383376a7c187967a1",
"json_rpc_2_tag": "@1.1.1",
"linter_rev": "@afde8f4e2325b234db5c23f99b8386e4f8e10145",
"linter_rev": "@e5281475126efdc556c3eba6a8b6683fd814b033",
"logging_rev": "@85d83e002670545e9039ad3985f0018ab640e597",
"markdown_rev": "@4aaadf3d940bb172e1f6285af4d2b1710d309982",
"matcher_tag": "@0.12.0",
"metatest_rev": "@e5aa8e4e19fc4188ac2f6d38368a47d8f07c3df1",
"mime_rev": "@75890811d4af5af080351ba8a2853ad4c8df98dd",
"mustache4dart_rev" : "@5724cfd85151e5b6b53ddcd3380daf188fe47f92",
"oauth2_rev": "@1bff41f4d54505c36f2d1a001b83b8b745c452f5",
"oauth2_tag": "@1.0.0",
"observe_rev": "@eee2b8ec34236fa46982575fbccff84f61202ac6",
"observatory_pub_packages_rev": "@5c199c5954146747f75ed127871207718dc87786",
"package_config_rev": "@0.1.3",
@ -83,7 +83,7 @@ vars = {
"ply_rev": "@604b32590ffad5cbb82e4afef1d305512d06ae93",
"plugin_tag": "@0.1.0",
"pool_rev": "@e454b4b54d2987e8d2f0fbd3ac519641ada9bd0f",
"pub_rev": "@1c08b841158e33b8090bb07e5c39df830db58d44",
"pub_rev": "@8c091bf6332e8b392fdac63ae297426fb65ed925",
"pub_cache_tag": "@v0.1.0",
"pub_semver_tag": "@1.2.1",
"quiver_tag": "@0.21.4",
@ -230,7 +230,7 @@ deps = {
+ "/external/github.com/valotas/mustache4dart.git"
+ Var("mustache4dart_rev"),
Var("dart_root") + "/third_party/pkg/oauth2":
(Var("github_mirror") % "oauth2") + Var("oauth2_rev"),
(Var("github_mirror") % "oauth2") + Var("oauth2_tag"),
Var("dart_root") + "/third_party/pkg/observe":
(Var("github_mirror") % "observe") + Var("observe_rev"),
Var("dart_root") + "/third_party/observatory_pub_packages":

File diff suppressed because one or more lines are too long

View file

@ -938,6 +938,176 @@ class AnalysisGetHoverResult implements HasToJson {
return JenkinsSmiHash.finish(hash);
}
}
/**
* analysis.getReachableSources params
*
* {
* "file": FilePath
* }
*
* Clients may not extend, implement or mix-in this class.
*/
class AnalysisGetReachableSourcesParams implements HasToJson {
String _file;
/**
* The file for which reachable source information is being requested.
*/
String get file => _file;
/**
* The file for which reachable source information is being requested.
*/
void set file(String value) {
assert(value != null);
this._file = value;
}
AnalysisGetReachableSourcesParams(String file) {
this.file = file;
}
factory AnalysisGetReachableSourcesParams.fromJson(JsonDecoder jsonDecoder, String jsonPath, Object json) {
if (json == null) {
json = {};
}
if (json is Map) {
String file;
if (json.containsKey("file")) {
file = jsonDecoder.decodeString(jsonPath + ".file", json["file"]);
} else {
throw jsonDecoder.missingKey(jsonPath, "file");
}
return new AnalysisGetReachableSourcesParams(file);
} else {
throw jsonDecoder.mismatch(jsonPath, "analysis.getReachableSources params", json);
}
}
factory AnalysisGetReachableSourcesParams.fromRequest(Request request) {
return new AnalysisGetReachableSourcesParams.fromJson(
new RequestDecoder(request), "params", request._params);
}
Map<String, dynamic> toJson() {
Map<String, dynamic> result = {};
result["file"] = file;
return result;
}
Request toRequest(String id) {
return new Request(id, "analysis.getReachableSources", toJson());
}
@override
String toString() => JSON.encode(toJson());
@override
bool operator==(other) {
if (other is AnalysisGetReachableSourcesParams) {
return file == other.file;
}
return false;
}
@override
int get hashCode {
int hash = 0;
hash = JenkinsSmiHash.combine(hash, file.hashCode);
return JenkinsSmiHash.finish(hash);
}
}
/**
* analysis.getReachableSources result
*
* {
* "sources": Map<String, List<String>>
* }
*
* Clients may not extend, implement or mix-in this class.
*/
class AnalysisGetReachableSourcesResult implements HasToJson {
Map<String, List<String>> _sources;
/**
* A mapping from source URIs to directly reachable source URIs. For example,
* a file "foo.dart" that imports "bar.dart" would have the corresponding
* mapping { "file:///foo.dart" : ["file:///bar.dart"] }. If "bar.dart" has
* further imports (or exports) there will be a mapping from the URI
* "file:///bar.dart" to them. To check if a specific URI is reachable from a
* given file, clients can check for its presence in the resulting key set.
*/
Map<String, List<String>> get sources => _sources;
/**
* A mapping from source URIs to directly reachable source URIs. For example,
* a file "foo.dart" that imports "bar.dart" would have the corresponding
* mapping { "file:///foo.dart" : ["file:///bar.dart"] }. If "bar.dart" has
* further imports (or exports) there will be a mapping from the URI
* "file:///bar.dart" to them. To check if a specific URI is reachable from a
* given file, clients can check for its presence in the resulting key set.
*/
void set sources(Map<String, List<String>> value) {
assert(value != null);
this._sources = value;
}
AnalysisGetReachableSourcesResult(Map<String, List<String>> sources) {
this.sources = sources;
}
factory AnalysisGetReachableSourcesResult.fromJson(JsonDecoder jsonDecoder, String jsonPath, Object json) {
if (json == null) {
json = {};
}
if (json is Map) {
Map<String, List<String>> sources;
if (json.containsKey("sources")) {
sources = jsonDecoder.decodeMap(jsonPath + ".sources", json["sources"], valueDecoder: (String jsonPath, Object json) => jsonDecoder.decodeList(jsonPath, json, jsonDecoder.decodeString));
} else {
throw jsonDecoder.missingKey(jsonPath, "sources");
}
return new AnalysisGetReachableSourcesResult(sources);
} else {
throw jsonDecoder.mismatch(jsonPath, "analysis.getReachableSources result", json);
}
}
factory AnalysisGetReachableSourcesResult.fromResponse(Response response) {
return new AnalysisGetReachableSourcesResult.fromJson(
new ResponseDecoder(REQUEST_ID_REFACTORING_KINDS.remove(response.id)), "result", response._result);
}
Map<String, dynamic> toJson() {
Map<String, dynamic> result = {};
result["sources"] = sources;
return result;
}
Response toResponse(String id) {
return new Response(id, result: toJson());
}
@override
String toString() => JSON.encode(toJson());
@override
bool operator==(other) {
if (other is AnalysisGetReachableSourcesResult) {
return mapEqual(sources, other.sources, (List<String> a, List<String> b) => listEqual(a, b, (String a, String b) => a == b));
}
return false;
}
@override
int get hashCode {
int hash = 0;
hash = JenkinsSmiHash.combine(hash, sources.hashCode);
return JenkinsSmiHash.finish(hash);
}
}
/**
* analysis.getLibraryDependencies params
*
@ -13920,6 +14090,7 @@ class RequestError implements HasToJson {
* FORMAT_WITH_ERRORS
* GET_ERRORS_INVALID_FILE
* GET_NAVIGATION_INVALID_FILE
* GET_REACHABLE_SOURCES_INVALID_FILE
* INVALID_ANALYSIS_ROOT
* INVALID_EXECUTION_CONTEXT
* INVALID_OVERLAY_CHANGE
@ -13977,6 +14148,12 @@ class RequestErrorCode implements Enum {
*/
static const GET_NAVIGATION_INVALID_FILE = const RequestErrorCode._("GET_NAVIGATION_INVALID_FILE");
/**
* An "analysis.getReachableSources" request specified a FilePath which does
* not match a file currently subject to analysis.
*/
static const GET_REACHABLE_SOURCES_INVALID_FILE = const RequestErrorCode._("GET_REACHABLE_SOURCES_INVALID_FILE");
/**
* A path passed as an argument to a request (such as analysis.reanalyze) is
* required to be an analysis root, but isn't.
@ -14083,7 +14260,7 @@ class RequestErrorCode implements Enum {
/**
* A list containing all of the enum values that are defined.
*/
static const List<RequestErrorCode> VALUES = const <RequestErrorCode>[CONTENT_MODIFIED, FILE_NOT_ANALYZED, FORMAT_INVALID_FILE, FORMAT_WITH_ERRORS, GET_ERRORS_INVALID_FILE, GET_NAVIGATION_INVALID_FILE, INVALID_ANALYSIS_ROOT, INVALID_EXECUTION_CONTEXT, INVALID_OVERLAY_CHANGE, INVALID_PARAMETER, INVALID_REQUEST, NO_INDEX_GENERATED, ORGANIZE_DIRECTIVES_ERROR, REFACTORING_REQUEST_CANCELLED, SERVER_ALREADY_STARTED, SERVER_ERROR, SORT_MEMBERS_INVALID_FILE, SORT_MEMBERS_PARSE_ERRORS, UNANALYZED_PRIORITY_FILES, UNKNOWN_REQUEST, UNKNOWN_SOURCE, UNSUPPORTED_FEATURE];
static const List<RequestErrorCode> VALUES = const <RequestErrorCode>[CONTENT_MODIFIED, FILE_NOT_ANALYZED, FORMAT_INVALID_FILE, FORMAT_WITH_ERRORS, GET_ERRORS_INVALID_FILE, GET_NAVIGATION_INVALID_FILE, GET_REACHABLE_SOURCES_INVALID_FILE, INVALID_ANALYSIS_ROOT, INVALID_EXECUTION_CONTEXT, INVALID_OVERLAY_CHANGE, INVALID_PARAMETER, INVALID_REQUEST, NO_INDEX_GENERATED, ORGANIZE_DIRECTIVES_ERROR, REFACTORING_REQUEST_CANCELLED, SERVER_ALREADY_STARTED, SERVER_ERROR, SORT_MEMBERS_INVALID_FILE, SORT_MEMBERS_PARSE_ERRORS, UNANALYZED_PRIORITY_FILES, UNKNOWN_REQUEST, UNKNOWN_SOURCE, UNSUPPORTED_FEATURE];
final String name;
@ -14103,6 +14280,8 @@ class RequestErrorCode implements Enum {
return GET_ERRORS_INVALID_FILE;
case "GET_NAVIGATION_INVALID_FILE":
return GET_NAVIGATION_INVALID_FILE;
case "GET_REACHABLE_SOURCES_INVALID_FILE":
return GET_REACHABLE_SOURCES_INVALID_FILE;
case "INVALID_ANALYSIS_ROOT":
return INVALID_ANALYSIS_ROOT;
case "INVALID_EXECUTION_CONTEXT":

View file

@ -410,6 +410,16 @@ class Response {
RequestErrorCode.GET_NAVIGATION_INVALID_FILE,
'Error during `analysis.getNavigation`: invalid file.'));
/**
* Initialize a newly created instance to represent the
* GET_REACHABLE_SOURCES_INVALID_FILE error condition.
*/
Response.getReachableSourcesInvalidFile(Request request)
: this(request.id,
error: new RequestError(
RequestErrorCode.GET_REACHABLE_SOURCES_INVALID_FILE,
'Error during `analysis.getReachableSources`: invalid file.'));
/**
* Initialize a newly created instance to represent an error condition caused
* by an analysis.reanalyze [request] that specifies an analysis root that is

View file

@ -26,6 +26,7 @@ const String ANALYSIS_GET_HOVER = 'analysis.getHover';
const String ANALYSIS_GET_LIBRARY_DEPENDENCIES =
'analysis.getLibraryDependencies';
const String ANALYSIS_GET_NAVIGATION = 'analysis.getNavigation';
const String ANALYSIS_GET_REACHABLE_SOURCES = 'analysis.getReachableSources';
const String ANALYSIS_REANALYZE = 'analysis.reanalyze';
const String ANALYSIS_SET_ANALYSIS_ROOTS = 'analysis.setAnalysisRoots';
const String ANALYSIS_SET_GENERAL_SUBSCRIPTIONS =

View file

@ -21,6 +21,7 @@ import 'package:analysis_server/src/operation/operation_analysis.dart'
import 'package:analysis_server/src/protocol/protocol_internal.dart';
import 'package:analysis_server/src/protocol_server.dart';
import 'package:analysis_server/src/services/dependencies/library_dependencies.dart';
import 'package:analysis_server/src/services/dependencies/reachable_source_collector.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
@ -164,6 +165,22 @@ class AnalysisDomainHandler implements RequestHandler {
return Response.DELAYED_RESPONSE;
}
/**
* Implement the `analysis.getReachableSources` request.
*/
Response getReachableSources(Request request) {
AnalysisGetReachableSourcesParams params =
new AnalysisGetReachableSourcesParams.fromRequest(request);
ContextSourcePair pair = server.getContextSourcePair(params.file);
if (pair.context == null || pair.source == null) {
return new Response.getReachableSourcesInvalidFile(request);
}
Map<String, List<String>> sources = new ReachableSourceCollector(
pair.source, pair.context).collectSources();
return new AnalysisGetReachableSourcesResult(sources)
.toResponse(request.id);
}
@override
Response handleRequest(Request request) {
try {
@ -176,6 +193,8 @@ class AnalysisDomainHandler implements RequestHandler {
return getLibraryDependencies(request);
} else if (requestName == ANALYSIS_GET_NAVIGATION) {
return getNavigation(request);
} else if (requestName == ANALYSIS_GET_REACHABLE_SOURCES) {
return getReachableSources(request);
} else if (requestName == ANALYSIS_REANALYZE) {
return reanalyze(request);
} else if (requestName == ANALYSIS_SET_ANALYSIS_ROOTS) {

View file

@ -101,7 +101,7 @@ class CompletionDomainHandler implements RequestHandler {
}
_discardManager();
}
_manager = createCompletionManager(context, source, searchEngine);
_manager = createCompletionManager(server, context, source);
if (context != null) {
_sourcesChangedSubscription =
context.onSourcesChanged.listen(sourcesChanged);
@ -123,8 +123,9 @@ class CompletionDomainHandler implements RequestHandler {
}
CompletionManager createCompletionManager(
AnalysisContext context, Source source, SearchEngine searchEngine) {
return new CompletionManager.create(context, source, searchEngine);
AnalysisServer server, AnalysisContext context, Source source) {
return new CompletionManager.create(context, source, server.searchEngine,
server.serverPlugin.completionContributors);
}
@override

View file

@ -189,8 +189,9 @@ class ServerPlugin implements Plugin {
* Return a list containing all of the completion contributors that were
* contributed.
*/
List<CompletionContributor> get completionContributors =>
completionContributorExtensionPoint.extensions;
Iterable<CompletionContributor> get completionContributors =>
completionContributorExtensionPoint.extensions
.map((CompletionContributorFactory factory) => factory());
/**
* Return a list containing all of the fix contributors that were contributed.
@ -370,10 +371,10 @@ class ServerPlugin implements Plugin {
* valid completion contributor.
*/
void _validateCompletionContributorExtension(Object extension) {
if (extension is! CompletionContributor) {
if (extension is! CompletionContributorFactory) {
String id = completionContributorExtensionPoint.uniqueIdentifier;
throw new ExtensionError(
'Extensions to $id must be an CompletionContributor');
'Extensions to $id must be an CompletionContributorFactory');
}
}

View file

@ -6,12 +6,13 @@
* Support for client code that extends the analysis server by adding new code
* completion contributors.
*
* Plugins can register completion contributors. The registered contributors
* will be used to get completions any time a client issues a
* 'completion.getSuggestions' request.
* Plugins can register completion contributor factories.
* The registered contributor factories will be used to instantiate new
* contributors to get completions any time a client issues
* a 'completion.getSuggestions' request.
*
* If a plugin wants to add completions, it should implement the class
* [CompletionContributor] and then register the contributor by including code
* If a plugin wants to add completions, it should implement
* [CompletionContributorFactory] by including code
* like the following in the plugin's registerExtensions method:
*
* @override
@ -19,20 +20,19 @@
* ...
* registerExtension(
* COMPLETION_CONTRIBUTOR_EXTENSION_POINT_ID,
* new MyCompletionContributor());
* () => new MyCompletionContributor());
* ...
* }
*/
library analysis_server.src.provisional.completion.completion;
library analysis_server.src.provisional.completion.core;
import 'package:analysis_server/src/plugin/server_plugin.dart';
import 'package:analysis_server/src/provisional/completion/completion_core.dart';
import 'package:plugin/plugin.dart';
/**
* The identifier of the extension point that allows plugins to register code
* completion contributors. The object used as an extension must be a
* [CompletionContributor].
* [CompletionContributorFactory].
*/
final String COMPLETION_CONTRIBUTOR_EXTENSION_POINT_ID = Plugin.join(
ServerPlugin.UNIQUE_IDENTIFIER,

View file

@ -18,6 +18,13 @@ import 'package:analyzer/src/generated/source.dart';
*/
const EMPTY_LIST = const <CompletionSuggestion>[];
/**
* An object used to instantiate a [CompletionContributor] instance
* for each 'completion.getSuggestions' request.
* Contributors should *not* be cached between requests.
*/
typedef CompletionContributor CompletionContributorFactory();
/**
* An object used to produce completions at a specific location within a file.
*

View file

@ -24,7 +24,7 @@
* ...
* }
*/
library analysis_server.src.provisional.completion.completion;
library analysis_server.src.provisional.completion.dart;
import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
import 'package:analysis_server/src/provisional/completion/dart/completion_plugin.dart';
@ -33,7 +33,7 @@ import 'package:plugin/plugin.dart';
/**
* The identifier of the extension point that allows plugins to register code
* completion contributors. The object used as an extension must be a
* [DartCompletionContributor].
* [DartCompletionContributorFactory].
*/
final String DART_COMPLETION_CONTRIBUTOR_EXTENSION_POINT_ID = Plugin.join(
DartCompletionPlugin.UNIQUE_IDENTIFIER,

View file

@ -4,8 +4,10 @@
library analysis_server.src.provisional.completion.dart.plugin;
import 'package:analysis_server/src/provisional/completion/completion.dart';
import 'package:analysis_server/src/provisional/completion/dart/completion.dart';
import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
import 'package:analysis_server/src/services/completion/dart/keyword_contributor.dart';
import 'package:plugin/plugin.dart';
@ -38,11 +40,11 @@ class DartCompletionPlugin implements Plugin {
String get uniqueIdentifier => UNIQUE_IDENTIFIER;
/**
* Return a list containing all of the Dart specific completion contributor
* factories that were contributed.
* Return a list containing all of the Dart specific completion contributors.
*/
List<DartCompletionContributorFactory> get contributorFactories =>
_contributorExtensionPoint.extensions;
Iterable<DartCompletionContributorFactory> get contributors =>
_contributorExtensionPoint.extensions
.map((DartCompletionContributorFactory factory) => factory());
@override
void registerExtensionPoints(RegisterExtensionPoint registerExtensionPoint) {
@ -53,6 +55,15 @@ class DartCompletionPlugin implements Plugin {
@override
void registerExtensions(RegisterExtension registerExtension) {
//
// Register DartCompletionManager as a CompletionContributor
// which delegates to all the DartCompletionContributors
//
registerExtension(COMPLETION_CONTRIBUTOR_EXTENSION_POINT_ID,
() => new DartCompletionManager());
//
// Register the default DartCompletionContributors
//
registerExtension(DART_COMPLETION_CONTRIBUTOR_EXTENSION_POINT_ID,
() => new KeywordContributor());
}

View file

@ -198,6 +198,12 @@ class _ArgSuggestionBuilder {
}
}
}
if (parent is Annotation) {
Element element = parent.element;
if (element is ExecutableElement) {
_addSuggestions(element.parameters);
}
}
return new Future.value(false);
}

View file

@ -8,7 +8,7 @@ import 'dart:async';
import 'package:analysis_server/plugin/protocol/protocol.dart';
import 'package:analysis_server/src/provisional/completion/completion_core.dart'
show CompletionRequest, CompletionResult;
show CompletionContributor, CompletionContributorFactory, CompletionRequest, CompletionResult;
import 'package:analysis_server/src/services/completion/dart_completion_manager.dart';
import 'package:analysis_server/src/services/search/search_engine.dart';
import 'package:analyzer/src/generated/engine.dart';
@ -57,14 +57,14 @@ abstract class CompletionManager {
* Create a manager for the given request.
*/
factory CompletionManager.create(
AnalysisContext context, Source source, SearchEngine searchEngine) {
AnalysisContext context,
Source source,
SearchEngine searchEngine,
Iterable<CompletionContributor> newContributors) {
if (context != null) {
if (AnalysisEngine.isDartFileName(source.shortName)) {
return new DartCompletionManager.create(context, searchEngine, source);
}
if (AnalysisEngine.isHtmlFileName(source.shortName)) {
//TODO (danrubel) implement
// return new HtmlCompletionManager(context, searchEngine, source, offset);
return new DartCompletionManager.create(
context, searchEngine, source, newContributors);
}
}
return new NoOpCompletionManager(source);

View file

@ -44,18 +44,10 @@ class DartCompletionManager implements CompletionContributor {
*/
Future<List<CompletionSuggestion>> _computeDartSuggestions(
DartCompletionRequest request) async {
// Build the Dart specific completion contributors
List<DartCompletionContributor> contributors =
<DartCompletionContributor>[];
for (DartCompletionContributorFactory contributorFactory
in dartCompletionPlugin.contributorFactories) {
contributors.add(contributorFactory());
}
// Request Dart specific completions from each contributor
List<CompletionSuggestion> suggestions = <CompletionSuggestion>[];
for (DartCompletionContributor contributor in contributors) {
suggestions.addAll(await contributor.computeSuggestions(request));
for (DartCompletionContributor c in dartCompletionPlugin.contributors) {
suggestions.addAll(await c.computeSuggestions(request));
}
return suggestions;
}

View file

@ -15,8 +15,6 @@ import 'package:analysis_server/src/services/completion/combinator_contributor.d
import 'package:analysis_server/src/services/completion/completion_core.dart';
import 'package:analysis_server/src/services/completion/completion_manager.dart';
import 'package:analysis_server/src/services/completion/dart/common_usage_sorter.dart';
import 'package:analysis_server/src/services/completion/dart/completion_manager.dart'
as newImpl;
import 'package:analysis_server/src/services/completion/dart/contribution_sorter.dart';
import 'package:analysis_server/src/services/completion/dart_completion_cache.dart';
import 'package:analysis_server/src/services/completion/imported_reference_contributor.dart';
@ -87,7 +85,7 @@ class DartCompletionManager extends CompletionManager {
final SearchEngine searchEngine;
final DartCompletionCache cache;
List<DartCompletionContributor> contributors;
List<CompletionContributor> newContributors;
Iterable<CompletionContributor> newContributors;
DartContributionSorter contributionSorter;
DartCompletionManager(
@ -114,7 +112,7 @@ class DartCompletionManager extends CompletionManager {
if (newContributors == null) {
newContributors = <CompletionContributor>[
// TODO(danrubel) initialize using plugin API
new newImpl.DartCompletionManager(),
//new newImpl.DartCompletionManager(),
];
}
if (contributionSorter == null) {
@ -126,9 +124,12 @@ class DartCompletionManager extends CompletionManager {
* Create a new initialized Dart source completion manager
*/
factory DartCompletionManager.create(
AnalysisContext context, SearchEngine searchEngine, Source source) {
AnalysisContext context,
SearchEngine searchEngine,
Source source,
Iterable<CompletionContributor> newContributors) {
return new DartCompletionManager(context, searchEngine, source,
new DartCompletionCache(context, source));
new DartCompletionCache(context, source), null, newContributors);
}
@override

View file

@ -127,6 +127,8 @@ class DartFixKind {
static const CHANGE_TO = const FixKind('CHANGE_TO', 49, "Change to '{0}'");
static const CHANGE_TO_STATIC_ACCESS = const FixKind(
'CHANGE_TO_STATIC_ACCESS', 50, "Change access to static using '{0}'");
static const CHANGE_TYPE_ANNOTATION = const FixKind(
'CHANGE_TYPE_ANNOTATION', 50, "Change '{0}' to '{1}' type annotation");
static const CREATE_CLASS =
const FixKind('CREATE_CLASS', 50, "Create class '{0}'");
static const CREATE_CONSTRUCTOR =

View file

@ -292,6 +292,9 @@ class FixProcessor {
_addFix_useStaticAccess_method();
_addFix_useStaticAccess_property();
}
if (errorCode == StaticTypeWarningCode.INVALID_ASSIGNMENT) {
_addFix_changeTypeAnnotation();
}
if (errorCode == StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION) {
_addFix_removeParentheses_inGetterInvocation();
}
@ -489,6 +492,29 @@ class FixProcessor {
}
}
void _addFix_changeTypeAnnotation() {
AstNode declaration = coveredNode.parent;
if (declaration is VariableDeclaration &&
declaration.initializer == coveredNode) {
AstNode variableList = declaration.parent;
if (variableList is VariableDeclarationList &&
variableList.variables.length == 1) {
TypeName typeNode = variableList.type;
if (typeNode != null) {
Expression initializer = coveredNode;
DartType newType = initializer.bestType;
if (newType is InterfaceType || newType is FunctionType) {
String newTypeSource =
utils.getTypeSource(newType, librariesToImport);
_addReplaceEdit(rf.rangeNode(typeNode), newTypeSource);
_addFix(DartFixKind.CHANGE_TYPE_ANNOTATION,
[typeNode.type.displayName, newTypeSource]);
}
}
}
}
}
void _addFix_createClass() {
Element prefixElement = null;
String name = null;

View file

@ -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.
library services.dependencies.reachable_source_collector;
import 'dart:collection';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/task/dart.dart';
/// Collects reachable sources.
class ReachableSourceCollector {
final Map<String, List<String>> _sourceMap =
new HashMap<String, List<String>>();
final Source source;
final AnalysisContext context;
ReachableSourceCollector(this.source, this.context) {
if (source == null) {
throw new ArgumentError.notNull('source');
}
if (context == null) {
throw new ArgumentError.notNull('context');
}
}
/// Collect reachable sources.
Map<String, List<String>> collectSources() {
_addDependencies(source);
return _sourceMap;
}
void _addDependencies(Source source) {
String sourceUri = source.uri.toString();
// Careful not to revisit.
if (_sourceMap[source.uri.toString()] != null) {
return;
}
List<Source> sources = <Source>[];
sources.addAll(context.computeResult(source, IMPORTED_LIBRARIES));
sources.addAll(context.computeResult(source, EXPORTED_LIBRARIES));
_sourceMap[sourceUri] =
sources.map((source) => source.uri.toString()).toList();
sources.forEach((s) => _addDependencies(s));
}
}

View file

@ -50,6 +50,9 @@ class AstWriter extends UnifyingAstVisitor with TreeWriter {
} else if (node is ExportDirective) {
properties['element'] = node.element;
properties['source'] = node.source;
} else if (node is FunctionDeclaration) {
properties['external keyword'] = node.externalKeyword;
properties['property keyword'] = node.propertyKeyword;
} else if (node is FunctionExpressionInvocation) {
properties['static element'] = node.staticElement;
properties['static type'] = node.staticType;
@ -60,6 +63,11 @@ class AstWriter extends UnifyingAstVisitor with TreeWriter {
properties['source'] = node.source;
} else if (node is LibraryDirective) {
properties['element'] = node.element;
} else if (node is MethodDeclaration) {
properties['external keyword'] = node.externalKeyword;
properties['modifier keyword'] = node.modifierKeyword;
properties['operator keyword'] = node.operatorKeyword;
properties['property keyword'] = node.propertyKeyword;
} else if (node is PartDirective) {
properties['element'] = node.element;
properties['source'] = node.source;

View file

@ -678,6 +678,7 @@ class GetHandler {
List<Folder> allContexts = <Folder>[];
Map<Folder, List<CacheEntry>> entryMap =
new HashMap<Folder, List<CacheEntry>>();
StringBuffer invalidKeysBuffer = new StringBuffer();
analysisServer.folderMap
.forEach((Folder folder, InternalAnalysisContext context) {
Source source = context.sourceFactory.forUri(sourceUri);
@ -694,7 +695,15 @@ class GetHandler {
entries = <CacheEntry>[];
entryMap[folder] = entries;
}
entries.add(iterator.value);
CacheEntry value = iterator.value;
if (value == null) {
if (invalidKeysBuffer.isNotEmpty) {
invalidKeysBuffer.write(', ');
}
invalidKeysBuffer.write(iterator.key.toString());
} else {
entries.add(value);
}
}
}
}
@ -706,6 +715,11 @@ class GetHandler {
_writeResponse(request, (StringBuffer buffer) {
_writePage(buffer, 'Analysis Server - Cache Entry',
['Context: $contextFilter', 'File: $sourceUri'], (HttpResponse) {
if (invalidKeysBuffer.isNotEmpty) {
buffer.write('<h3>Targets with null Entries</h3><p>');
buffer.write(invalidKeysBuffer.toString());
buffer.write('</p>');
}
List<CacheEntry> entries = entryMap[folder];
buffer.write('<h3>Analyzing Contexts</h3><p>');
bool first = true;

View file

@ -55,6 +55,46 @@ main() {
group('updateContent', testUpdateContent);
group('AnalysisDomainHandler', () {
group('getReachableSources', () {
test('valid sources', () async {
String fileA = '/project/a.dart';
String fileB = '/project/b.dart';
resourceProvider.newFile(fileA, 'import "b.dart";');
resourceProvider.newFile(fileB, '');
server.setAnalysisRoots('0', ['/project/'], [], {});
await server.onAnalysisComplete;
var request =
new AnalysisGetReachableSourcesParams(fileA).toRequest('0');
var response = handler.handleRequest(request);
var json = response.toJson()[Response.RESULT];
// Sanity checks.
expect(json['sources'], hasLength(6));
expect(json['sources']['file:///project/a.dart'],
unorderedEquals(['dart:core', 'file:///project/b.dart']));
expect(json['sources']['file:///project/b.dart'], ['dart:core']);
});
test('invalid source', () async {
resourceProvider.newFile('/project/a.dart', 'import "b.dart";');
server.setAnalysisRoots('0', ['/project/'], [], {});
await server.onAnalysisComplete;
var request =
new AnalysisGetReachableSourcesParams('/does/not/exist.dart')
.toRequest('0');
var response = handler.handleRequest(request);
expect(response.error, isNotNull);
expect(response.error.code,
RequestErrorCode.GET_REACHABLE_SOURCES_INVALID_FILE);
});
});
group('setAnalysisRoots', () {
Response testSetAnalysisRoots(
List<String> included, List<String> excluded) {

View file

@ -831,7 +831,7 @@ class Test_CompletionDomainHandler extends CompletionDomainHandler {
}
CompletionManager createCompletionManager(
AnalysisContext context, Source source, SearchEngine searchEngine) {
AnalysisServer server, AnalysisContext context, Source source) {
return new MockCompletionManager(mockContext, source, searchEngine);
}
}

View file

@ -237,6 +237,41 @@ abstract class IntegrationTestMixin {
});
}
/**
* Return the transitive closure of reachable sources for a given file.
*
* If a request is made for a file which does not exist, or which is not
* currently subject to analysis (e.g. because it is not associated with any
* analysis root specified to analysis.setAnalysisRoots), an error of type
* GET_REACHABLE_SOURCES_INVALID_FILE will be generated.
*
* Parameters
*
* file ( FilePath )
*
* The file for which reachable source information is being requested.
*
* Returns
*
* sources ( Map<String, List<String>> )
*
* A mapping from source URIs to directly reachable source URIs. For
* example, a file "foo.dart" that imports "bar.dart" would have the
* corresponding mapping { "file:///foo.dart" : ["file:///bar.dart"] }. If
* "bar.dart" has further imports (or exports) there will be a mapping from
* the URI "file:///bar.dart" to them. To check if a specific URI is
* reachable from a given file, clients can check for its presence in the
* resulting key set.
*/
Future<AnalysisGetReachableSourcesResult> sendAnalysisGetReachableSources(String file) {
var params = new AnalysisGetReachableSourcesParams(file).toJson();
return server.send("analysis.getReachableSources", params)
.then((result) {
ResponseDecoder decoder = new ResponseDecoder(null);
return new AnalysisGetReachableSourcesResult.fromJson(decoder, 'result', result);
});
}
/**
* Return library dependency information for use in client-side indexing and
* package URI resolution.

View file

@ -152,6 +152,30 @@ final Matcher isAnalysisGetHoverResult = new LazyMatcher(() => new MatchesJsonOb
"hovers": isListOf(isHoverInformation)
}));
/**
* analysis.getReachableSources params
*
* {
* "file": FilePath
* }
*/
final Matcher isAnalysisGetReachableSourcesParams = new LazyMatcher(() => new MatchesJsonObject(
"analysis.getReachableSources params", {
"file": isFilePath
}));
/**
* analysis.getReachableSources result
*
* {
* "sources": Map<String, List<String>>
* }
*/
final Matcher isAnalysisGetReachableSourcesResult = new LazyMatcher(() => new MatchesJsonObject(
"analysis.getReachableSources result", {
"sources": isMapOf(isString, isListOf(isString))
}));
/**
* analysis.getLibraryDependencies params
*/
@ -2077,6 +2101,7 @@ final Matcher isRequestError = new LazyMatcher(() => new MatchesJsonObject(
* FORMAT_WITH_ERRORS
* GET_ERRORS_INVALID_FILE
* GET_NAVIGATION_INVALID_FILE
* GET_REACHABLE_SOURCES_INVALID_FILE
* INVALID_ANALYSIS_ROOT
* INVALID_EXECUTION_CONTEXT
* INVALID_OVERLAY_CHANGE
@ -2102,6 +2127,7 @@ final Matcher isRequestErrorCode = new MatchesEnum("RequestErrorCode", [
"FORMAT_WITH_ERRORS",
"GET_ERRORS_INVALID_FILE",
"GET_NAVIGATION_INVALID_FILE",
"GET_REACHABLE_SOURCES_INVALID_FILE",
"INVALID_ANALYSIS_ROOT",
"INVALID_EXECUTION_CONTEXT",
"INVALID_OVERLAY_CHANGE",

View file

@ -91,7 +91,6 @@ class ArgListContributorTest extends AbstractCompletionTest {
}
test_Annotation_local_constructor_named_param() {
//
addTestSource('''
class A { A({int one, String two: 'defaultValue'}) { } }
@A(^) main() { }''');
@ -101,6 +100,16 @@ class A { A({int one, String two: 'defaultValue'}) { } }
});
}
test_Annotation_imported_constructor_named_param() {
addSource('/libA.dart', '''
library libA; class A { A({int one, String two: 'defaultValue'}) { } }''');
addTestSource('import "/libA.dart"; @A(^) main() { }');
computeFast();
return computeFull((bool result) {
assertSuggestArguments(namedArguments: ['one','two']);
});
}
test_ArgumentList_getter() {
addTestSource('class A {int get foo => 7; main() {foo(^)}');
computeFast();

View file

@ -66,7 +66,8 @@ class DartCompletionManagerTest extends AbstractSingleUnitTest {
index = createLocalMemoryIndex();
searchEngine = new SearchEngineImpl(index);
source = addSource('/does/not/exist.dart', '');
manager = new DartCompletionManager.create(context, searchEngine, source);
manager =
new DartCompletionManager.create(context, searchEngine, source, []);
suggestion1 = new CompletionSuggestion(CompletionSuggestionKind.INVOCATION,
DART_RELEVANCE_DEFAULT, "suggestion1", 1, 1, false, false);
suggestion2 = new CompletionSuggestion(CompletionSuggestionKind.IDENTIFIER,
@ -83,7 +84,6 @@ class DartCompletionManagerTest extends AbstractSingleUnitTest {
contributor1 = new MockCompletionContributor(suggestion1, null);
contributor2 = new MockCompletionContributor(null, suggestion2);
manager.contributors = [contributor1, contributor2];
manager.newContributors = [];
int count = 0;
bool done = false;
CompletionRequest completionRequest =
@ -121,7 +121,6 @@ class DartCompletionManagerTest extends AbstractSingleUnitTest {
contributor1 = new MockCompletionContributor(suggestion1, null);
contributor2 = new MockCompletionContributor(suggestion2, null);
manager.contributors = [contributor1, contributor2];
manager.newContributors = [];
int count = 0;
bool done = false;
CompletionRequest completionRequest =

View file

@ -24,25 +24,25 @@ class CompletionManagerTest extends AbstractContextTest {
test_dart() {
Source source = addSource('/does/not/exist.dart', '');
var manager = new CompletionManager.create(context, source, null);
var manager = new CompletionManager.create(context, source, null, []);
expect(manager.runtimeType, DartCompletionManager);
}
test_html() {
Source source = addSource('/does/not/exist.html', '');
var manager = new CompletionManager.create(context, source, null);
var manager = new CompletionManager.create(context, source, null, []);
expect(manager.runtimeType, NoOpCompletionManager);
}
test_null_context() {
Source source = addSource('/does/not/exist.dart', '');
var manager = new CompletionManager.create(null, source, null);
var manager = new CompletionManager.create(null, source, null, []);
expect(manager.runtimeType, NoOpCompletionManager);
}
test_other() {
Source source = addSource('/does/not/exist.foo', '');
var manager = new CompletionManager.create(context, source, null);
var manager = new CompletionManager.create(context, source, null, []);
expect(manager.runtimeType, NoOpCompletionManager);
}
}

View file

@ -64,8 +64,8 @@ abstract class AbstractCompletionTest extends AbstractContextTest {
content.substring(completionOffset + 1);
testSource = addSource(testFile, content);
cache = new DartCompletionCache(context, testSource);
request = new DartCompletionRequest(context, provider, searchEngine,
testSource, completionOffset, cache);
request = new DartCompletionRequest(
context, provider, searchEngine, testSource, completionOffset, cache);
}
void assertHasNoParameterInfo(CompletionSuggestion suggestion) {

View file

@ -107,7 +107,7 @@ class CommonUsageSorterTest extends AbstractAnalysisTest {
source,
new DartCompletionCache(context, source),
null,
null,
server.serverPlugin.completionContributors,
new CommonUsageSorter(selectorRelevance));
Response response =

View file

@ -597,6 +597,55 @@ main(B b) {
''');
}
test_changeTypeAnnotation_BAD_multipleVariables() async {
resolveTestUnit('''
main() {
String a, b = 42;
}
''');
await assertNoFix(DartFixKind.CHANGE_TYPE_ANNOTATION);
}
test_changeTypeAnnotation_BAD_notVariableDeclaration() async {
resolveTestUnit('''
main() {
String v;
v = 42;
}
''');
await assertNoFix(DartFixKind.CHANGE_TYPE_ANNOTATION);
}
test_changeTypeAnnotation_OK_generic() async {
resolveTestUnit('''
main() {
String v = <int>[];
}
''');
await assertHasFix(
DartFixKind.CHANGE_TYPE_ANNOTATION,
'''
main() {
List<int> v = <int>[];
}
''');
}
test_changeTypeAnnotation_OK_simple() async {
resolveTestUnit('''
main() {
String v = 'abc'.length;
}
''');
await assertHasFix(
DartFixKind.CHANGE_TYPE_ANNOTATION,
'''
main() {
int v = 'abc'.length;
}
''');
}
test_createClass() async {
resolveTestUnit('''
main() {

View file

@ -34,7 +34,7 @@ class LibraryDependenciesTest extends AbstractContextTest {
// Cycles
expect(libs, contains('/lib1.dart'));
expect(libs, contains('/lib2.dart'));
// Regular sourcs
// Regular sources
expect(libs, contains('/lib3.dart'));
expect(libs, contains('/lib4.dart'));
// Non-source, referenced by source

View file

@ -0,0 +1,84 @@
// 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 test.services.dependencies.import_collector;
import 'package:analysis_server/src/services/dependencies/reachable_source_collector.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'package:unittest/unittest.dart';
import '../../abstract_context.dart';
import '../../utils.dart';
main() {
initializeTestEnvironment();
defineReflectiveTests(ReachableSourceCollectorTest);
}
@reflectiveTest
class ReachableSourceCollectorTest extends AbstractContextTest {
CompilationUnit addLibrary(String path, String contents) =>
resolveLibraryUnit(addSource(path, contents));
Map<String, List<String>> importsFor(Source source) =>
new ReachableSourceCollector(source, context).collectSources();
test_null_context() {
Source lib = addSource('/lib.dart', '');
expect(() => new ReachableSourceCollector(lib, null),
throwsA(new isInstanceOf<ArgumentError>()));
}
test_null_source() {
expect(() => new ReachableSourceCollector(null, context),
throwsA(new isInstanceOf<ArgumentError>()));
}
test_sources() {
Source lib1 = addSource(
'/lib1.dart',
'''
import "lib2.dart";
import "dart:html";''');
Source lib2 = addSource('/lib2.dart', 'import "lib1.dart";');
Source lib3 = addSource('/lib3.dart', 'import "lib4.dart";');
addSource('/lib4.dart', 'import "lib3.dart";');
Map<String, List<String>> imports = importsFor(lib1);
// Verify keys.
expect(
imports.keys,
unorderedEquals([
'dart:_internal',
'dart:async',
'dart:core',
'dart:html',
'dart:math',
'file:///lib1.dart',
'file:///lib2.dart',
]));
// Values.
expect(imports['file:///lib1.dart'],
unorderedEquals(['dart:core', 'dart:html', 'file:///lib2.dart']));
// Check transitivity.
expect(importsFor(lib2).keys, contains('dart:html'));
// Cycles should be OK.
expect(
importsFor(lib3).keys,
unorderedEquals([
'dart:_internal',
'dart:async',
'dart:core',
'dart:math',
'file:///lib3.dart',
'file:///lib4.dart'
]));
}
}

View file

@ -7,12 +7,14 @@ library test.services.dependencies;
import 'package:unittest/unittest.dart';
import '../../utils.dart';
import 'library_dependencies_test.dart' as library_dependencies_test;
import 'library_dependencies_test.dart' as library_dependencies;
import 'reachable_source_collector_test.dart' as reachable_source_collector;
/// Utility for manually running all tests.
main() {
initializeTestEnvironment();
group('dependencies', () {
library_dependencies_test.main();
library_dependencies.main();
reachable_source_collector.main();
});
}

View file

@ -118,6 +118,20 @@ public interface AnalysisServer {
*/
public void analysis_getNavigation(String file, int offset, int length, GetNavigationConsumer consumer);
/**
* {@code analysis.getReachableSources}
*
* Return the transitive closure of reachable sources for a given file.
*
* If a request is made for a file which does not exist, or which is not currently subject to
* analysis (e.g. because it is not associated with any analysis root specified to
* analysis.setAnalysisRoots), an error of type GET_REACHABLE_SOURCES_INVALID_FILE will be
* generated.
*
* @param file The file for which reachable source information is being requested.
*/
public void analysis_getReachableSources(String file, GetReachableSourcesConsumer consumer);
/**
* {@code analysis.reanalyze}
*

View file

@ -58,6 +58,12 @@ public class RequestErrorCode {
*/
public static final String GET_NAVIGATION_INVALID_FILE = "GET_NAVIGATION_INVALID_FILE";
/**
* An "analysis.getReachableSources" request specified a FilePath which does not match a file
* currently subject to analysis.
*/
public static final String GET_REACHABLE_SOURCES_INVALID_FILE = "GET_REACHABLE_SOURCES_INVALID_FILE";
/**
* A path passed as an argument to a request (such as analysis.reanalyze) is required to be an
* analysis root, but isn't.

View file

@ -6,7 +6,7 @@
</head>
<body>
<h1>Analysis Server API Specification</h1>
<h1 style="color:#999999">Version <version>1.12.0</version></h1>
<h1 style="color:#999999">Version <version>1.13.0</version></h1>
<p>
This document contains a specification of the API provided by the
analysis server. The API in this document is currently under
@ -395,6 +395,42 @@
</field>
</result>
</request>
<request method="getReachableSources">
<p>
Return the transitive closure of reachable sources for a given file.
</p>
<p>
If a request is made for a file which does not exist, or
which is not currently subject to analysis (e.g. because it
is not associated with any analysis root specified to
analysis.setAnalysisRoots), an error of type
<tt>GET_REACHABLE_SOURCES_INVALID_FILE</tt> will be generated.
</p>
<params>
<field name="file">
<ref>FilePath</ref>
<p>
The file for which reachable source information is being requested.
</p>
</field>
</params>
<result>
<field name="sources">
<map>
<key><ref>String</ref></key>
<value><list>><ref>String</ref></list></value>
</map>
<p>
A mapping from source URIs to directly reachable source URIs. For example,
a file "foo.dart" that imports "bar.dart" would have the corresponding mapping
{ "file:///foo.dart" : ["file:///bar.dart"] }. If "bar.dart" has further imports
(or exports) there will be a mapping from the URI "file:///bar.dart" to them.
To check if a specific URI is reachable from a given file, clients can check
for its presence in the resulting key set.
</p>
</field>
</result>
</request>
<request method="getLibraryDependencies">
<p>
Return library dependency information for use in client-side indexing
@ -3649,6 +3685,14 @@
analysis.
</p>
</value>
<value>
<code>GET_REACHABLE_SOURCES_INVALID_FILE</code>
<p>
An "analysis.getReachableSources" request specified a FilePath
which does not match a file currently subject to
analysis.
</p>
</value>
<value>
<code>INVALID_ANALYSIS_ROOT</code>
<p>

View file

@ -257,6 +257,9 @@ class AnalysisContextImpl implements InternalAnalysisContext {
this._options.preserveComments != options.preserveComments ||
this._options.strongMode != options.strongMode ||
this._options.enableAssertMessage != options.enableAssertMessage ||
((options is AnalysisOptionsImpl)
? this._options.strongModeHints != options.strongModeHints
: false) ||
this._options.enableStrictCallChecks !=
options.enableStrictCallChecks ||
this._options.enableGenericMethods != options.enableGenericMethods ||
@ -281,6 +284,9 @@ class AnalysisContextImpl implements InternalAnalysisContext {
this._options.lint = options.lint;
this._options.preserveComments = options.preserveComments;
this._options.strongMode = options.strongMode;
if (options is AnalysisOptionsImpl) {
this._options.strongModeHints = options.strongModeHints;
}
if (needsRecompute) {
for (WorkManager workManager in workManagers) {
workManager.onAnalysisOptionsChanged();

View file

@ -1349,6 +1349,14 @@ class AnalysisOptionsImpl implements AnalysisOptions {
* A flag indicating whether strong-mode analysis should be used.
*/
bool strongMode = false;
/**
* A flag indicating whether strong-mode inference hints should be
* used. This flag is not exposed in the interface, and should be
* replaced by something more general.
*/
// TODO(leafp): replace this with something more general
bool strongModeHints = false;
/**
* Initialize a newly created set of analysis options to have their default
@ -1378,6 +1386,9 @@ class AnalysisOptionsImpl implements AnalysisOptions {
lint = options.lint;
preserveComments = options.preserveComments;
strongMode = options.strongMode;
if (options is AnalysisOptionsImpl) {
strongModeHints = options.strongModeHints;
}
}
/**
@ -1401,6 +1412,9 @@ class AnalysisOptionsImpl implements AnalysisOptions {
lint = options.lint;
preserveComments = options.preserveComments;
strongMode = options.strongMode;
if (options is AnalysisOptionsImpl) {
strongModeHints = options.strongModeHints;
}
}
bool get analyzeFunctionBodies {

View file

@ -2150,13 +2150,18 @@ class DeclarationResolver extends RecursiveAstVisitor<Object> {
_findIdentifier(_enclosingUnit.functions, functionName);
}
} else {
PropertyAccessorElement accessor =
_findIdentifier(_enclosingUnit.accessors, functionName);
if ((property as sc.KeywordToken).keyword == sc.Keyword.SET) {
accessor = accessor.variable.setter;
functionName.staticElement = accessor;
if (_enclosingExecutable != null) {
_enclosingExecutable =
_findIdentifier(_enclosingExecutable.functions, functionName);
} else {
PropertyAccessorElement accessor =
_findIdentifier(_enclosingUnit.accessors, functionName);
if ((property as sc.KeywordToken).keyword == sc.Keyword.SET) {
accessor = accessor.variable.setter;
functionName.staticElement = accessor;
}
_enclosingExecutable = accessor;
}
_enclosingExecutable = accessor;
}
node.functionExpression.element = _enclosingExecutable;
return super.visitFunctionDeclaration(node);
@ -5335,6 +5340,11 @@ class InferenceContext {
*/
final AnalysisErrorListener _errorListener;
/**
* If true, emit hints when types are inferred
*/
final bool _inferenceHints;
/**
* Type provider, needed for type matching.
*/
@ -5356,8 +5366,8 @@ class InferenceContext {
*/
List<DartType> _returnStack = <DartType>[];
InferenceContext._(
this._errorListener, TypeProvider typeProvider, this._typeSystem)
InferenceContext._(this._errorListener, TypeProvider typeProvider,
this._typeSystem, this._inferenceHints)
: _typeProvider = typeProvider,
_rules = new TypeRules(typeProvider);
@ -5401,7 +5411,7 @@ class InferenceContext {
*/
void recordInference(Expression node, DartType type) {
StaticInfo info = InferredType.create(_rules, node, type);
if (info == null) {
if (!_inferenceHints || info == null) {
return;
}
AnalysisError error = info.toAnalysisError();
@ -8285,8 +8295,13 @@ class ResolverVisitor extends ScopedVisitor {
}
this.elementResolver = new ElementResolver(this);
this.typeSystem = definingLibrary.context.typeSystem;
this.inferenceContext =
new InferenceContext._(errorListener, typeProvider, typeSystem);
bool strongModeHints = false;
AnalysisOptions options = definingLibrary.context.analysisOptions;
if (options is AnalysisOptionsImpl) {
strongModeHints = options.strongModeHints;
}
this.inferenceContext = new InferenceContext._(
errorListener, typeProvider, typeSystem, strongModeHints);
if (typeAnalyzerFactory == null) {
this.typeAnalyzer = new StaticTypeAnalyzer(this);
} else {

View file

@ -1828,7 +1828,10 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
if (element is ExecutableElement &&
fnType is FunctionTypeImpl &&
ts is StrongTypeSystemImpl) {
List<Expression> arguments = node.argumentList.arguments;
// We may have too many (or too few) arguments. Only use arguments
// which have been matched up with a static parameter.
Iterable<Expression> arguments = node.argumentList.arguments
.where((e) => e.staticParameterElement != null);
List<DartType> argTypes = arguments.map((e) => e.staticType).toList();
List<DartType> paramTypes =
arguments.map((e) => e.staticParameterElement.type).toList();
@ -1847,11 +1850,10 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
correspondingParams.add(inferredParameters[i]);
}
node.argumentList.correspondingStaticParameters = correspondingParams;
_recordStaticType(node.methodName, inferred);
_recordStaticType(node, inferred.returnType);
return true;
}
return true;
}
return false;
}

View file

@ -50,11 +50,11 @@ class StrongTypeSystemImpl implements TypeSystem {
FunctionTypeImpl fnType,
List<DartType> correspondingParameterTypes,
List<DartType> argumentTypes) {
ExecutableElement element = fnType.element;
TypeParameterizedElement element = fnType.element;
if (element.typeParameters.isEmpty) {
return fnType;
}
assert(correspondingParameterTypes.length == argumentTypes.length);
int numParams = element.typeParameters.length;
List<DartType> fnTypeParams = fnType.typeArguments;
fnTypeParams = fnTypeParams.sublist(0, numParams);

View file

@ -3487,13 +3487,13 @@ class ParseDartTask extends SourceBasedAnalysisTask {
/**
* 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
* [source].
* [target].
*/
static Map<String, TaskInput> buildInputs(AnalysisTarget target) {
return <String, TaskInput>{
LINE_INFO_INPUT_NAME: LINE_INFO.of(target),
MODIFICATION_TIME_INPUT_NAME: MODIFICATION_TIME.of(target),
TOKEN_STREAM_INPUT_NAME: TOKEN_STREAM.of(target)
TOKEN_STREAM_INPUT_NAME: TOKEN_STREAM.of(target, flushOnAccess: true)
};
}

View file

@ -583,8 +583,13 @@ class CodeChecker extends RecursiveAstVisitor {
@override
void visitRedirectingConstructorInvocation(
RedirectingConstructorInvocation node) {
var type = node.staticElement.type;
checkArgumentList(node.argumentList, type);
var type = node.staticElement?.type;
// TODO(leafp): There's a TODO in visitRedirectingConstructorInvocation
// in the element_resolver to handle the case that the element is null
// and emit an error. In the meantime, just be defensive here.
if (type != null) {
checkArgumentList(node.argumentList, type);
}
node.visitChildren(this);
}

View file

@ -321,6 +321,10 @@ class TypeRules {
return false;
}
if (t1.isVoid || t2.isVoid) {
return false;
}
if (t2.isDartCoreFunction) {
if (t1 is FunctionType) return true;
if (t1.element is ClassElement) {

View file

@ -0,0 +1,98 @@
// 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 engine.declaration_resolver_test;
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
import 'package:analyzer/src/generated/resolver.dart';
import 'package:unittest/unittest.dart';
import '../reflective_tests.dart';
import '../utils.dart';
import 'resolver_test.dart';
import 'test_support.dart';
main() {
initializeTestEnvironment();
runReflectiveTests(DeclarationResolverTest);
}
@reflectiveTest
class DeclarationResolverTest extends ResolverTestCase {
@override
void setUp() {
super.setUp();
}
void test_functionDeclaration_getter() {
String code = r'''
int get zzz => 42;
''';
CompilationUnit unit = resolveSource(code);
PropertyAccessorElement getterElement =
findSimpleIdentifier(unit, code, 'zzz =>').staticElement;
expect(getterElement.isGetter, isTrue);
// re-resolve
CompilationUnit unit2 = _cloneResolveUnit(unit);
SimpleIdentifier getterName = findSimpleIdentifier(unit2, code, 'zzz =>');
expect(getterName.staticElement, same(getterElement));
}
void test_functionDeclaration_setter() {
String code = r'''
void set zzz(_) {}
''';
CompilationUnit unit = resolveSource(code);
PropertyAccessorElement setterElement =
findSimpleIdentifier(unit, code, 'zzz(_)').staticElement;
expect(setterElement.isSetter, isTrue);
// re-resolve
CompilationUnit unit2 = _cloneResolveUnit(unit);
SimpleIdentifier getterName = findSimpleIdentifier(unit2, code, 'zzz(_)');
expect(getterName.staticElement, same(setterElement));
}
void test_invalid_functionDeclaration_getter_inFunction() {
String code = r'''
main() {
int get zzz => 42;
}
''';
CompilationUnit unit = resolveSource(code);
FunctionElement getterElement =
findSimpleIdentifier(unit, code, 'zzz =>').staticElement;
// re-resolve
CompilationUnit unit2 = _cloneResolveUnit(unit);
SimpleIdentifier getterName = findSimpleIdentifier(unit2, code, 'zzz =>');
expect(getterName.staticElement, same(getterElement));
}
void test_invalid_functionDeclaration_setter_inFunction() {
String code = r'''
main() {
set zzz(x) {}
}
''';
CompilationUnit unit = resolveSource(code);
FunctionElement setterElement =
findSimpleIdentifier(unit, code, 'zzz(x)').staticElement;
// re-resolve
CompilationUnit unit2 = _cloneResolveUnit(unit);
SimpleIdentifier setterName = findSimpleIdentifier(unit2, code, 'zzz(x)');
expect(setterName.staticElement, same(setterElement));
}
static SimpleIdentifier findSimpleIdentifier(
AstNode root, String code, String search) {
return EngineTestCase.findNode(
root, code, search, (n) => n is SimpleIdentifier);
}
static CompilationUnit _cloneResolveUnit(CompilationUnit unit) {
CompilationUnit clonedUnit = AstCloner.clone(unit);
new DeclarationResolver().resolve(clonedUnit, unit.element);
return clonedUnit;
}
}

View file

@ -12184,6 +12184,18 @@ class StrongModeDownwardsInferenceTest extends ResolverTestCase {
expect(functionReturnValue(4).staticType, typeProvider.stringType);
}
void test_inference_hints() {
Source source = addSource(r'''
void main () {
var x = 3;
List<int> l0 = [];
}
''');
LibraryElement library = resolve2(source);
assertNoErrors(source);
verify([source]);
}
void test_instanceCreation() {
String code = r'''
class A<S, T> {

View file

@ -10,6 +10,7 @@ import '../utils.dart';
import 'all_the_rest_test.dart' as all_the_rest;
import 'ast_test.dart' as ast_test;
import 'compile_time_error_code_test.dart' as compile_time_error_code_test;
import 'declaration_resolver_test.dart' as declaration_resolver_test;
import 'element_test.dart' as element_test;
import 'engine_test.dart' as engine_test;
import 'incremental_resolver_test.dart' as incremental_resolver_test;
@ -33,6 +34,7 @@ main() {
all_the_rest.main();
ast_test.main();
compile_time_error_code_test.main();
declaration_resolver_test.main();
element_test.main();
engine_test.main();
incremental_resolver_test.main();

View file

@ -675,6 +675,13 @@ class StrongSubtypingTest {
subtypes: subtypes);
}
// Regression test for https://github.com/dart-lang/sdk/issues/25069
void test_isSubtypeOf_simple_function_void() {
FunctionType functionType =
TypeBuilder.functionType(<DartType>[intType], objectType);
_checkIsNotSubtypeOf(voidType, functionType);
}
void test_isSubtypeOf_simple_function() {
FunctionType top =
TypeBuilder.functionType(<DartType>[intType], objectType);

View file

@ -2913,6 +2913,13 @@ part 'test.dart';
expect(outputs[UNITS], hasLength(1));
}
test_perform_flushTokenStream() {
_performParseTask(r'''
class Test {}
''');
expect(analysisCache.getState(source, TOKEN_STREAM), CacheState.FLUSHED);
}
test_perform_invalidDirectives() {
_performParseTask(r'''
library lib;

View file

@ -276,6 +276,17 @@ void main() {
'''
});
// Regression test for https://github.com/dart-lang/sdk/issues/25069
testChecker('Void subtyping', {
'/main.dart': '''
typedef int Foo();
void foo() {}
void main () {
Foo x = /*severe:StaticTypeError*/foo();
}
'''
});
testChecker('Ground type subtyping: dynamic is top', {
'/main.dart': '''
@ -1105,6 +1116,15 @@ void main() {
'''
});
// This is a regression test for https://github.com/dart-lang/sdk/issues/25071
testChecker('unbound redirecting constructor', {
'/main.dart': '''
class Foo {
Foo() : this.init();
}
'''
});
testChecker('redirecting constructor', {
'/main.dart': '''
class A {

View file

@ -227,6 +227,7 @@ void testChecker(String name, Map<String, String> testFiles) {
// Enable task model strong mode
var context = AnalysisEngine.instance.createAnalysisContext();
context.analysisOptions.strongMode = true;
context.analysisOptions.strongModeHints = true;
context.sourceFactory = new SourceFactory([
new MockDartSdk(mockSdkSources, reportMissing: true).resolver,

View file

@ -257,16 +257,23 @@ class TypeMaskSystem implements AbstractValueDomain {
void associateConstantValueWithElement(ConstantValue constant,
Element element) {
if (constant is ListConstantValue) {
_constantMasks[constant] = inferrer.getGuaranteedTypeOfElement(element);
// TODO(25093): Replace this code with an approach that works for anonymous
// constants and non-constant literals.
if (constant is ListConstantValue || constant is MapConstantValue) {
// Inferred type is usually better (e.g. a ContainerTypeMask) but is
// occasionally less general.
TypeMask computed = computeTypeMask(inferrer.compiler, constant);
TypeMask inferred = inferrer.getGuaranteedTypeOfElement(element);
TypeMask best = intersection(inferred, computed);
assert(!best.isEmpty);
_constantMasks[constant] = best;
}
}
@override
TypeMask getTypeOf(ConstantValue constant) {
TypeMask mask = _constantMasks[constant];
if (mask != null) return mask;
return computeTypeMask(inferrer.compiler, constant);
return _constantMasks[constant] ??
computeTypeMask(inferrer.compiler, constant);
}
@override

View file

@ -387,7 +387,7 @@ class ConstantPropagationLattice {
AbstractConstantValue closedOnInt(AbstractConstantValue left,
AbstractConstantValue right) {
if (isDefinitelyInt(left) && isDefinitelyInt(right)) {
if (isDefinitelyInt(left) && isDefinitelyInt(right, allowNull: true)) {
return nonConstant(typeSystem.intType);
}
return null;
@ -395,7 +395,7 @@ class ConstantPropagationLattice {
AbstractConstantValue closedOnUint(AbstractConstantValue left,
AbstractConstantValue right) {
if (isDefinitelyUint(left) && isDefinitelyUint(right)) {
if (isDefinitelyUint(left) && isDefinitelyUint(right, allowNull: true)) {
return nonConstant(typeSystem.uintType);
}
return null;
@ -403,7 +403,7 @@ class ConstantPropagationLattice {
AbstractConstantValue closedOnUint31(AbstractConstantValue left,
AbstractConstantValue right) {
if (isDefinitelyUint31(left) && isDefinitelyUint31(right)) {
if (isDefinitelyUint31(left) && isDefinitelyUint31(right, allowNull: true)) {
return nonConstant(typeSystem.uint31Type);
}
return null;
@ -414,7 +414,8 @@ class ConstantPropagationLattice {
AbstractConstantValue folded = foldBinary(constantSystem.add, left, right);
if (folded != null) return folded;
if (isDefinitelyNum(left)) {
if (isDefinitelyUint31(left) && isDefinitelyUint31(right)) {
if (isDefinitelyUint31(left) &&
isDefinitelyUint31(right, allowNull: true)) {
return nonConstant(typeSystem.uint32Type);
}
return closedOnUint(left, right) ?? closedOnInt(left, right);
@ -450,7 +451,7 @@ class ConstantPropagationLattice {
if (isDefinitelyUint32(left) && isDefinitelyIntInRange(right, min: 2)) {
return nonConstant(typeSystem.uint31Type);
}
if (isDefinitelyUint(right)) {
if (isDefinitelyUint(right, allowNull: true)) {
// `0` will be an exception, other values will shrink the result.
if (isDefinitelyUint31(left)) return nonConstant(typeSystem.uint31Type);
if (isDefinitelyUint32(left)) return nonConstant(typeSystem.uint32Type);
@ -501,7 +502,8 @@ class ConstantPropagationLattice {
foldBinary(constantSystem.bitAnd, left, right);
if (folded != null) return folded;
if (isDefinitelyNum(left)) {
if (isDefinitelyUint31(left) || isDefinitelyUint31(right)) {
if (isDefinitelyUint31(left) ||
isDefinitelyUint31(right, allowNull: true)) {
// Either 31-bit argument will truncate the other.
return nonConstant(typeSystem.uint31Type);
}

View file

@ -140,9 +140,15 @@ class NativeBehavior {
///
/// Two forms of the string is supported:
///
/// 1) A single type string of the form 'void', '', 'var' or 'T1|...|Tn'
/// which defines the types returned and for the later form also created by
/// the call to JS.
/// 1) A single type string of the form 'void', '', 'var' or 'T1|...|Tn' which
/// defines the types returned, and, for the last form, the types also
/// created by the call to JS. 'var' (and '') are like 'dynamic' or
/// 'Object' except that 'dynamic' would indicate that objects of any type
/// are created, which defeats tree-shaking. Think of 'var' (and '') as
/// meaning 'any pre-existing type'.
///
/// The types Ti are non-nullable, so add class `Null` to specify a
/// nullable type, e.g `'String|Null'`.
///
/// 2) A sequence of <tag>:<value> pairs of the following kinds
///
@ -155,7 +161,7 @@ class NativeBehavior {
/// A <type-tag> is either 'returns' or 'creates' and <type-string> is a
/// type string like in 1). The type string marked by 'returns' defines the
/// types returned and 'creates' defines the types created by the call to
/// JS.
/// JS. If 'creates' is missing, it defaults to 'returns'.
///
/// An <effect-tag> is either 'effects' or 'depends' and <effect-string> is
/// either 'all', 'none' or a comma-separated list of 'no-index',
@ -309,23 +315,30 @@ class NativeBehavior {
String returns = values['returns'];
if (returns != null) {
resolveTypesString(returns, onVar: () {
typesReturned.add(objectType);
typesReturned.add(nullType);
}, onType: (type) {
typesReturned.add(type);
});
resolveTypesString(returns,
onVar: () {
typesReturned.add(objectType);
typesReturned.add(nullType);
},
onType: (type) {
typesReturned.add(type);
});
}
String creates = values['creates'];
if (creates != null) {
resolveTypesString(creates, onVoid: () {
reportError("Invalid type string 'creates:$creates'");
}, onVar: () {
reportError("Invalid type string 'creates:$creates'");
}, onType: (type) {
typesInstantiated.add(type);
});
resolveTypesString(creates,
onVoid: () {
reportError("Invalid type string 'creates:$creates'");
},
onType: (type) {
typesInstantiated.add(type);
});
} else if (returns != null) {
resolveTypesString(returns,
onType: (type) {
typesInstantiated.add(type);
});
}
const throwsOption = const <String, NativeThrowBehavior>{

View file

@ -1625,14 +1625,17 @@ ASSEMBLER_TEST_RUN(LoadImmediateMedNeg4, test) {
static void EnterTestFrame(Assembler* assembler) {
__ EnterFrame(0);
__ Push(CODE_REG);
__ Push(THR);
__ TagAndPushPP();
__ ldr(CODE_REG, Address(R0, VMHandles::kOffsetOfRawPtrInHandle));
__ mov(THR, R1);
__ LoadPoolPointer(PP);
}
static void LeaveTestFrame(Assembler* assembler) {
__ PopAndUntagPP();
__ Pop(THR);
__ Pop(CODE_REG);
__ LeaveFrame();
}
@ -1652,7 +1655,7 @@ ASSEMBLER_TEST_GENERATE(LoadImmediatePPSmall, assembler) {
ASSEMBLER_TEST_RUN(LoadImmediatePPSmall, test) {
EXPECT_EQ(42, test->InvokeWithCode<int64_t>());
EXPECT_EQ(42, test->InvokeWithCodeAndThread<int64_t>());
}
@ -1667,7 +1670,7 @@ ASSEMBLER_TEST_GENERATE(LoadImmediatePPMed, assembler) {
ASSEMBLER_TEST_RUN(LoadImmediatePPMed, test) {
EXPECT_EQ(0xf1234123, test->InvokeWithCode<int64_t>());
EXPECT_EQ(0xf1234123, test->InvokeWithCodeAndThread<int64_t>());
}
@ -1682,7 +1685,7 @@ ASSEMBLER_TEST_GENERATE(LoadImmediatePPMed2, assembler) {
ASSEMBLER_TEST_RUN(LoadImmediatePPMed2, test) {
EXPECT_EQ(0x4321f1234124, test->InvokeWithCode<int64_t>());
EXPECT_EQ(0x4321f1234124, test->InvokeWithCodeAndThread<int64_t>());
}
@ -1698,23 +1701,15 @@ ASSEMBLER_TEST_GENERATE(LoadImmediatePPLarge, assembler) {
ASSEMBLER_TEST_RUN(LoadImmediatePPLarge, test) {
EXPECT_EQ(static_cast<int64_t>(0x9287436598237465),
test->InvokeWithCode<int64_t>());
test->InvokeWithCodeAndThread<int64_t>());
}
#define ASSEMBLER_TEST_RUN_WITH_THREAD(result_type, var_name) \
Thread* thread = Thread::Current(); \
result_type var_name = test->InvokeWithCode<result_type>(thread);
// LoadObject null.
ASSEMBLER_TEST_GENERATE(LoadObjectNull, assembler) {
__ SetupDartSP(kTestStackSpace);
EnterTestFrame(assembler);
__ Push(THR);
__ mov(THR, R1);
__ LoadObject(R0, Object::null_object());
__ Pop(THR);
LeaveTestFrame(assembler);
__ mov(CSP, SP);
__ ret();
@ -1722,18 +1717,14 @@ ASSEMBLER_TEST_GENERATE(LoadObjectNull, assembler) {
ASSEMBLER_TEST_RUN(LoadObjectNull, test) {
ASSEMBLER_TEST_RUN_WITH_THREAD(RawObject*, result);
EXPECT_EQ(Object::null(), result);
EXPECT_EQ(Object::null(), test->InvokeWithCodeAndThread<RawObject*>());
}
ASSEMBLER_TEST_GENERATE(LoadObjectTrue, assembler) {
__ SetupDartSP(kTestStackSpace);
EnterTestFrame(assembler);
__ Push(THR);
__ mov(THR, R1);
__ LoadObject(R0, Bool::True());
__ Pop(THR);
LeaveTestFrame(assembler);
__ mov(CSP, SP);
__ ret();
@ -1741,18 +1732,14 @@ ASSEMBLER_TEST_GENERATE(LoadObjectTrue, assembler) {
ASSEMBLER_TEST_RUN(LoadObjectTrue, test) {
ASSEMBLER_TEST_RUN_WITH_THREAD(RawObject*, result);
EXPECT_EQ(Bool::True().raw(), result);
EXPECT_EQ(Bool::True().raw(), test->InvokeWithCodeAndThread<RawObject*>());
}
ASSEMBLER_TEST_GENERATE(LoadObjectFalse, assembler) {
__ SetupDartSP(kTestStackSpace);
EnterTestFrame(assembler);
__ Push(THR);
__ mov(THR, R1);
__ LoadObject(R0, Bool::False());
__ Pop(THR);
LeaveTestFrame(assembler);
__ mov(CSP, SP);
__ ret();
@ -1760,8 +1747,7 @@ ASSEMBLER_TEST_GENERATE(LoadObjectFalse, assembler) {
ASSEMBLER_TEST_RUN(LoadObjectFalse, test) {
ASSEMBLER_TEST_RUN_WITH_THREAD(RawObject*, result);
EXPECT_EQ(Bool::False().raw(), result);
EXPECT_EQ(Bool::False().raw(), test->InvokeWithCodeAndThread<RawObject*>());
}
@ -3591,7 +3577,7 @@ ASSEMBLER_TEST_GENERATE(ComputeRange, assembler) {
__ SetupDartSP(kTestStackSpace);
EnterTestFrame(assembler);
Label miss, done;
__ ComputeRange(R0, R1, R2, &miss);
__ ComputeRange(R0, R2, R3, &miss);
__ b(&done);
__ Bind(&miss);
@ -3605,7 +3591,8 @@ ASSEMBLER_TEST_GENERATE(ComputeRange, assembler) {
ASSEMBLER_TEST_RUN(ComputeRange, test) {
#define RANGE_OF(arg_type, v) test->InvokeWithCode<intptr_t, arg_type>(v)
#define RANGE_OF(arg_type, v) \
test->InvokeWithCodeAndThread<intptr_t, arg_type>(v)
EXPECT_EQ(ICData::kInt32RangeBit, RANGE_OF(RawSmi*, Smi::New(0)));
EXPECT_EQ(ICData::kInt32RangeBit, RANGE_OF(RawSmi*, Smi::New(1)));

View file

@ -785,57 +785,29 @@ void Assembler::orps(XmmRegister dst, XmmRegister src) {
}
void Assembler::notps(XmmRegister dst) {
static const struct ALIGN16 {
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
} float_not_constant =
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
LoadImmediate(
TMP, Immediate(reinterpret_cast<intptr_t>(&float_not_constant)));
// { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
movq(TMP, Address(THR, Thread::float_not_address_offset()));
xorps(dst, Address(TMP, 0));
}
void Assembler::negateps(XmmRegister dst) {
static const struct ALIGN16 {
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
} float_negate_constant =
{ 0x80000000, 0x80000000, 0x80000000, 0x80000000 };
LoadImmediate(
TMP, Immediate(reinterpret_cast<intptr_t>(&float_negate_constant)));
// { 0x80000000, 0x80000000, 0x80000000, 0x80000000 }
movq(TMP, Address(THR, Thread::float_negate_address_offset()));
xorps(dst, Address(TMP, 0));
}
void Assembler::absps(XmmRegister dst) {
static const struct ALIGN16 {
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
} float_absolute_constant =
{ 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF };
LoadImmediate(
TMP, Immediate(reinterpret_cast<intptr_t>(&float_absolute_constant)));
// { 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF }
movq(TMP, Address(THR, Thread::float_absolute_address_offset()));
andps(dst, Address(TMP, 0));
}
void Assembler::zerowps(XmmRegister dst) {
static const struct ALIGN16 {
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
} float_zerow_constant =
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000 };
LoadImmediate(
TMP, Immediate(reinterpret_cast<intptr_t>(&float_zerow_constant)));
// { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000 }
movq(TMP, Address(THR, Thread::float_zerow_address_offset()));
andps(dst, Address(TMP, 0));
}
@ -1017,13 +989,8 @@ void Assembler::addpd(XmmRegister dst, XmmRegister src) {
void Assembler::negatepd(XmmRegister dst) {
static const struct ALIGN16 {
uint64_t a;
uint64_t b;
} double_negate_constant =
{ 0x8000000000000000LL, 0x8000000000000000LL };
LoadImmediate(
TMP, Immediate(reinterpret_cast<intptr_t>(&double_negate_constant)));
// { 0x8000000000000000LL, 0x8000000000000000LL }
movq(TMP, Address(THR, Thread::double_negate_address_offset()));
xorpd(dst, Address(TMP, 0));
}
@ -1065,13 +1032,8 @@ void Assembler::divpd(XmmRegister dst, XmmRegister src) {
void Assembler::abspd(XmmRegister dst) {
static const struct ALIGN16 {
uint64_t a;
uint64_t b;
} double_absolute_const =
{ 0x7FFFFFFFFFFFFFFFLL, 0x7FFFFFFFFFFFFFFFLL };
LoadImmediate(
TMP, Immediate(reinterpret_cast<intptr_t>(&double_absolute_const)));
// { 0x7FFFFFFFFFFFFFFFLL, 0x7FFFFFFFFFFFFFFFLL }
movq(TMP, Address(THR, Thread::double_abs_address_offset()));
andpd(dst, Address(TMP, 0));
}
@ -3160,25 +3122,15 @@ void Assembler::IncrementSmiField(const Address& dest, int64_t increment) {
void Assembler::DoubleNegate(XmmRegister d) {
static const struct ALIGN16 {
uint64_t a;
uint64_t b;
} double_negate_constant =
{0x8000000000000000LL, 0x8000000000000000LL};
LoadImmediate(
TMP, Immediate(reinterpret_cast<intptr_t>(&double_negate_constant)));
// {0x8000000000000000LL, 0x8000000000000000LL}
movq(TMP, Address(THR, Thread::double_negate_address_offset()));
xorpd(d, Address(TMP, 0));
}
void Assembler::DoubleAbs(XmmRegister reg) {
static const struct ALIGN16 {
uint64_t a;
uint64_t b;
} double_abs_constant =
{0x7FFFFFFFFFFFFFFFLL, 0x7FFFFFFFFFFFFFFFLL};
LoadImmediate(TMP,
Immediate(reinterpret_cast<intptr_t>(&double_abs_constant)));
// {0x7FFFFFFFFFFFFFFFLL, 0x7FFFFFFFFFFFFFFFLL}
movq(TMP, Address(THR, Thread::double_abs_address_offset()));
andpd(reg, Address(TMP, 0));
}

View file

@ -2021,16 +2021,21 @@ ASSEMBLER_TEST_RUN(PackedDoubleSub, test) {
static void EnterTestFrame(Assembler* assembler) {
COMPILE_ASSERT(THR != CallingConventions::kArg1Reg);
COMPILE_ASSERT(CODE_REG != CallingConventions::kArg2Reg);
__ EnterFrame(0);
__ pushq(CODE_REG);
__ pushq(PP);
__ pushq(THR);
__ movq(CODE_REG, Address(CallingConventions::kArg1Reg,
VMHandles::kOffsetOfRawPtrInHandle));
__ movq(THR, CallingConventions::kArg2Reg);
__ LoadPoolPointer(PP);
}
static void LeaveTestFrame(Assembler* assembler) {
__ popq(THR);
__ popq(PP);
__ popq(CODE_REG);
__ LeaveFrame();
@ -2053,7 +2058,7 @@ ASSEMBLER_TEST_GENERATE(PackedDoubleNegate, assembler) {
ASSEMBLER_TEST_RUN(PackedDoubleNegate, test) {
double res = test->InvokeWithCode<double>();
double res = test->InvokeWithCodeAndThread<double>();
EXPECT_FLOAT_EQ(-1.0, res, 0.000001f);
}
@ -2074,7 +2079,7 @@ ASSEMBLER_TEST_GENERATE(PackedDoubleAbsolute, assembler) {
ASSEMBLER_TEST_RUN(PackedDoubleAbsolute, test) {
double res = test->InvokeWithCode<double>();
double res = test->InvokeWithCodeAndThread<double>();
EXPECT_FLOAT_EQ(1.0, res, 0.000001f);
}
@ -2520,7 +2525,7 @@ ASSEMBLER_TEST_GENERATE(PackedNegate, assembler) {
ASSEMBLER_TEST_RUN(PackedNegate, test) {
float res = test->InvokeWithCode<float>();
float res = test->InvokeWithCodeAndThread<float>();
EXPECT_FLOAT_EQ(-12.3f, res, 0.001f);
}
@ -2538,7 +2543,7 @@ ASSEMBLER_TEST_GENERATE(PackedAbsolute, assembler) {
ASSEMBLER_TEST_RUN(PackedAbsolute, test) {
float res = test->InvokeWithCode<float>();
float res = test->InvokeWithCodeAndThread<float>();
EXPECT_FLOAT_EQ(15.3f, res, 0.001f);
}
@ -2554,7 +2559,7 @@ ASSEMBLER_TEST_GENERATE(PackedSetWZero, assembler) {
ASSEMBLER_TEST_RUN(PackedSetWZero, test) {
float res = test->InvokeWithCode<float>();
float res = test->InvokeWithCodeAndThread<float>();
EXPECT_FLOAT_EQ(0.0f, res, 0.001f);
}
@ -2678,7 +2683,7 @@ ASSEMBLER_TEST_GENERATE(PackedLogicalNot, assembler) {
ASSEMBLER_TEST_RUN(PackedLogicalNot, test) {
uint32_t res = test->InvokeWithCode<uint32_t>();
uint32_t res = test->InvokeWithCodeAndThread<uint32_t>();
EXPECT_EQ(static_cast<uword>(0x0), res);
}
@ -3103,7 +3108,7 @@ ASSEMBLER_TEST_GENERATE(TestObjectCompare, assembler) {
ASSEMBLER_TEST_RUN(TestObjectCompare, test) {
bool res = test->InvokeWithCode<bool>();
bool res = test->InvokeWithCodeAndThread<bool>();
EXPECT_EQ(true, res);
}
@ -3415,10 +3420,13 @@ ASSEMBLER_TEST_RUN(DoubleToDoubleTrunc, test) {
ASSEMBLER_TEST_GENERATE(DoubleAbs, assembler) {
EnterTestFrame(assembler);
#if defined(TARGET_OS_WINDOWS)
// First argument is code object, MSVC passes second argument in XMM1.
__ DoubleAbs(XMM1);
__ movaps(XMM0, XMM1);
// First argument is code object, second argument is thread. MSVC passes
// third argument in XMM2.
__ DoubleAbs(XMM2);
__ movaps(XMM0, XMM2);
#else
// SysV ABI allocates integral and double registers for arguments
// independently.
__ DoubleAbs(XMM0);
#endif
LeaveTestFrame(assembler);
@ -3428,10 +3436,10 @@ ASSEMBLER_TEST_GENERATE(DoubleAbs, assembler) {
ASSEMBLER_TEST_RUN(DoubleAbs, test) {
double val = -12.45;
double res = test->InvokeWithCode<double, double>(val);
double res = test->InvokeWithCodeAndThread<double, double>(val);
EXPECT_FLOAT_EQ(-val, res, 0.001);
val = 12.45;
res = test->InvokeWithCode<double, double>(val);
res = test->InvokeWithCodeAndThread<double, double>(val);
EXPECT_FLOAT_EQ(val, res, 0.001);
}

View file

@ -149,8 +149,6 @@ void FlowGraphTypePropagator::PropagateRecursive(BlockEntryInstr* block) {
}
}
HandleBranchOnStrictCompare(block);
for (intptr_t i = 0; i < block->dominated_blocks().length(); ++i) {
PropagateRecursive(block->dominated_blocks()[i]);
}
@ -159,61 +157,6 @@ void FlowGraphTypePropagator::PropagateRecursive(BlockEntryInstr* block) {
}
void FlowGraphTypePropagator::HandleBranchOnStrictCompare(
BlockEntryInstr* block) {
BranchInstr* branch = block->last_instruction()->AsBranch();
if (branch == NULL) {
return;
}
StrictCompareInstr* compare = branch->comparison()->AsStrictCompare();
if ((compare == NULL) || !compare->right()->BindsToConstant()) {
return;
}
const intptr_t rollback_point = rollback_.length();
Definition* defn = compare->left()->definition();
const Object& right = compare->right()->BoundConstant();
intptr_t cid = right.GetClassId();
if (defn->IsLoadClassId() && right.IsSmi()) {
defn = defn->AsLoadClassId()->object()->definition();
cid = Smi::Cast(right).Value();
}
if (!CheckClassInstr::IsImmutableClassId(cid)) {
if ((cid == kOneByteStringCid) || (cid == kTwoByteStringCid)) {
SetTypeOf(defn, ZoneCompileType::Wrap(CompileType::String()));
PropagateRecursive((compare->kind() == Token::kEQ_STRICT) ?
branch->true_successor() : branch->false_successor());
RollbackTo(rollback_point);
}
return;
}
if (compare->kind() == Token::kEQ_STRICT) {
if (cid == kNullCid) {
branch->set_constrained_type(MarkNonNullable(defn));
PropagateRecursive(branch->false_successor());
}
SetCid(defn, cid);
PropagateRecursive(branch->true_successor());
} else if (compare->kind() == Token::kNE_STRICT) {
if (cid == kNullCid) {
branch->set_constrained_type(MarkNonNullable(defn));
PropagateRecursive(branch->true_successor());
}
SetCid(defn, cid);
PropagateRecursive(branch->false_successor());
}
RollbackTo(rollback_point);
}
void FlowGraphTypePropagator::RollbackTo(intptr_t rollback_point) {
for (intptr_t i = rollback_.length() - 1; i >= rollback_point; i--) {
types_[rollback_[i].index()] = rollback_[i].type();

View file

@ -20,7 +20,6 @@ class FlowGraphTypePropagator : public FlowGraphVisitor {
void Propagate();
void PropagateRecursive(BlockEntryInstr* block);
void HandleBranchOnStrictCompare(BlockEntryInstr* block);
void RollbackTo(intptr_t rollback_point);

View file

@ -1485,6 +1485,7 @@ class Class : public Object {
friend class Object;
friend class Type;
friend class Intrinsifier;
friend class Precompiler;
};

View file

@ -713,6 +713,7 @@ void Precompiler::DropUncompiledFunctions() {
Class& cls = Class::Handle(Z);
Array& functions = Array::Handle(Z);
Function& function = Function::Handle(Z);
Function& function2 = Function::Handle(Z);
GrowableObjectArray& retained_functions = GrowableObjectArray::Handle(Z);
GrowableObjectArray& closures = GrowableObjectArray::Handle(Z);
@ -729,7 +730,16 @@ void Precompiler::DropUncompiledFunctions() {
retained_functions = GrowableObjectArray::New();
for (intptr_t j = 0; j < functions.Length(); j++) {
function ^= functions.At(j);
if (function.HasCode()) {
bool retain = function.HasCode();
if (!retain && function.HasImplicitClosureFunction()) {
// It can happen that all uses of an implicit closure inline their
// target function, leaving the target function uncompiled. Keep
// the target function anyway so we can enumerate it to bind its
// static calls, etc.
function2 = function.ImplicitClosureFunction();
retain = function2.HasCode();
}
if (retain) {
retained_functions.Add(function);
function.DropUncompiledImplicitClosureFunction();
} else {
@ -780,7 +790,10 @@ void Precompiler::BindStaticCalls() {
}
void VisitFunction(const Function& function) {
ASSERT(function.HasCode());
if (!function.HasCode()) {
ASSERT(function.HasImplicitClosureFunction());
return;
}
code_ = function.CurrentCode();
table_ = code_.static_calls_target_table();
@ -837,6 +850,10 @@ void Precompiler::DedupStackmaps() {
}
void VisitFunction(const Function& function) {
if (!function.HasCode()) {
ASSERT(function.HasImplicitClosureFunction());
return;
}
code_ = function.CurrentCode();
stackmaps_ = code_.stackmaps();
if (stackmaps_.IsNull()) return;
@ -876,6 +893,7 @@ void Precompiler::VisitFunctions(FunctionVisitor* visitor) {
Library& lib = Library::Handle(Z);
Class& cls = Class::Handle(Z);
Array& functions = Array::Handle(Z);
Object& object = Object::Handle(Z);
Function& function = Function::Handle(Z);
GrowableObjectArray& closures = GrowableObjectArray::Handle(Z);
@ -897,6 +915,15 @@ void Precompiler::VisitFunctions(FunctionVisitor* visitor) {
visitor->VisitFunction(function);
}
}
functions = cls.invocation_dispatcher_cache();
for (intptr_t j = 0; j < functions.Length(); j++) {
object = functions.At(j);
if (object.IsFunction()) {
function ^= functions.At(j);
visitor->VisitFunction(function);
}
}
}
}
closures = isolate()->object_store()->closure_functions();

View file

@ -87,6 +87,51 @@ LEAF_RUNTIME_ENTRY_LIST(DEFAULT_INIT)
}
static const struct ALIGN16 {
uint64_t a;
uint64_t b;
} double_negate_constant =
{0x8000000000000000LL, 0x8000000000000000LL};
static const struct ALIGN16 {
uint64_t a;
uint64_t b;
} double_abs_constant =
{0x7FFFFFFFFFFFFFFFLL, 0x7FFFFFFFFFFFFFFFLL};
static const struct ALIGN16 {
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
} float_not_constant =
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
static const struct ALIGN16 {
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
} float_negate_constant =
{ 0x80000000, 0x80000000, 0x80000000, 0x80000000 };
static const struct ALIGN16 {
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
} float_absolute_constant =
{ 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF };
static const struct ALIGN16 {
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
} float_zerow_constant =
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000 };
void Thread::InitVMConstants() {
#define ASSERT_VM_HEAP(type_name, member_name, init_expr, default_init_value) \
ASSERT((init_expr)->IsOldObject());

View file

@ -79,7 +79,6 @@ class Zone;
V(RawCode*, invoke_dart_code_stub_, \
StubCode::InvokeDartCode_entry()->code(), NULL) \
#define CACHED_ADDRESSES_LIST(V) \
V(uword, update_store_buffer_entry_point_, \
StubCode::UpdateStoreBuffer_entry()->EntryPoint(), 0) \
@ -87,12 +86,23 @@ class Zone;
NativeEntry::NativeCallWrapperEntry(), 0) \
V(RawString**, predefined_symbols_address_, \
Symbols::PredefinedAddress(), NULL) \
V(uword, double_negate_address_, \
reinterpret_cast<uword>(&double_negate_constant), 0) \
V(uword, double_abs_address_, \
reinterpret_cast<uword>(&double_abs_constant), 0) \
V(uword, float_not_address_, \
reinterpret_cast<uword>(&float_not_constant), 0) \
V(uword, float_negate_address_, \
reinterpret_cast<uword>(&float_negate_constant), 0) \
V(uword, float_absolute_address_, \
reinterpret_cast<uword>(&float_absolute_constant), 0) \
V(uword, float_zerow_address_, \
reinterpret_cast<uword>(&float_zerow_constant), 0) \
#define CACHED_CONSTANTS_LIST(V) \
CACHED_VM_OBJECTS_LIST(V) \
CACHED_ADDRESSES_LIST(V) \
// A VM thread; may be executing Dart code or performing helper tasks like
// garbage collection or compilation. The Thread structure associated with
// a thread is allocated by EnsureInit before entering an isolate, and destroyed

View file

@ -106,11 +106,11 @@ void ThreadRegistry::Unschedule(Thread* thread,
OSThread* os_thread = thread->os_thread();
ASSERT(os_thread != NULL);
os_thread->DisableThreadInterrupts();
os_thread->set_thread(NULL);
OSThread::SetCurrent(os_thread);
thread->isolate_ = NULL;
thread->heap_ = NULL;
thread->set_os_thread(NULL);
os_thread->set_thread(NULL);
OSThread::SetCurrent(os_thread);
if (!is_mutator) {
ASSERT(thread->api_top_scope() == NULL);
ReturnThreadToFreelist(thread);

View file

@ -360,36 +360,44 @@ class AssemblerTest {
uword entry() const { return code_.EntryPoint(); }
// Invoke/InvokeWithCode is used to call assembler test functions using the
// ABI calling convention.
// Invoke/InvokeWithCodeAndThread is used to call assembler test functions
// using the ABI calling convention.
// ResultType is the return type of the assembler test function.
// ArgNType is the type of the Nth argument.
#if defined(USING_SIMULATOR)
#if defined(ARCH_IS_64_BIT)
// TODO(fschneider): Make InvokeWithCode<> more general and work on 32-bit.
// TODO(fschneider): Make InvokeWithCodeAndThread<> more general and work on
// 32-bit.
// Since Simulator::Call always return a int64_t, bit_cast does not work
// on 32-bit platforms when returning an int32_t. Since template functions
// don't support partial specialization, we'd need to introduce a helper
// class to support 32-bit return types.
template<typename ResultType> ResultType InvokeWithCode() {
template<typename ResultType> ResultType InvokeWithCodeAndThread() {
const bool fp_return = is_double<ResultType>::value;
const bool fp_args = false;
Thread* thread = Thread::Current();
ASSERT(thread != NULL);
return bit_cast<ResultType, int64_t>(Simulator::Current()->Call(
bit_cast<intptr_t, uword>(entry()),
reinterpret_cast<intptr_t>(&code_), 0, 0, 0, fp_return, fp_args));
reinterpret_cast<intptr_t>(&code_),
reinterpret_cast<intptr_t>(thread),
0, 0, fp_return, fp_args));
}
template<typename ResultType, typename Arg1Type>
ResultType InvokeWithCode(Arg1Type arg1) {
ResultType InvokeWithCodeAndThread(Arg1Type arg1) {
const bool fp_return = is_double<ResultType>::value;
const bool fp_args = is_double<Arg1Type>::value;
// TODO(fschneider): Support double arguments for simulator calls.
COMPILE_ASSERT(!fp_args);
Thread* thread = Thread::Current();
ASSERT(thread != NULL);
return bit_cast<ResultType, int64_t>(Simulator::Current()->Call(
bit_cast<intptr_t, uword>(entry()),
reinterpret_cast<intptr_t>(&code_),
reinterpret_cast<intptr_t>(thread),
reinterpret_cast<intptr_t>(arg1),
0, 0, fp_return, fp_args));
0, fp_return, fp_args));
}
#endif // ARCH_IS_64_BIT
@ -413,15 +421,19 @@ class AssemblerTest {
0, fp_return, fp_args);
}
#else
template<typename ResultType> ResultType InvokeWithCode() {
typedef ResultType (*FunctionType) (const Code&);
return reinterpret_cast<FunctionType>(entry())(code_);
template<typename ResultType> ResultType InvokeWithCodeAndThread() {
Thread* thread = Thread::Current();
ASSERT(thread != NULL);
typedef ResultType (*FunctionType) (const Code&, Thread*);
return reinterpret_cast<FunctionType>(entry())(code_, thread);
}
template<typename ResultType, typename Arg1Type>
ResultType InvokeWithCode(Arg1Type arg1) {
typedef ResultType (*FunctionType) (const Code&, Arg1Type);
return reinterpret_cast<FunctionType>(entry())(code_, arg1);
ResultType InvokeWithCodeAndThread(Arg1Type arg1) {
Thread* thread = Thread::Current();
ASSERT(thread != NULL);
typedef ResultType (*FunctionType) (const Code&, Thread*, Arg1Type);
return reinterpret_cast<FunctionType>(entry())(code_, thread, arg1);
}
template<typename ResultType,

View file

@ -56,14 +56,18 @@ js_backend_cps_ir_constructor_test: Pass, Slow
exit_code_test: Skip # This tests requires checked mode.
[ $checked ]
analyze_dart2js_helpers_test: Pass, Slow
analyze_dart2js_test: Pass, Slow
analyze_unused_dart2js_test: Pass, Slow
uri_retention_test: Pass, Slow
deferred_mirrors_test: Pass, Slow
mirror_final_field_inferrer2_test: Pass, Slow
check_elements_invariants_test: Slow, Pass, Timeout
import_mirrors_test: Slow, Pass
deferred_mirrors_test: Pass, Slow
exit_code_test: Pass, Slow
import_mirrors_test: Slow, Pass
interop_anonymous_unreachable_test: Pass, Slow
mirror_final_field_inferrer2_test: Pass, Slow
source_map_pub_build_validity_test: Pass, Slow
sourcemaps/source_mapping_operators_test: Pass, Slow
uri_retention_test: Pass, Slow
value_range_test: Pass, Slow
[ $mode == debug ]

View file

@ -64,10 +64,11 @@ void test(String specString,
typesReturned: actualReturns, typesInstantiated: actualCreates,
objectType: OBJECT, nullType: NULL);
} catch (e) {
Expect.isTrue(expectError);
Expect.isNotNull(listener.errorMessage, 'Error expected.');
Expect.isTrue(expectError, 'Unexpected error "$specString"');
Expect.isNotNull(listener.errorMessage, 'Error message expected.');
return;
}
Expect.isFalse(expectError, 'Missing error for "$specString".');
Expect.isNull(listener.errorMessage, 'Unexpected error.');
if (returns != null) {
Expect.listEquals(returns, actualReturns, 'Unexpected returns.');
@ -217,17 +218,24 @@ void main() {
test('returns:void;', returns: [], creates: []);
test('returns:;', returns: [OBJECT, NULL], creates: []);
test('returns:var;', returns: [OBJECT, NULL], creates: []);
test('returns:A;', returns: ['A'], creates: []);
test('returns:A|B;', returns: ['A', 'B'], creates: []);
test('returns:A|B|C;', returns: ['A', 'B', 'C'], creates: []);
test('returns:A;', returns: ['A'], creates: ['A']);
test('returns:A|B;', returns: ['A', 'B'], creates: ['A', 'B']);
test('returns:A|B|C;', returns: ['A', 'B', 'C'], creates: ['A', 'B', 'C']);
test('creates:void;', expectError: true);
test('creates:;', expectError: true);
test('creates:var;', expectError: true);
test('creates:;', creates: []);
test('creates:var;', creates: []);
test('creates:A;', returns: [], creates: ['A']);
test('creates:A|B;', returns: [], creates: ['A', 'B']);
test('creates:A|B|C;', returns: [], creates: ['A', 'B', 'C']);
test('returns:void;creates:', returns: [], creates: []);
test('returns:;creates:', returns: [OBJECT, NULL], creates: []);
test('returns:var;creates:', returns: [OBJECT, NULL], creates: []);
test('returns:A;creates:', returns: ['A'], creates: []);
test('returns:A|B;creates:;', returns: ['A', 'B'], creates: []);
test('returns:A|B|C;creates:;', returns: ['A', 'B', 'C'], creates: []);
test('returns:void;creates:A;', returns: [], creates: ['A']);
test('returns:;creates:A|B;', returns: [OBJECT, NULL], creates: ['A', 'B']);
test('returns:var;creates:A|B|C;',
@ -242,19 +250,32 @@ void main() {
testWithSideEffects('returns:void;', returns: [], creates: []);
testWithSideEffects('returns:;', returns: [OBJECT, NULL], creates: []);
testWithSideEffects('returns:var;', returns: [OBJECT, NULL], creates: []);
testWithSideEffects('returns:A;', returns: ['A'], creates: []);
testWithSideEffects('returns:A|B;', returns: ['A', 'B'], creates: []);
testWithSideEffects('returns:A|B|C;', returns: ['A', 'B', 'C'], creates: []);
testWithSideEffects('returns:A;', returns: ['A'], creates: ['A']);
testWithSideEffects('returns:A|B;',
returns: ['A', 'B'], creates: ['A', 'B']);
testWithSideEffects('returns:A|B|C;',
returns: ['A', 'B', 'C'], creates: ['A', 'B', 'C']);
testWithSideEffects('returns: A| B |C ;',
returns: ['A', 'B', 'C'], creates: []);
returns: ['A', 'B', 'C'], creates: ['A', 'B', 'C']);
testWithSideEffects('creates:void;', expectError: true);
testWithSideEffects('creates:;', expectError: true);
testWithSideEffects('creates:var;', expectError: true);
testWithSideEffects('creates:;', creates: []);
testWithSideEffects('creates:var;', creates: []);
testWithSideEffects('creates:A;', returns: [], creates: ['A']);
testWithSideEffects('creates:A|B;', returns: [], creates: ['A', 'B']);
testWithSideEffects('creates:A|B|C;', returns: [], creates: ['A', 'B', 'C']);
testWithSideEffects('returns:void;creates:;', returns: [], creates: []);
testWithSideEffects('returns:;creates:;',
returns: [OBJECT, NULL], creates: []);
testWithSideEffects('returns:var;creates:;',
returns: [OBJECT, NULL], creates: []);
testWithSideEffects('returns:A;creates:;', returns: ['A'], creates: []);
testWithSideEffects('returns:A|B;creates:;',
returns: ['A', 'B'], creates: []);
testWithSideEffects('returns:A|B|C;creates:;',
returns: ['A', 'B', 'C'], creates: []);
testWithSideEffects('returns:void;creates:A;', returns: [], creates: ['A']);
testWithSideEffects('returns:;creates:A|B;',
returns: [OBJECT, NULL], creates: ['A', 'B']);

View file

@ -0,0 +1,56 @@
// 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.
// VMOptions=--optimization-counter-threshold=1000 --max-polymorphic-checks=1
// Test correct loop invariant code motion and type propagation from is-checks
// and null-comparisons.
class B {
var b;
B(this.b);
}
class C {
final f0 = null;
final a;
C() : a = new B(0);
}
foo(x) {
for (var i = 0; i < 10; i++) {
i + i;
i + i;
if (x is C) {
x.a.b < 0;
}
}
}
class Y { var f = null; }
bar(y) {
var x = y.f;
for (var i = 0; i < 10; i++) {
if (x != null) {
x.a.b < 0;
}
}
}
main () {
var o = new Y();
o.f = new C();
bar(o);
o.f = null;
bar(o);
for (var i = 0; i < 1000; i++) bar(o);
foo(new C());
foo(0);
for (var i = 0; i < 1000; i++) foo(0);
}