| // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library dart2js.analyze_helpers.test; |
| |
| import 'dart:io'; |
| |
| import 'package:async_helper/async_helper.dart'; |
| import 'package:compiler/compiler_new.dart' show Diagnostic; |
| import 'package:compiler/src/apiimpl.dart' show CompilerImpl; |
| import 'package:compiler/src/commandline_options.dart'; |
| import 'package:compiler/src/constants/expressions.dart' |
| show ConstructedConstantExpression; |
| import 'package:compiler/src/elements/resolution_types.dart' |
| show ResolutionInterfaceType; |
| import 'package:compiler/src/diagnostics/source_span.dart' show SourceSpan; |
| import 'package:compiler/src/elements/elements.dart'; |
| import 'package:compiler/src/filenames.dart' show nativeToUriPath; |
| import 'package:compiler/src/resolution/semantic_visitor.dart'; |
| import 'package:compiler/src/resolution/tree_elements.dart' show TreeElements; |
| import 'package:compiler/src/source_file_provider.dart' |
| show FormattingDiagnosticHandler; |
| import 'package:compiler/src/tree/tree.dart'; |
| import 'package:compiler/src/universe/call_structure.dart' show CallStructure; |
| import 'package:expect/expect.dart'; |
| |
| import 'memory_compiler.dart'; |
| |
| main(List<String> arguments) { |
| bool verbose = arguments.contains('-v'); |
| |
| List<String> options = <String>[ |
| Flags.analyzeOnly, |
| Flags.analyzeMain, |
| '--categories=Client,Server' |
| ]; |
| if (verbose) { |
| options.add(Flags.verbose); |
| } |
| asyncTest(() async { |
| CompilerImpl compiler = |
| compilerFor(options: options, showDiagnostics: verbose); |
| FormattingDiagnosticHandler diagnostics = |
| new FormattingDiagnosticHandler(compiler.provider); |
| Directory dir = |
| new Directory.fromUri(Uri.base.resolve('pkg/compiler/lib/')); |
| String helpersUriPrefix = dir.uri.resolve('src/helpers/').toString(); |
| HelperAnalyzer analyzer = new HelperAnalyzer(diagnostics, helpersUriPrefix); |
| LibraryElement helperLibrary; |
| for (FileSystemEntity entity in dir.listSync(recursive: true)) { |
| if (entity is File && entity.path.endsWith('.dart')) { |
| Uri file = Uri.base.resolve(nativeToUriPath(entity.path)); |
| if (verbose) { |
| print('---- analyzing $file ----'); |
| } |
| LibraryElement library = await compiler.analyzeUri(file); |
| if (library != null) { |
| if (library.libraryName == 'dart2js.helpers') { |
| helperLibrary = library; |
| } |
| library.forEachLocalMember((Element element) { |
| if (element is ClassElement) { |
| element.forEachLocalMember((_member) { |
| AstElement member = _member; |
| analyzer.analyze(member.resolvedAst); |
| }); |
| } else if (element is MemberElement) { |
| analyzer.analyze(element.resolvedAst); |
| } |
| }); |
| } |
| } |
| } |
| Expect.isNotNull(helperLibrary, 'Helper library not found'); |
| Expect.isTrue(analyzer.isHelper(helperLibrary), |
| "Helper library $helperLibrary is not considered a helper."); |
| Expect.isTrue(analyzer.errors.isEmpty, "Errors found."); |
| }); |
| } |
| |
| class HelperAnalyzer extends TraversalVisitor { |
| final FormattingDiagnosticHandler diagnostics; |
| final String helpersUriPrefix; |
| List<SourceSpan> errors = <SourceSpan>[]; |
| |
| ResolvedAst resolvedAst; |
| |
| @override |
| TreeElements get elements => resolvedAst.elements; |
| |
| AnalyzableElement get analyzedElement => resolvedAst.element; |
| |
| HelperAnalyzer(this.diagnostics, this.helpersUriPrefix) : super(null); |
| |
| @override |
| void apply(Node node, [_]) { |
| node.accept(this); |
| } |
| |
| void analyze(ResolvedAst resolvedAst) { |
| if (resolvedAst.kind != ResolvedAstKind.PARSED) { |
| // Skip synthesized members. |
| return; |
| } |
| this.resolvedAst = resolvedAst; |
| apply(resolvedAst.node); |
| this.resolvedAst = null; |
| } |
| |
| bool isHelper(Element element) { |
| Uri uri = element.library.canonicalUri; |
| return '$uri'.startsWith(helpersUriPrefix); |
| } |
| |
| void checkAccess(Node node, MemberElement element) { |
| if (isHelper(element) && !isHelper(analyzedElement)) { |
| Uri uri = analyzedElement.implementation.sourcePosition.uri; |
| SourceSpan span = new SourceSpan.fromNode(uri, node); |
| diagnostics.report(null, span.uri, span.begin, span.end, |
| "Helper used in production code.", Diagnostic.ERROR); |
| errors.add(span); |
| } |
| } |
| |
| @override |
| void visitTopLevelFieldInvoke(Send node, FieldElement field, |
| NodeList arguments, CallStructure callStructure, _) { |
| checkAccess(node, field); |
| apply(arguments); |
| } |
| |
| @override |
| void visitTopLevelGetterInvoke(Send node, GetterElement getter, |
| NodeList arguments, CallStructure callStructure, _) { |
| checkAccess(node, getter); |
| apply(arguments); |
| } |
| |
| @override |
| void visitTopLevelFunctionInvoke(Send node, MethodElement method, |
| NodeList arguments, CallStructure callStructure, _) { |
| checkAccess(node, method); |
| apply(arguments); |
| } |
| |
| @override |
| void visitTopLevelFieldGet(Send node, FieldElement field, _) { |
| checkAccess(node, field); |
| } |
| |
| @override |
| void visitTopLevelGetterGet(Send node, GetterElement getter, _) { |
| checkAccess(node, getter); |
| } |
| |
| @override |
| void visitTopLevelFunctionGet(Send node, MethodElement method, _) { |
| checkAccess(node, method); |
| } |
| |
| @override |
| void visitGenerativeConstructorInvoke( |
| NewExpression node, |
| ConstructorElement constructor, |
| ResolutionInterfaceType type, |
| NodeList arguments, |
| CallStructure callStructure, |
| _) { |
| checkAccess(node, constructor); |
| apply(arguments); |
| } |
| |
| @override |
| void visitRedirectingGenerativeConstructorInvoke( |
| NewExpression node, |
| ConstructorElement constructor, |
| ResolutionInterfaceType type, |
| NodeList arguments, |
| CallStructure callStructure, |
| _) { |
| checkAccess(node, constructor); |
| apply(arguments); |
| } |
| |
| @override |
| void visitFactoryConstructorInvoke( |
| NewExpression node, |
| ConstructorElement constructor, |
| ResolutionInterfaceType type, |
| NodeList arguments, |
| CallStructure callStructure, |
| _) { |
| checkAccess(node, constructor); |
| apply(arguments); |
| } |
| |
| @override |
| void visitRedirectingFactoryConstructorInvoke( |
| NewExpression node, |
| ConstructorElement constructor, |
| ResolutionInterfaceType type, |
| ConstructorElement effectiveTarget, |
| ResolutionInterfaceType effectiveTargetType, |
| NodeList arguments, |
| CallStructure callStructure, |
| _) { |
| checkAccess(node, constructor); |
| apply(arguments); |
| } |
| |
| @override |
| void visitConstConstructorInvoke( |
| NewExpression node, ConstructedConstantExpression constant, _) { |
| ConstructorElement constructor = constant.target; |
| checkAccess(node, constructor); |
| } |
| } |