Chromium Code Reviews| Index: pkg/analyzer/lib/src/task/dart.dart |
| diff --git a/pkg/analyzer/lib/src/task/dart.dart b/pkg/analyzer/lib/src/task/dart.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..8303e6dfa19811ab3e105b56b871b0e42060c286 |
| --- /dev/null |
| +++ b/pkg/analyzer/lib/src/task/dart.dart |
| @@ -0,0 +1,340 @@ |
| +// 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 analyzer.src.task.dart; |
| + |
| +import 'dart:collection'; |
| + |
| +import 'package:analyzer/src/generated/ast.dart'; |
| +import 'package:analyzer/src/generated/element.dart'; |
| +import 'package:analyzer/src/generated/engine.dart' hide AnalysisTask; |
| +import 'package:analyzer/src/generated/error.dart'; |
| +import 'package:analyzer/src/generated/java_engine.dart'; |
| +import 'package:analyzer/src/generated/parser.dart'; |
| +import 'package:analyzer/src/generated/resolver.dart'; |
| +import 'package:analyzer/src/generated/scanner.dart'; |
| +import 'package:analyzer/src/generated/source.dart'; |
| +import 'package:analyzer/task/dart.dart'; |
| +import 'package:analyzer/task/general.dart'; |
| +import 'package:analyzer/task/model.dart'; |
| + |
| +/** |
| + * A task that builds a compilation unit element for a single compilation unit. |
| + */ |
| +class BuildCompilationUnitElementTask extends AnalysisTask { |
| + /** |
| + * The name of the input whose value is the line information for the |
| + * compilation unit. |
| + */ |
| + static const String LINE_INFO_INPUT_NAME = "lineInfo"; |
| + |
| + /** |
| + * The name of the input whose value is the AST for the compilation unit. |
| + */ |
| + static const String PARSED_UNIT_INPUT_NAME = "parsedUnit"; |
| + |
| + /** |
| + * The task descriptor describing this kind of task. |
| + */ |
| + static final TaskDescriptor DESCRIPTOR = new TaskDescriptor( |
| + 'BUILD_COMPILATION_UNIT_ELEMENT', |
| + createTask, |
| + buildInputs, |
| + <ResultDescriptor>[COMPILATION_UNIT_ELEMENT, BUILT_UNIT]); |
| + |
| + /** |
| + * Initialize a newly created task to build a compilation unit element for |
| + * the given [target] in the given [context]. |
| + */ |
| + BuildCompilationUnitElementTask(InternalAnalysisContext context, |
| + AnalysisTarget target) |
| + : super(context, target); |
| + |
| + @override |
| + String get description { |
| + Source source = target.source; |
| + if (source == null) { |
| + return "build the unit element model for <unknown source>"; |
| + } |
| + return "build the unit element model for " + source.fullName; |
| + } |
| + |
| + @override |
| + TaskDescriptor get descriptor => DESCRIPTOR; |
| + |
| + @override |
| + void internalPerform() { |
| + Source source = getRequiredSource(); |
| + CompilationUnit unit = getRequiredInput(PARSED_UNIT_INPUT_NAME); |
| + |
| + CompilationUnitBuilder builder = new CompilationUnitBuilder(); |
| + CompilationUnitElement element = builder.buildCompilationUnit(source, unit); |
| + |
| + outputs[COMPILATION_UNIT_ELEMENT] = element; |
| + outputs[BUILT_UNIT] = 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) { |
| + return <String, TaskInput>{ |
| + PARSED_UNIT_INPUT_NAME: PARSED_UNIT.inputFor(target) |
| + }; |
| + } |
| + |
| + /** |
| + * Create a [BuildCompilationUnitElementTask] based on the given [target] in |
| + * the given [context]. |
| + */ |
| + static BuildCompilationUnitElementTask createTask(AnalysisContext context, |
| + AnalysisTarget target) { |
| + return new BuildCompilationUnitElementTask(context, target); |
| + } |
| +} |
| + |
| +/** |
| + * A task that parses the content of a Dart file, producing an AST structure. |
| + */ |
| +class ParseDartTask extends AnalysisTask { |
| + /** |
| + * The name of the input whose value is the line information produced for the |
| + * file. |
| + */ |
| + static const String LINE_INFO_INPUT_NAME = "lineInfo"; |
| + |
| + /** |
| + * The name of the input whose value is the token stream produced for the file. |
| + */ |
| + static const String TOKEN_STREAM_INPUT_NAME = "tokenStream"; |
| + |
| + /** |
| + * The task descriptor describing this kind of task. |
| + */ |
| + static final TaskDescriptor DESCRIPTOR = new TaskDescriptor( |
| + 'PARSE_DART', |
| + createTask, |
| + buildInputs, |
| + <ResultDescriptor>[ |
| + EXPORTED_LIBRARIES, |
| + IMPORTED_LIBRARIES, |
| + INCLUDED_PARTS, |
| + PARSE_ERRORS, |
| + PARSED_UNIT, |
| + SOURCE_KIND]); |
| + |
| + /** |
| + * Initialize a newly created task to parse the content of the Dart file |
| + * associated with the given [target] in the given [context]. |
| + */ |
| + ParseDartTask(InternalAnalysisContext context, AnalysisTarget target) |
| + : super(context, target); |
| + |
| + @override |
| + String get description { |
| + Source source = target.source; |
| + if (source == null) { |
| + return "parse <unknown source> as Dart"; |
| + } |
| + return "parse ${source.fullName} as Dart"; |
| + } |
| + |
| + @override |
| + TaskDescriptor get descriptor => DESCRIPTOR; |
| + |
| + @override |
| + void internalPerform() { |
| + Source source = getRequiredSource(); |
| + LineInfo lineInfo = getRequiredInput(LINE_INFO_INPUT_NAME); |
| + Token tokenStream = getRequiredInput(TOKEN_STREAM_INPUT_NAME); |
| + |
| + RecordingErrorListener errorListener = new RecordingErrorListener(); |
| + Parser parser = new Parser(source, errorListener); |
| + AnalysisOptions options = context.analysisOptions; |
| + parser.parseFunctionBodies = options.analyzeFunctionBodies; |
| + CompilationUnit unit = parser.parseCompilationUnit(tokenStream); |
| + unit.lineInfo = lineInfo; |
| + |
| + bool hasNonPartOfDirective = false; |
| + bool hasPartOfDirective = false; |
| + HashSet<Source> exportedSources = new HashSet<Source>(); |
| + HashSet<Source> importedSources = new HashSet<Source>(); |
|
Paul Berry
2015/01/28 18:25:53
If there is no explicit import of dart:core, shoul
Brian Wilkerson
2015/01/28 20:51:07
Possibly. At the moment this is done when we build
|
| + HashSet<Source> includedSources = new HashSet<Source>(); |
| + for (Directive directive in unit.directives) { |
| + if (directive is PartOfDirective) { |
| + hasPartOfDirective = true; |
| + } else { |
| + hasNonPartOfDirective = true; |
| + if (directive is UriBasedDirective) { |
| + Source referencedSource = |
| + resolveDirective(context, source, directive, errorListener); |
| + if (referencedSource != null) { |
| + if (directive is ExportDirective) { |
| + exportedSources.add(referencedSource); |
| + } else if (directive is ImportDirective) { |
| + importedSources.add(referencedSource); |
| + } else if (directive is PartDirective) { |
| + if (referencedSource != source) { |
| + includedSources.add(referencedSource); |
| + } |
| + } else { |
| + throw new AnalysisException( |
| + "$runtimeType failed to handle a ${directive.runtimeType}"); |
| + } |
| + } |
| + } |
| + } |
| + } |
| + SourceKind sourceKind = SourceKind.LIBRARY; |
| + if (!hasNonPartOfDirective && hasPartOfDirective) { |
| + sourceKind = SourceKind.PART; |
| + } |
| + |
| + outputs[EXPORTED_LIBRARIES] = exportedSources.toList(); |
| + outputs[IMPORTED_LIBRARIES] = importedSources.toList(); |
| + outputs[INCLUDED_PARTS] = includedSources.toList(); |
| + outputs[PARSE_ERRORS] = errorListener.getErrorsForSource(source); |
| + outputs[PARSED_UNIT] = unit; |
| + outputs[SOURCE_KIND] = sourceKind; |
| + } |
| + |
| + /** |
| + * 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) { |
| + return <String, TaskInput>{ |
| + LINE_INFO_INPUT_NAME: LINE_INFO.inputFor(target), |
| + TOKEN_STREAM_INPUT_NAME: TOKEN_STREAM.inputFor(target) |
| + }; |
| + } |
| + |
| + /** |
| + * Create a [ParseDartTask] based on the given [target] in the given |
| + * [context]. |
| + */ |
| + static ParseDartTask createTask(AnalysisContext context, |
| + AnalysisTarget target) { |
| + return new ParseDartTask(context, target); |
| + } |
| + |
| + /** |
| + * Return the result of resolving the URI of the given URI-based [directive] |
| + * against the URI of the given library, or `null` if the URI is not valid. |
| + * |
| + * Resolution is to be performed in the given [context]. Errors should be |
| + * reported to the [errorListener]. |
| + */ |
| + static Source resolveDirective(AnalysisContext context, Source librarySource, |
| + UriBasedDirective directive, AnalysisErrorListener errorListener) { |
| + StringLiteral uriLiteral = directive.uri; |
| + String uriContent = uriLiteral.stringValue; |
| + if (uriContent != null) { |
| + uriContent = uriContent.trim(); |
| + directive.uriContent = uriContent; |
| + } |
| + UriValidationCode code = directive.validate(); |
| + if (code == null) { |
| + String encodedUriContent = Uri.encodeFull(uriContent); |
| + Source source = |
| + context.sourceFactory.resolveUri(librarySource, encodedUriContent); |
| + directive.source = source; |
| + return source; |
| + } |
| + if (code == UriValidationCode.URI_WITH_DART_EXT_SCHEME) { |
| + return null; |
| + } |
| + if (code == UriValidationCode.URI_WITH_INTERPOLATION) { |
| + errorListener.onError( |
| + new AnalysisError.con2( |
| + librarySource, |
| + uriLiteral.offset, |
| + uriLiteral.length, |
| + CompileTimeErrorCode.URI_WITH_INTERPOLATION)); |
| + return null; |
| + } |
| + if (code == UriValidationCode.INVALID_URI) { |
| + errorListener.onError( |
| + new AnalysisError.con2( |
| + librarySource, |
| + uriLiteral.offset, |
| + uriLiteral.length, |
| + CompileTimeErrorCode.INVALID_URI, |
| + [uriContent])); |
| + return null; |
| + } |
| + throw new AnalysisException('Failed to handle validation code: $code'); |
| + } |
| +} |
| + |
| +/** |
| + * A task that scans the content of a file, producing a set of Dart tokens. |
| + */ |
| +class ScanDartTask extends AnalysisTask { |
| + /** |
| + * The name of the input whose value is the content of the file. |
| + */ |
| + static const String CONTENT_INPUT_NAME = "content"; |
| + |
| + /** |
| + * The task descriptor describing this kind of task. |
| + */ |
| + static final TaskDescriptor DESCRIPTOR = new TaskDescriptor( |
| + 'SCAN_DART', |
| + createTask, |
| + buildInputs, |
| + <ResultDescriptor>[LINE_INFO, SCAN_ERRORS, TOKEN_STREAM]); |
| + |
| + /** |
| + * Initialize a newly created task to access the content of the source |
| + * associated with the given [target] in the given [context]. |
| + */ |
| + ScanDartTask(InternalAnalysisContext context, AnalysisTarget target) |
| + : super(context, target); |
| + |
| + @override |
| + String get description { |
| + Source source = target.source; |
| + if (source == null) { |
|
Paul Berry
2015/01/28 18:25:53
This logic for handling source == null is repeated
Brian Wilkerson
2015/01/28 20:51:07
I don't follow. internalPerform doesn't have to be
|
| + return "scan <unknown source> as Dart"; |
| + } |
| + return "scan ${source.fullName} as Dart"; |
| + } |
| + |
| + @override |
| + TaskDescriptor get descriptor => DESCRIPTOR; |
| + |
| + @override |
| + void internalPerform() { |
| + Source source = getRequiredSource(); |
| + String content = getRequiredInput(CONTENT_INPUT_NAME); |
| + |
| + RecordingErrorListener errorListener = new RecordingErrorListener(); |
| + Scanner scanner = |
| + new Scanner(source, new CharSequenceReader(content), errorListener); |
| + scanner.preserveComments = context.analysisOptions.preserveComments; |
| + outputs[TOKEN_STREAM] = scanner.tokenize(); |
| + outputs[LINE_INFO] = new LineInfo(scanner.lineStarts); |
| + outputs[SCAN_ERRORS] = errorListener.getErrorsForSource(source); |
| + } |
| + |
| + /** |
| + * 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) { |
| + return <String, TaskInput>{ |
| + CONTENT_INPUT_NAME: CONTENT.inputFor(target) |
| + }; |
| + } |
| + |
| + /** |
| + * Create a [ScanDartTask] based on the given [target] in the given [context]. |
| + */ |
| + static ScanDartTask createTask(AnalysisContext context, |
| + AnalysisTarget target) { |
| + return new ScanDartTask(context, target); |
| + } |
| +} |