Version 1.5.0-dev.4.4
Reverting bleeding edge revisions 37107 and r37101 from trunk
svn merge -r37107:37106 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -r37101:37100 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
git-svn-id: http://dart.googlecode.com/svn/trunk@37111 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkg/analysis_server/lib/src/computer/computer_highlights.dart b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
new file mode 100644
index 0000000..06a3db4
--- /dev/null
+++ b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
@@ -0,0 +1,517 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library computer.highlights;
+
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/scanner.dart';
+
+
+/**
+ * A computer for [HighlightRegion]s in a Dart [CompilationUnit].
+ */
+class DartUnitHighlightsComputer {
+ final CompilationUnit _unit;
+
+ final List<Map<String, Object>> _regions = <Map<String, Object>>[];
+
+ DartUnitHighlightsComputer(this._unit);
+
+ /**
+ * Returns the computed highlight regions, not `null`.
+ */
+ List<Map<String, Object>> compute() {
+ _unit.accept(new _DartUnitHighlightsComputerVisitor(this));
+ return _regions;
+ }
+
+ void _addIdentifierRegion(SimpleIdentifier node) {
+ if (_addIdentifierRegion_keyword(node)) {
+ return;
+ }
+ if (_addIdentifierRegion_class(node)) {
+ return;
+ }
+ if (_addIdentifierRegion_constructor(node)) {
+ return;
+ }
+ if (_addIdentifierRegion_dynamicType(node)) {
+ return;
+ }
+ if (_addIdentifierRegion_getterSetterDeclaration(node)) {
+ return;
+ }
+ if (_addIdentifierRegion_field(node)) {
+ return;
+ }
+ if (_addIdentifierRegion_function(node)) {
+ return;
+ }
+ if (_addIdentifierRegion_functionTypeAlias(node)) {
+ return;
+ }
+ if (_addIdentifierRegion_importPrefix(node)) {
+ return;
+ }
+ if (_addIdentifierRegion_localVariable(node)) {
+ return;
+ }
+ if (_addIdentifierRegion_method(node)) {
+ return;
+ }
+ if (_addIdentifierRegion_parameter(node)) {
+ return;
+ }
+ if (_addIdentifierRegion_topLevelVariable(node)) {
+ return;
+ }
+ if (_addIdentifierRegion_typeParameter(node)) {
+ return;
+ }
+ _addRegion_node(node, HighlightType.IDENTIFIER_DEFAULT);
+ }
+
+ void _addIdentifierRegion_annotation(Annotation node) {
+ ArgumentList arguments = node.arguments;
+ if (arguments == null) {
+ _addRegion_node(node, HighlightType.ANNOTATION);
+ } else {
+ _addRegion_nodeStart_tokenEnd(node, arguments.beginToken, HighlightType.ANNOTATION);
+ _addRegion_token(arguments.endToken, HighlightType.ANNOTATION);
+ }
+ }
+
+ bool _addIdentifierRegion_class(SimpleIdentifier node) {
+ Element element = node.staticElement;
+ if (element is! ClassElement) {
+ return false;
+ }
+ return _addRegion_node(node, HighlightType.CLASS);
+ }
+
+ bool _addIdentifierRegion_constructor(SimpleIdentifier node) {
+ Element element = node.staticElement;
+ if (element is! ConstructorElement) {
+ return false;
+ }
+ return _addRegion_node(node, HighlightType.CONSTRUCTOR);
+ }
+
+ bool _addIdentifierRegion_dynamicType(SimpleIdentifier node) {
+ // should be variable
+ Element element = node.staticElement;
+ if (element is! VariableElement) {
+ return false;
+ }
+ // has propagated type
+ if (node.propagatedType != null) {
+ return false;
+ }
+ // has dynamic static type
+ DartType staticType = node.staticType;
+ if (staticType == null || !staticType.isDynamic) {
+ return false;
+ }
+ // OK
+ return _addRegion_node(node, HighlightType.DYNAMIC_TYPE);
+ }
+
+ bool _addIdentifierRegion_field(SimpleIdentifier node) {
+ Element element = node.bestElement;
+ if (element is FieldFormalParameterElement) {
+ element = (element as FieldFormalParameterElement).field;
+ }
+ if (element is FieldElement) {
+ if ((element as FieldElement).isStatic) {
+ return _addRegion_node(node, HighlightType.FIELD_STATIC);
+ } else {
+ return _addRegion_node(node, HighlightType.FIELD);
+ }
+ }
+ if (element is PropertyAccessorElement) {
+ if ((element as PropertyAccessorElement).isStatic) {
+ return _addRegion_node(node, HighlightType.FIELD_STATIC);
+ } else {
+ return _addRegion_node(node, HighlightType.FIELD);
+ }
+ }
+ return false;
+ }
+
+ bool _addIdentifierRegion_function(SimpleIdentifier node) {
+ Element element = node.staticElement;
+ if (element is! FunctionElement) {
+ return false;
+ }
+ HighlightType type;
+ if (node.inDeclarationContext()) {
+ type = HighlightType.FUNCTION_DECLARATION;
+ } else {
+ type = HighlightType.FUNCTION;
+ }
+ return _addRegion_node(node, type);
+ }
+
+ bool _addIdentifierRegion_functionTypeAlias(SimpleIdentifier node) {
+ Element element = node.staticElement;
+ if (element is! FunctionTypeAliasElement) {
+ return false;
+ }
+ return _addRegion_node(node, HighlightType.FUNCTION_TYPE_ALIAS);
+ }
+
+ bool _addIdentifierRegion_getterSetterDeclaration(SimpleIdentifier node) {
+ // should be declaration
+ AstNode parent = node.parent;
+ if (!(parent is MethodDeclaration || parent is FunctionDeclaration)) {
+ return false;
+ }
+ // should be property accessor
+ Element element = node.staticElement;
+ if (element is! PropertyAccessorElement) {
+ return false;
+ }
+ // getter or setter
+ PropertyAccessorElement propertyAccessorElement = element as PropertyAccessorElement;
+ if (propertyAccessorElement.isGetter) {
+ return _addRegion_node(node, HighlightType.GETTER_DECLARATION);
+ } else {
+ return _addRegion_node(node, HighlightType.SETTER_DECLARATION);
+ }
+ }
+
+ bool _addIdentifierRegion_importPrefix(SimpleIdentifier node) {
+ Element element = node.staticElement;
+ if (element is! PrefixElement) {
+ return false;
+ }
+ return _addRegion_node(node, HighlightType.IMPORT_PREFIX);
+ }
+
+ bool _addIdentifierRegion_keyword(SimpleIdentifier node) {
+ String name = node.name;
+ if (name == "void") {
+ return _addRegion_node(node, HighlightType.KEYWORD);
+ }
+ return false;
+ }
+
+ bool _addIdentifierRegion_localVariable(SimpleIdentifier node) {
+ Element element = node.staticElement;
+ if (element is! LocalVariableElement) {
+ return false;
+ }
+ // OK
+ HighlightType type;
+ if (node.inDeclarationContext()) {
+ type = HighlightType.LOCAL_VARIABLE_DECLARATION;
+ } else {
+ type = HighlightType.LOCAL_VARIABLE;
+ }
+ return _addRegion_node(node, type);
+ }
+
+ bool _addIdentifierRegion_method(SimpleIdentifier node) {
+ Element element = node.bestElement;
+ if (element is! MethodElement) {
+ return false;
+ }
+ MethodElement methodElement = element as MethodElement;
+ bool isStatic = methodElement.isStatic;
+ // OK
+ HighlightType type;
+ if (node.inDeclarationContext()) {
+ if (isStatic) {
+ type = HighlightType.METHOD_DECLARATION_STATIC;
+ } else {
+ type = HighlightType.METHOD_DECLARATION;
+ }
+ } else {
+ if (isStatic) {
+ type = HighlightType.METHOD_STATIC;
+ } else {
+ type = HighlightType.METHOD;
+ }
+ }
+ return _addRegion_node(node, type);
+ }
+
+ bool _addIdentifierRegion_parameter(SimpleIdentifier node) {
+ Element element = node.staticElement;
+ if (element is! ParameterElement) {
+ return false;
+ }
+ return _addRegion_node(node, HighlightType.PARAMETER);
+ }
+
+ bool _addIdentifierRegion_topLevelVariable(SimpleIdentifier node) {
+ Element element = node.staticElement;
+ if (element is! TopLevelVariableElement) {
+ return false;
+ }
+ return _addRegion_node(node, HighlightType.TOP_LEVEL_VARIABLE);
+ }
+
+ bool _addIdentifierRegion_typeParameter(SimpleIdentifier node) {
+ Element element = node.staticElement;
+ if (element is! TypeParameterElement) {
+ return false;
+ }
+ return _addRegion_node(node, HighlightType.TYPE_PARAMETER);
+ }
+
+ void _addRegion(int offset, int length, HighlightType type) {
+ _regions.add({'offset': offset, 'length': length, 'type': type.name});
+ }
+
+ bool _addRegion_node(AstNode node, HighlightType type) {
+ int offset = node.offset;
+ int length = node.length;
+ _addRegion(offset, length, type);
+ return true;
+ }
+
+ void _addRegion_nodeStart_tokenEnd(AstNode a, Token b, HighlightType type) {
+ int offset = a.offset;
+ int end = b.end;
+ _addRegion(offset, end - offset, type);
+ }
+
+ void _addRegion_token(Token token, HighlightType type) {
+ if (token != null) {
+ int offset = token.offset;
+ int length = token.length;
+ _addRegion(offset, length, type);
+ }
+ }
+
+ void _addRegion_tokenStart_tokenEnd(Token a, Token b, HighlightType type) {
+ int offset = a.offset;
+ int end = b.end;
+ _addRegion(offset, end - offset, type);
+ }
+}
+
+
+/**
+ * An AST visitor for [DartUnitHighlightsComputer].
+ */
+class _DartUnitHighlightsComputerVisitor extends RecursiveAstVisitor<Object> {
+ final DartUnitHighlightsComputer computer;
+
+ _DartUnitHighlightsComputerVisitor(this.computer);
+
+ @override
+ Object visitAnnotation(Annotation node) {
+ computer._addIdentifierRegion_annotation(node);
+ return super.visitAnnotation(node);
+ }
+
+ @override
+ Object visitAsExpression(AsExpression node) {
+ computer._addRegion_token(node.asOperator, HighlightType.BUILT_IN);
+ return super.visitAsExpression(node);
+ }
+
+ @override
+ Object visitBooleanLiteral(BooleanLiteral node) {
+ computer._addRegion_node(node, HighlightType.LITERAL_BOOLEAN);
+ return super.visitBooleanLiteral(node);
+ }
+
+ @override
+ Object visitCatchClause(CatchClause node) {
+ computer._addRegion_token(node.onKeyword, HighlightType.BUILT_IN);
+ return super.visitCatchClause(node);
+ }
+
+ @override
+ Object visitClassDeclaration(ClassDeclaration node) {
+ computer._addRegion_token(node.abstractKeyword, HighlightType.BUILT_IN);
+ return super.visitClassDeclaration(node);
+ }
+
+ @override
+ Object visitConstructorDeclaration(ConstructorDeclaration node) {
+ computer._addRegion_token(node.externalKeyword, HighlightType.BUILT_IN);
+ computer._addRegion_token(node.factoryKeyword, HighlightType.BUILT_IN);
+ return super.visitConstructorDeclaration(node);
+ }
+
+ @override
+ Object visitDoubleLiteral(DoubleLiteral node) {
+ computer._addRegion_node(node, HighlightType.LITERAL_DOUBLE);
+ return super.visitDoubleLiteral(node);
+ }
+
+ @override
+ Object visitExportDirective(ExportDirective node) {
+ computer._addRegion_token(node.keyword, HighlightType.BUILT_IN);
+ return super.visitExportDirective(node);
+ }
+
+ @override
+ Object visitFieldDeclaration(FieldDeclaration node) {
+ computer._addRegion_token(node.staticKeyword, HighlightType.BUILT_IN);
+ return super.visitFieldDeclaration(node);
+ }
+
+ @override
+ Object visitFunctionDeclaration(FunctionDeclaration node) {
+ computer._addRegion_token(node.externalKeyword, HighlightType.BUILT_IN);
+ computer._addRegion_token(node.propertyKeyword, HighlightType.BUILT_IN);
+ return super.visitFunctionDeclaration(node);
+ }
+
+ @override
+ Object visitFunctionTypeAlias(FunctionTypeAlias node) {
+ computer._addRegion_token(node.keyword, HighlightType.BUILT_IN);
+ return super.visitFunctionTypeAlias(node);
+ }
+
+ @override
+ Object visitHideCombinator(HideCombinator node) {
+ computer._addRegion_token(node.keyword, HighlightType.BUILT_IN);
+ return super.visitHideCombinator(node);
+ }
+
+ @override
+ Object visitImplementsClause(ImplementsClause node) {
+ computer._addRegion_token(node.keyword, HighlightType.BUILT_IN);
+ return super.visitImplementsClause(node);
+ }
+
+ @override
+ Object visitImportDirective(ImportDirective node) {
+ computer._addRegion_token(node.keyword, HighlightType.BUILT_IN);
+ computer._addRegion_token(node.deferredToken, HighlightType.BUILT_IN);
+ computer._addRegion_token(node.asToken, HighlightType.BUILT_IN);
+ return super.visitImportDirective(node);
+ }
+
+ @override
+ Object visitIntegerLiteral(IntegerLiteral node) {
+ computer._addRegion_node(node, HighlightType.LITERAL_INTEGER);
+ return super.visitIntegerLiteral(node);
+ }
+
+ @override
+ Object visitLibraryDirective(LibraryDirective node) {
+ computer._addRegion_token(node.keyword, HighlightType.BUILT_IN);
+ return super.visitLibraryDirective(node);
+ }
+
+ @override
+ Object visitMethodDeclaration(MethodDeclaration node) {
+ computer._addRegion_token(node.externalKeyword, HighlightType.BUILT_IN);
+ computer._addRegion_token(node.modifierKeyword, HighlightType.BUILT_IN);
+ computer._addRegion_token(node.operatorKeyword, HighlightType.BUILT_IN);
+ computer._addRegion_token(node.propertyKeyword, HighlightType.BUILT_IN);
+ return super.visitMethodDeclaration(node);
+ }
+
+ @override
+ Object visitNativeClause(NativeClause node) {
+ computer._addRegion_token(node.keyword, HighlightType.BUILT_IN);
+ return super.visitNativeClause(node);
+ }
+
+ @override
+ Object visitNativeFunctionBody(NativeFunctionBody node) {
+ computer._addRegion_token(node.nativeToken, HighlightType.BUILT_IN);
+ return super.visitNativeFunctionBody(node);
+ }
+
+ @override
+ Object visitPartDirective(PartDirective node) {
+ computer._addRegion_token(node.keyword, HighlightType.BUILT_IN);
+ return super.visitPartDirective(node);
+ }
+
+ @override
+ Object visitPartOfDirective(PartOfDirective node) {
+ computer._addRegion_tokenStart_tokenEnd(node.partToken, node.ofToken, HighlightType.BUILT_IN);
+ return super.visitPartOfDirective(node);
+ }
+
+ @override
+ Object visitShowCombinator(ShowCombinator node) {
+ computer._addRegion_token(node.keyword, HighlightType.BUILT_IN);
+ return super.visitShowCombinator(node);
+ }
+
+ @override
+ Object visitSimpleIdentifier(SimpleIdentifier node) {
+ computer._addIdentifierRegion(node);
+ return super.visitSimpleIdentifier(node);
+ }
+
+ @override
+ Object visitSimpleStringLiteral(SimpleStringLiteral node) {
+ computer._addRegion_node(node, HighlightType.LITERAL_STRING);
+ return super.visitSimpleStringLiteral(node);
+ }
+
+ @override
+ Object visitTypeName(TypeName node) {
+ DartType type = node.type;
+ if (type != null) {
+ if (type.isDynamic && node.name.name == "dynamic") {
+ computer._addRegion_node(node, HighlightType.TYPE_NAME_DYNAMIC);
+ return null;
+ }
+ }
+ return super.visitTypeName(node);
+ }
+}
+
+
+/**
+ * Highlighting kinds constants.
+ */
+class HighlightType {
+ static const HighlightType ANNOTATION = const HighlightType('ANNOTATION');
+ static const HighlightType BUILT_IN = const HighlightType('BUILT_IN');
+ static const HighlightType CLASS = const HighlightType('CLASS');
+ static const HighlightType COMMENT_BLOCK = const HighlightType('COMMENT_BLOCK');
+ static const HighlightType COMMENT_DOCUMENTATION = const HighlightType('COMMENT_DOCUMENTATION');
+ static const HighlightType COMMENT_END_OF_LINE = const HighlightType('COMMENT_END_OF_LINE');
+ static const HighlightType CONSTRUCTOR = const HighlightType('CONSTRUCTOR');
+ static const HighlightType DIRECTIVE = const HighlightType('DIRECTIVE');
+ static const HighlightType DYNAMIC_TYPE = const HighlightType('DYNAMIC_TYPE');
+ static const HighlightType FIELD = const HighlightType('FIELD');
+ static const HighlightType FIELD_STATIC = const HighlightType('FIELD_STATIC');
+ static const HighlightType FUNCTION_DECLARATION = const HighlightType('FUNCTION_DECLARATION');
+ static const HighlightType FUNCTION = const HighlightType('FUNCTION');
+ static const HighlightType FUNCTION_TYPE_ALIAS = const HighlightType('FUNCTION_TYPE_ALIAS');
+ static const HighlightType GETTER_DECLARATION = const HighlightType('GETTER_DECLARATION');
+ static const HighlightType KEYWORD = const HighlightType('KEYWORD');
+ static const HighlightType IDENTIFIER_DEFAULT = const HighlightType('IDENTIFIER_DEFAULT');
+ static const HighlightType IMPORT_PREFIX = const HighlightType('IMPORT_PREFIX');
+ static const HighlightType LITERAL_BOOLEAN = const HighlightType('LITERAL_BOOLEAN');
+ static const HighlightType LITERAL_DOUBLE = const HighlightType('LITERAL_DOUBLE');
+ static const HighlightType LITERAL_INTEGER = const HighlightType('LITERAL_INTEGER');
+ static const HighlightType LITERAL_LIST = const HighlightType('LITERAL_LIST');
+ static const HighlightType LITERAL_MAP = const HighlightType('LITERAL_MAP');
+ static const HighlightType LITERAL_STRING = const HighlightType('LITERAL_STRING');
+ static const HighlightType LOCAL_VARIABLE_DECLARATION = const HighlightType('LOCAL_VARIABLE_DECLARATION');
+ static const HighlightType LOCAL_VARIABLE = const HighlightType('LOCAL_VARIABLE');
+ static const HighlightType METHOD_DECLARATION = const HighlightType('METHOD_DECLARATION');
+ static const HighlightType METHOD_DECLARATION_STATIC = const HighlightType('METHOD_DECLARATION_STATIC');
+ static const HighlightType METHOD = const HighlightType('METHOD');
+ static const HighlightType METHOD_STATIC = const HighlightType('METHOD_STATIC');
+ static const HighlightType PARAMETER = const HighlightType('PARAMETER');
+ static const HighlightType SETTER_DECLARATION = const HighlightType('SETTER_DECLARATION');
+ static const HighlightType TOP_LEVEL_VARIABLE = const HighlightType('TOP_LEVEL_VARIABLE');
+ static const HighlightType TYPE_NAME_DYNAMIC = const HighlightType('TYPE_NAME_DYNAMIC');
+ static const HighlightType TYPE_PARAMETER = const HighlightType('TYPE_PARAMETER');
+
+ final String name;
+
+ @override
+ String toString() => name;
+
+ const HighlightType(this.name);
+}
diff --git a/pkg/analysis_server/lib/src/computer/computer_navigation.dart b/pkg/analysis_server/lib/src/computer/computer_navigation.dart
new file mode 100644
index 0000000..d329425
--- /dev/null
+++ b/pkg/analysis_server/lib/src/computer/computer_navigation.dart
@@ -0,0 +1,208 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library computer.navigation;
+
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/scanner.dart';
+import 'package:analyzer/src/generated/source.dart';
+
+
+/**
+ * A computer for navigation regions in a Dart [CompilationUnit].
+ */
+class DartUnitNavigationComputer {
+ final CompilationUnit _unit;
+
+ List<Map<String, Object>> _regions = <Map<String, Object>>[];
+
+ DartUnitNavigationComputer(this._unit);
+
+ /**
+ * Returns the computed navigation regions, not `null`.
+ */
+ List<Map<String, Object>> compute() {
+ _unit.accept(new _DartUnitNavigationComputerVisitor(this));
+ return new List.from(_regions);
+ }
+
+ void _addRegion(int offset, int length, Element element) {
+ Map<String, Object> target = _createTarget(element);
+ if (target == null) {
+ return;
+ }
+ _regions.add({
+ 'offset': offset,
+ 'length': length,
+ 'targets': [target]
+ });
+ }
+
+ void _addRegion_nodeStart_nodeEnd(AstNode a, AstNode b, Element element) {
+ int offset = a.offset;
+ int length = b.end - offset;
+ _addRegion(offset, length, element);
+ }
+
+ void _addRegion_nodeStart_nodeStart(AstNode a, AstNode b, Element element) {
+ int offset = a.offset;
+ int length = b.offset - offset;
+ _addRegion(offset, length, element);
+ }
+
+ void _addRegion_tokenStart_nodeEnd(Token a, AstNode b, Element element) {
+ int offset = a.offset;
+ int length = b.end - offset;
+ _addRegion(offset, length, element);
+ }
+
+ void _addRegionForNode(AstNode node, Element element) {
+ int offset = node.offset;
+ int length = node.length;
+ _addRegion(offset, length, element);
+ }
+
+ void _addRegionForToken(Token token, Element element) {
+ int offset = token.offset;
+ int length = token.length;
+ _addRegion(offset, length, element);
+ }
+
+ /**
+ * Returns the JSON for the given [Element], maybe `null` if `null` was given.
+ */
+ Map<String, Object> _createTarget(Element element) {
+ if (element == null) {
+ return null;
+ }
+ if (element is FieldFormalParameterElement) {
+ element = (element as FieldFormalParameterElement).field;
+ }
+ // prepare Source
+ Source source = element.source;
+ if (source == null) {
+ return null;
+ }
+ // prepare location
+ int offset = element.nameOffset;
+ int length = element.displayName.length;
+ if (element is CompilationUnitElement) {
+ offset = 0;
+ length = 0;
+ }
+ // return as JSON
+ return {
+ 'file': source.fullName,
+ 'offset': offset,
+ 'length': length,
+ 'elementId': element.location.encoding
+ };
+ }
+}
+
+
+
+class _DartUnitNavigationComputerVisitor extends RecursiveAstVisitor {
+ final DartUnitNavigationComputer computer;
+
+ _DartUnitNavigationComputerVisitor(this.computer);
+
+ @override
+ visitAssignmentExpression(AssignmentExpression node) {
+ computer._addRegionForToken(node.operator, node.bestElement);
+ return super.visitAssignmentExpression(node);
+ }
+
+ @override
+ visitBinaryExpression(BinaryExpression node) {
+ computer._addRegionForToken(node.operator, node.bestElement);
+ return super.visitBinaryExpression(node);
+ }
+
+ @override
+ visitConstructorDeclaration(ConstructorDeclaration node) {
+ // associate constructor with "T" or "T.name"
+ {
+ AstNode firstNode = node.returnType;
+ AstNode lastNode = node.name;
+ if (lastNode == null) {
+ lastNode = firstNode;
+ }
+ if (firstNode != null && lastNode != null) {
+ computer._addRegion_nodeStart_nodeEnd(firstNode, lastNode, node.element);
+ }
+ }
+ return super.visitConstructorDeclaration(node);
+ }
+
+ @override
+ visitExportDirective(ExportDirective node) {
+ ExportElement exportElement = node.element;
+ if (exportElement != null) {
+ Element element = exportElement.exportedLibrary;
+ computer._addRegion_tokenStart_nodeEnd(node.keyword, node.uri, element);
+ }
+ return super.visitExportDirective(node);
+ }
+
+ @override
+ visitImportDirective(ImportDirective node) {
+ ImportElement importElement = node.element;
+ if (importElement != null) {
+ Element element = importElement.importedLibrary;
+ computer._addRegion_tokenStart_nodeEnd(node.keyword, node.uri, element);
+ }
+ return super.visitImportDirective(node);
+ }
+
+ @override
+ visitIndexExpression(IndexExpression node) {
+ computer._addRegionForToken(node.rightBracket, node.bestElement);
+ return super.visitIndexExpression(node);
+ }
+
+ @override
+ visitInstanceCreationExpression(InstanceCreationExpression node) {
+ Element element = node.staticElement;
+ if (element != null && element.isSynthetic) {
+ element = element.enclosingElement;
+ }
+ computer._addRegion_nodeStart_nodeStart(node, node.argumentList, element);
+ return super.visitInstanceCreationExpression(node);
+ }
+
+ @override
+ visitPartDirective(PartDirective node) {
+ computer._addRegion_tokenStart_nodeEnd(node.keyword, node.uri, node.element);
+ return super.visitPartDirective(node);
+ }
+
+ @override
+ visitPartOfDirective(PartOfDirective node) {
+ computer._addRegion_tokenStart_nodeEnd(node.keyword, node.libraryName, node.element);
+ return super.visitPartOfDirective(node);
+ }
+
+ @override
+ visitPostfixExpression(PostfixExpression node) {
+ computer._addRegionForToken(node.operator, node.bestElement);
+ return super.visitPostfixExpression(node);
+ }
+
+ @override
+ visitPrefixExpression(PrefixExpression node) {
+ computer._addRegionForToken(node.operator, node.bestElement);
+ return super.visitPrefixExpression(node);
+ }
+
+ @override
+ visitSimpleIdentifier(SimpleIdentifier node) {
+ if (node.parent is ConstructorDeclaration) {
+ } else {
+ computer._addRegionForNode(node, node.bestElement);
+ }
+ return super.visitSimpleIdentifier(node);
+ }
+}
diff --git a/pkg/analysis_server/lib/src/computer/computer_outline.dart b/pkg/analysis_server/lib/src/computer/computer_outline.dart
new file mode 100644
index 0000000..3609759
--- /dev/null
+++ b/pkg/analysis_server/lib/src/computer/computer_outline.dart
@@ -0,0 +1,373 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library computer.outline;
+
+import 'package:analysis_server/src/constants.dart';
+import 'package:analyzer/src/generated/ast.dart';
+
+
+/**
+ * A computer for [CompilationUnit] outline.
+ */
+class DartUnitOutlineComputer {
+ final CompilationUnit _unit;
+
+ DartUnitOutlineComputer(this._unit);
+
+ /**
+ * Returns the computed outline, not `null`.
+ */
+ Map<String, Object> compute() {
+ _Outline unitOutline = _newUnitOutline();
+ for (CompilationUnitMember unitMember in _unit.declarations) {
+ if (unitMember is ClassDeclaration) {
+ ClassDeclaration classDeclaration = unitMember;
+ _Outline classOutline = _newClassOutline(unitOutline, classDeclaration);
+ for (ClassMember classMember in classDeclaration.members) {
+ if (classMember is ConstructorDeclaration) {
+ ConstructorDeclaration constructorDeclaration = classMember;
+ _newConstructorOutline(classOutline, constructorDeclaration);
+ }
+ if (classMember is FieldDeclaration) {
+ FieldDeclaration fieldDeclaration = classMember;
+ VariableDeclarationList fields = fieldDeclaration.fields;
+ if (fields != null) {
+ TypeName fieldType = fields.type;
+ String fieldTypeName = fieldType != null ? fieldType.toSource() : "";
+ for (VariableDeclaration field in fields.variables) {
+ _newVariableOutline(classOutline, fieldTypeName, _OutlineKind.FIELD, field, fieldDeclaration.isStatic);
+ }
+ }
+ }
+ if (classMember is MethodDeclaration) {
+ MethodDeclaration methodDeclaration = classMember;
+ _newMethodOutline(classOutline, methodDeclaration);
+ }
+ }
+ }
+ if (unitMember is TopLevelVariableDeclaration) {
+ TopLevelVariableDeclaration fieldDeclaration = unitMember;
+ VariableDeclarationList fields = fieldDeclaration.variables;
+ if (fields != null) {
+ TypeName fieldType = fields.type;
+ String fieldTypeName = fieldType != null ? fieldType.toSource() : "";
+ for (VariableDeclaration field in fields.variables) {
+ _newVariableOutline(unitOutline, fieldTypeName, _OutlineKind.TOP_LEVEL_VARIABLE, field, false);
+ }
+ }
+ }
+ if (unitMember is FunctionDeclaration) {
+ FunctionDeclaration functionDeclaration = unitMember;
+ _newFunctionOutline(unitOutline, functionDeclaration);
+ }
+ if (unitMember is ClassTypeAlias) {
+ ClassTypeAlias alias = unitMember;
+ _newClassTypeAlias(unitOutline, alias);
+ }
+ if (unitMember is FunctionTypeAlias) {
+ FunctionTypeAlias alias = unitMember;
+ _newFunctionTypeAliasOutline(unitOutline, alias);
+ }
+ }
+ return unitOutline.toJson();
+ }
+
+ void _addLocalFunctionOutlines(_Outline parent, FunctionBody body) {
+ body.accept(new _LocalFunctionOutlinesVisitor(this, parent));
+ }
+
+ /**
+ * Returns the [AstNode]'s source region.
+ */
+ _SourceRegion _getSourceRegion(AstNode node) {
+ int endOffset = node.end;
+ // prepare position of the node among its siblings
+ int firstOffset;
+ List<AstNode> siblings;
+ AstNode parent = node.parent;
+ // field
+ if (parent is VariableDeclarationList) {
+ VariableDeclarationList variableList = parent as VariableDeclarationList;
+ List<VariableDeclaration> variables = variableList.variables;
+ int variableIndex = variables.indexOf(node);
+ if (variableIndex == variables.length - 1) {
+ endOffset = variableList.parent.end;
+ }
+ if (variableIndex == 0) {
+ node = parent.parent;
+ parent = node.parent;
+ } else if (variableIndex >= 1) {
+ firstOffset = variables[variableIndex - 1].end;
+ return new _SourceRegion(firstOffset, endOffset - firstOffset);
+ }
+ }
+ // unit or class member
+ if (parent is CompilationUnit) {
+ firstOffset = 0;
+ siblings = (parent as CompilationUnit).declarations;
+ } else if (parent is ClassDeclaration) {
+ ClassDeclaration classDeclaration = parent as ClassDeclaration;
+ firstOffset = classDeclaration.leftBracket.end;
+ siblings = classDeclaration.members;
+ } else {
+ int offset = node.offset;
+ return new _SourceRegion(offset, endOffset - offset);
+ }
+ // first child: [endOfParent, endOfNode]
+ int index = siblings.indexOf(node);
+ if (index == 0) {
+ return new _SourceRegion(firstOffset, endOffset - firstOffset);
+ }
+ // not first child: [endOfPreviousSibling, endOfNode]
+ int prevSiblingEnd = siblings[index - 1].end;
+ return new _SourceRegion(prevSiblingEnd, endOffset - prevSiblingEnd);
+ }
+
+ _Outline _newClassOutline(_Outline parent, ClassDeclaration classDeclaration) {
+ SimpleIdentifier nameNode = classDeclaration.name;
+ String name = nameNode.name;
+ _SourceRegion sourceRegion = _getSourceRegion(classDeclaration);
+ _Outline outline = new _Outline(
+ _OutlineKind.CLASS, name,
+ nameNode.offset, nameNode.length,
+ sourceRegion.offset, sourceRegion.length,
+ classDeclaration.isAbstract, false,
+ null, null);
+ parent.children.add(outline);
+ return outline;
+ }
+
+ void _newClassTypeAlias(_Outline parent, ClassTypeAlias alias) {
+ SimpleIdentifier nameNode = alias.name;
+ String name = nameNode.name;
+ _SourceRegion sourceRegion = _getSourceRegion(alias);
+ _Outline outline = new _Outline(
+ _OutlineKind.CLASS_TYPE_ALIAS, name,
+ nameNode.offset, nameNode.length,
+ sourceRegion.offset, sourceRegion.length,
+ alias.isAbstract, false,
+ null, null);
+ parent.children.add(outline);
+ }
+
+ void _newConstructorOutline(_Outline parent, ConstructorDeclaration constructor) {
+ Identifier returnType = constructor.returnType;
+ String name = returnType.name;
+ int offset = returnType.offset;
+ int length = returnType.length;
+ SimpleIdentifier constructorNameNode = constructor.name;
+ if (constructorNameNode != null) {
+ String constructorName = constructorNameNode.name;
+ name += ".${constructorName}";
+ offset = constructorNameNode.offset;
+ length = constructorNameNode.length;
+ }
+ _SourceRegion sourceRegion = _getSourceRegion(constructor);
+ FormalParameterList parameters = constructor.parameters;
+ String parametersStr = parameters != null ? parameters.toSource() : "";
+ _Outline outline = new _Outline(
+ _OutlineKind.CONSTRUCTOR, name,
+ offset, length,
+ sourceRegion.offset, sourceRegion.length,
+ false, false,
+ parametersStr, null);
+ parent.children.add(outline);
+ _addLocalFunctionOutlines(outline, constructor.body);
+ }
+
+ void _newFunctionOutline(_Outline parent, FunctionDeclaration function) {
+ TypeName returnType = function.returnType;
+ SimpleIdentifier nameNode = function.name;
+ String name = nameNode.name;
+ FunctionExpression functionExpression = function.functionExpression;
+ FormalParameterList parameters = functionExpression.parameters;
+ _OutlineKind kind;
+ if (function.isGetter) {
+ kind = _OutlineKind.GETTER;
+ } else if (function.isSetter) {
+ kind = _OutlineKind.SETTER;
+ } else {
+ kind = _OutlineKind.FUNCTION;
+ }
+ _SourceRegion sourceRegion = _getSourceRegion(function);
+ String parametersStr = parameters != null ? parameters.toSource() : "";
+ String returnTypeStr = returnType != null ? returnType.toSource() : "";
+ _Outline outline = new _Outline(
+ kind, name,
+ nameNode.offset, nameNode.length,
+ sourceRegion.offset, sourceRegion.length,
+ false, false,
+ parametersStr, returnTypeStr);
+ parent.children.add(outline);
+ _addLocalFunctionOutlines(outline, functionExpression.body);
+ }
+
+ void _newFunctionTypeAliasOutline(_Outline parent, FunctionTypeAlias alias) {
+ TypeName returnType = alias.returnType;
+ SimpleIdentifier nameNode = alias.name;
+ String name = nameNode.name;
+ _SourceRegion sourceRegion = _getSourceRegion(alias);
+ FormalParameterList parameters = alias.parameters;
+ String parametersStr = parameters != null ? parameters.toSource() : "";
+ String returnTypeStr = returnType != null ? returnType.toSource() : "";
+ _Outline outline = new _Outline(
+ _OutlineKind.FUNCTION_TYPE_ALIAS, name,
+ nameNode.offset, nameNode.length,
+ sourceRegion.offset, sourceRegion.length,
+ false, false,
+ parametersStr, returnTypeStr);
+ parent.children.add(outline);
+ }
+
+ void _newMethodOutline(_Outline parent, MethodDeclaration method) {
+ TypeName returnType = method.returnType;
+ SimpleIdentifier nameNode = method.name;
+ String name = nameNode.name;
+ FormalParameterList parameters = method.parameters;
+ _OutlineKind kind;
+ if (method.isGetter) {
+ kind = _OutlineKind.GETTER;
+ } else if (method.isSetter) {
+ kind = _OutlineKind.SETTER;
+ } else {
+ kind = _OutlineKind.METHOD;
+ }
+ _SourceRegion sourceRegion = _getSourceRegion(method);
+ String parametersStr = parameters != null ? parameters.toSource() : "";
+ String returnTypeStr = returnType != null ? returnType.toSource() : "";
+ _Outline outline = new _Outline(
+ kind, name,
+ nameNode.offset, nameNode.length,
+ sourceRegion.offset, sourceRegion.length,
+ method.isAbstract, method.isStatic,
+ parametersStr, returnTypeStr);
+ parent.children.add(outline);
+ _addLocalFunctionOutlines(outline, method.body);
+ }
+
+ _Outline _newUnitOutline() {
+ return new _Outline(
+ _OutlineKind.COMPILATION_UNIT, "<unit>",
+ _unit.offset, _unit.length,
+ _unit.offset, _unit.length,
+ false, false,
+ null, null);
+ }
+
+ void _newVariableOutline(_Outline parent, String typeName, _OutlineKind kind, VariableDeclaration variable, bool isStatic) {
+ SimpleIdentifier nameNode = variable.name;
+ String name = nameNode.name;
+ _SourceRegion sourceRegion = _getSourceRegion(variable);
+ _Outline outline = new _Outline(
+ kind, name,
+ nameNode.offset, nameNode.length,
+ sourceRegion.offset, sourceRegion.length,
+ false, isStatic,
+ null, typeName);
+ parent.children.add(outline);
+ }
+}
+
+
+/**
+ * A visitor for building local function outlines.
+ */
+class _LocalFunctionOutlinesVisitor extends RecursiveAstVisitor {
+ final DartUnitOutlineComputer outlineComputer;
+ final _Outline parent;
+
+ _LocalFunctionOutlinesVisitor(this.outlineComputer, this.parent);
+
+ @override
+ visitFunctionDeclaration(FunctionDeclaration node) {
+ outlineComputer._newFunctionOutline(parent, node);
+ }
+}
+
+
+
+/**
+ * A range of characters.
+ */
+class _SourceRegion {
+ final int offset;
+ final int length;
+ _SourceRegion(this.offset, this.length);
+}
+
+
+/**
+ * Element outline kinds.
+ */
+class _OutlineKind {
+ static const _OutlineKind CLASS = const _OutlineKind('CLASS');
+ static const _OutlineKind CLASS_TYPE_ALIAS = const _OutlineKind('CLASS_TYPE_ALIAS');
+ static const _OutlineKind COMPILATION_UNIT = const _OutlineKind('COMPILATION_UNIT');
+ static const _OutlineKind CONSTRUCTOR = const _OutlineKind('CONSTRUCTOR');
+ static const _OutlineKind GETTER = const _OutlineKind('GETTER');
+ static const _OutlineKind FIELD = const _OutlineKind('FIELD');
+ static const _OutlineKind FUNCTION = const _OutlineKind('FUNCTION');
+ static const _OutlineKind FUNCTION_TYPE_ALIAS = const _OutlineKind('FUNCTION_TYPE_ALIAS');
+ static const _OutlineKind LIBRARY = const _OutlineKind('LIBRARY');
+ static const _OutlineKind METHOD = const _OutlineKind('METHOD');
+ static const _OutlineKind SETTER = const _OutlineKind('SETTER');
+ static const _OutlineKind TOP_LEVEL_VARIABLE = const _OutlineKind('TOP_LEVEL_VARIABLE');
+ static const _OutlineKind UNKNOWN = const _OutlineKind('UNKNOWN');
+ static const _OutlineKind UNIT_TEST_CASE = const _OutlineKind('UNIT_TEST_CASE');
+ static const _OutlineKind UNIT_TEST_GROUP = const _OutlineKind('UNIT_TEST_GROUP');
+
+ final String name;
+
+ const _OutlineKind(this.name);
+}
+
+
+/**
+ * An element outline.
+ */
+class _Outline {
+ static const List<_Outline> EMPTY_ARRAY = const <_Outline>[];
+
+ final _OutlineKind kind;
+ final String name;
+ final int nameOffset;
+ final int nameLength;
+ final int elementOffset;
+ final int elementLength;
+ final bool isAbstract;
+ final bool isStatic;
+ final String parameters;
+ final String returnType;
+ final List<_Outline> children = <_Outline>[];
+
+ _Outline(this.kind, this.name,
+ this.nameOffset, this.nameLength,
+ this.elementOffset, this.elementLength,
+ this.isAbstract, this.isStatic,
+ this.parameters, this.returnType);
+
+ Map<String, Object> toJson() {
+ Map<String, Object> json = {
+ KIND: kind.name,
+ NAME: name,
+ NAME_OFFSET: nameOffset,
+ NAME_LENGTH: nameLength,
+ ELEMENT_OFFSET: elementOffset,
+ ELEMENT_LENGTH: elementLength,
+ IS_ABSTRACT: isAbstract,
+ IS_STATIC: isStatic
+ };
+ if (parameters != null) {
+ json[PARAMETERS] = parameters;
+ }
+ if (returnType != null) {
+ json[RETURN_TYPE] = returnType;
+ }
+ if (children.isNotEmpty) {
+ json[CHILDREN] = children.map((child) => child.toJson()).toList();
+ }
+ return json;
+ }
+}
diff --git a/pkg/analysis_server/test/analysis_abstract.dart b/pkg/analysis_server/test/analysis_abstract.dart
new file mode 100644
index 0000000..2ea2e1d
--- /dev/null
+++ b/pkg/analysis_server/test/analysis_abstract.dart
@@ -0,0 +1,280 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library test.domain.analysis.abstract;
+
+import 'dart:async';
+
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/domain_analysis.dart';
+import 'package:analysis_server/src/constants.dart';
+import 'package:analysis_server/src/protocol.dart';
+import 'package:analysis_server/src/resource.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:unittest/unittest.dart';
+
+import 'mocks.dart';
+
+
+/**
+ * An abstract base for all 'analysis' domain tests.
+ */
+class AbstractAnalysisTest {
+ MockServerChannel serverChannel;
+ MemoryResourceProvider resourceProvider;
+ AnalysisServer server;
+ AnalysisDomainHandler handler;
+
+ Map<String, List<String>> analysisSubscriptions = {};
+
+ String projectPath = '/project';
+ String testFile = '/project/bin/test.dart';
+ String testCode;
+
+// Map<String, List<AnalysisError>> filesErrors = {};
+// Map<String, List<Map<String, Object>>> filesHighlights = {};
+// Map<String, List<Map<String, Object>>> filesNavigation = {};
+
+
+ AbstractAnalysisTest() {
+ }
+
+ void setUp() {
+ serverChannel = new MockServerChannel();
+ resourceProvider = new MemoryResourceProvider();
+ server = new AnalysisServer(serverChannel, resourceProvider);
+ handler = new AnalysisDomainHandler(server);
+ // listen for notifications
+ Stream<Notification> notificationStream = serverChannel.notificationController.stream;
+ notificationStream.listen((Notification notification) {
+ processNotification(notification);
+ });
+ // create an empty project
+ _createEmptyProject();
+ }
+
+ void addAnalysisSubscription(AnalysisService service, String file) {
+ // add file to subscription
+ var files = analysisSubscriptions[service.name];
+ if (files == null) {
+ files = <String>[];
+ analysisSubscriptions[service.name] = files;
+ }
+ files.add(file);
+ // set subscriptions
+ Request request = new Request('0', METHOD_SET_ANALYSIS_SUBSCRIPTIONS);
+ request.setParameter(SUBSCRIPTIONS, analysisSubscriptions);
+ handleSuccessfulRequest(request);
+ }
+
+ void tearDown() {
+ server.done();
+ handler = null;
+ server = null;
+ resourceProvider = null;
+ serverChannel = null;
+ }
+
+ void processNotification(Notification notification) {
+// if (notification.event == NOTIFICATION_ERRORS) {
+// String file = notification.getParameter(FILE);
+// List<Map<String, Object>> errorMaps = notification.getParameter(ERRORS);
+// filesErrors[file] = errorMaps.map(jsonToAnalysisError).toList();
+// }
+// if (notification.event == NOTIFICATION_HIGHLIGHTS) {
+// String file = notification.getParameter(FILE);
+// filesHighlights[file] = notification.getParameter(REGIONS);
+// }
+// if (notification.event == NOTIFICATION_NAVIGATION) {
+// String file = notification.getParameter(FILE);
+// filesNavigation[file] = notification.getParameter(REGIONS);
+// }
+ }
+
+ /**
+ * Returns a [Future] that completes when the [AnalysisServer] finishes
+ * all its scheduled tasks.
+ */
+ Future waitForTasksFinished() {
+ return waitForServerOperationsPerformed(server);
+ }
+
+ /**
+ * Returns the offset of [search] in [testCode].
+ * Fails if not found.
+ */
+ int findFileOffset(String path, String search) {
+ File file = resourceProvider.getResource(path) as File;
+ String code = file.createSource(UriKind.FILE_URI).contents.data;
+ int offset = code.indexOf(search);
+ expect(offset, isNot(-1), reason: '"$search" in\n$code');
+ return offset;
+ }
+
+ /**
+ * Returns the offset of [search] in [testCode].
+ * Fails if not found.
+ */
+ int findOffset(String search) {
+ int offset = testCode.indexOf(search);
+ expect(offset, isNot(-1));
+ return offset;
+ }
+
+// /**
+// * Returns [AnalysisError]s recorded for the given [file].
+// * May be empty, but not `null`.
+// */
+// List<AnalysisError> getErrors(String file) {
+// List<AnalysisError> errors = filesErrors[file];
+// if (errors != null) {
+// return errors;
+// }
+// return <AnalysisError>[];
+// }
+//
+// /**
+// * Returns highlights recorded for the given [file].
+// * May be empty, but not `null`.
+// */
+// List<Map<String, Object>> getHighlights(String file) {
+// List<Map<String, Object>> highlights = filesHighlights[file];
+// if (highlights != null) {
+// return highlights;
+// }
+// return [];
+// }
+//
+// /**
+// * Returns navigation regions recorded for the given [file].
+// * May be empty, but not `null`.
+// */
+// List<Map<String, Object>> getNavigation(String file) {
+// List<Map<String, Object>> navigation = filesNavigation[file];
+// if (navigation != null) {
+// return navigation;
+// }
+// return [];
+// }
+//
+// /**
+// * Returns [AnalysisError]s recorded for the [testFile].
+// * May be empty, but not `null`.
+// */
+// List<AnalysisError> getTestErrors() {
+// return getErrors(testFile);
+// }
+//
+// /**
+// * Returns highlights recorded for the given [testFile].
+// * May be empty, but not `null`.
+// */
+// List<Map<String, Object>> getTestHighlights() {
+// return getHighlights(testFile);
+// }
+//
+// /**
+// * Returns navigation information recorded for the given [testFile].
+// * May be empty, but not `null`.
+// */
+// List<Map<String, Object>> getTestNavigation() {
+// return getNavigation(testFile);
+// }
+
+ /**
+ * Creates an empty project `/project/`.
+ */
+ void _createEmptyProject() {
+ resourceProvider.newFolder(projectPath);
+ Request request = new Request('0', METHOD_SET_ANALYSIS_ROOTS);
+ request.setParameter(INCLUDED, [projectPath]);
+ request.setParameter(EXCLUDED, []);
+ handleSuccessfulRequest(request);
+ }
+
+// /**
+// * Creates a project with a single Dart file `/project/bin/test.dart` with
+// * the given [code].
+// */
+// void createSingleFileProject(code) {
+// this.testCode = _getCodeString(code);
+// resourceProvider.newFolder('/project');
+// resourceProvider.newFile(testFile, testCode);
+// Request request = new Request('0', METHOD_SET_ANALYSIS_ROOTS);
+// request.setParameter(INCLUDED, ['/project']);
+// request.setParameter(EXCLUDED, []);
+// handleSuccessfulRequest(request);
+// }
+
+ String addFile(String path, String content) {
+ resourceProvider.newFile(path, content);
+ return path;
+ }
+
+ String addTestFile(String content) {
+ addFile(testFile, content);
+ this.testCode = content;
+ return testFile;
+ }
+
+ /**
+ * Validates that the given [request] is handled successfully.
+ */
+ void handleSuccessfulRequest(Request request) {
+ Response response = handler.handleRequest(request);
+ expect(response, isResponseSuccess('0'));
+ }
+
+ static String _getCodeString(code) {
+ if (code is List<String>) {
+ code = code.join('\n');
+ }
+ return code as String;
+ }
+}
+
+
+
+class AnalysisError {
+ final String file;
+ final String errorCode;
+ final int offset;
+ final int length;
+ final String message;
+ final String correction;
+ AnalysisError(this.file, this.errorCode, this.offset, this.length,
+ this.message, this.correction);
+
+ @override
+ String toString() {
+ return 'NotificationError(file=$file; errorCode=$errorCode; '
+ 'offset=$offset; length=$length; message=$message)';
+ }
+}
+
+
+AnalysisError _jsonToAnalysisError(Map<String, Object> json) {
+ return new AnalysisError(
+ json['file'],
+ json['errorCode'],
+ json['offset'],
+ json['length'],
+ json['message'],
+ json['correction']);
+}
+
+
+int findIdentifierLength(String search) {
+ int length = 0;
+ while (length < search.length) {
+ int c = search.codeUnitAt(length);
+ if (!(c >= 'a'.codeUnitAt(0) && c <= 'z'.codeUnitAt(0) ||
+ c >= 'A'.codeUnitAt(0) && c <= 'Z'.codeUnitAt(0) ||
+ c >= '0'.codeUnitAt(0) && c <= '9'.codeUnitAt(0))) {
+ break;
+ }
+ length++;
+ }
+ return length;
+}
diff --git a/pkg/analysis_server/test/analysis_notification_outline_test.dart b/pkg/analysis_server/test/analysis_notification_outline_test.dart
new file mode 100644
index 0000000..49011ef
--- /dev/null
+++ b/pkg/analysis_server/test/analysis_notification_outline_test.dart
@@ -0,0 +1,681 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library test.domain.analysis.notification.outline;
+
+import 'dart:async';
+import 'dart:mirrors';
+
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/constants.dart';
+import 'package:analysis_server/src/protocol.dart';
+import 'package:unittest/unittest.dart';
+
+import 'analysis_abstract.dart';
+import 'reflective_tests.dart';
+
+
+main() {
+ group('notification.outline', () {
+ runReflectiveTests(AnalysisNotificationOutlineTest);
+ });
+}
+
+
+@ReflectiveTestCase()
+class AnalysisNotificationOutlineTest extends AbstractAnalysisTest {
+ _Outline outline;
+
+ void processNotification(Notification notification) {
+ if (notification.event == NOTIFICATION_OUTLINE) {
+ String file = notification.getParameter(FILE);
+ if (file == testFile) {
+ Map<String, Object> json = notification.getParameter(OUTLINE);
+ outline = new _Outline.fromJson(json);
+ }
+ }
+ }
+
+ Future prepareOutline(then()) {
+ addAnalysisSubscription(AnalysisService.OUTLINE, testFile);
+ return waitForTasksFinished().then((_) {
+ then();
+ });
+ }
+
+ test_class() {
+ addTestFile('''
+class A {
+ int fa, fb;
+ String fc;
+ A(int i, String s);
+ A.name(num p);
+ A._privateName(num p);
+ static String ma(int pa) => null;
+ _mb(int pb);
+ String get propA => null;
+ set propB(int v) {}
+}
+class B {
+ B(int p);
+}");
+''');
+ return prepareOutline(() {
+ _Outline unitOutline = outline;
+ List<_Outline> topOutlines = unitOutline.children;
+ expect(topOutlines, hasLength(2));
+ // A
+ {
+ _Outline outline_A = topOutlines[0];
+ expect(outline_A.kind, _OutlineKind.CLASS);
+ expect(outline_A.name, "A");
+ expect(outline_A.nameOffset, testCode.indexOf("A {"));
+ expect(outline_A.nameLength, 1);
+ expect(outline_A.parameters, null);
+ expect(outline_A.returnType, null);
+ // A children
+ List<_Outline> outlines_A = outline_A.children;
+ expect(outlines_A, hasLength(10));
+ {
+ _Outline outline = outlines_A[0];
+ expect(outline.kind, _OutlineKind.FIELD);
+ expect(outline.name, "fa");
+ expect(outline.parameters, isNull);
+ expect(outline.returnType, "int");
+ }
+ {
+ _Outline outline = outlines_A[1];
+ expect(outline.kind, _OutlineKind.FIELD);
+ expect(outline.name, "fb");
+ expect(outline.parameters, isNull);
+ expect(outline.returnType, "int");
+ }
+ {
+ _Outline outline = outlines_A[2];
+ expect(outline.kind, _OutlineKind.FIELD);
+ expect(outline.name, "fc");
+ expect(outline.parameters, isNull);
+ expect(outline.returnType, "String");
+ }
+ {
+ _Outline outline = outlines_A[3];
+ expect(outline.kind, _OutlineKind.CONSTRUCTOR);
+ expect(outline.name, "A");
+ expect(outline.nameOffset, testCode.indexOf("A(int i, String s);"));
+ expect(outline.nameLength, "A".length);
+ expect(outline.parameters, "(int i, String s)");
+ expect(outline.returnType, isNull);
+ expect(outline.isAbstract, isFalse);
+ expect(outline.isStatic, isFalse);
+ }
+ {
+ _Outline outline = outlines_A[4];
+ expect(outline.kind, _OutlineKind.CONSTRUCTOR);
+ expect(outline.name, "A.name");
+ expect(outline.nameOffset, testCode.indexOf("name(num p);"));
+ expect(outline.nameLength, "name".length);
+ expect(outline.parameters, "(num p)");
+ expect(outline.returnType, isNull);
+ expect(outline.isAbstract, isFalse);
+ expect(outline.isStatic, isFalse);
+ }
+ {
+ _Outline outline = outlines_A[5];
+ expect(outline.kind, _OutlineKind.CONSTRUCTOR);
+ expect(outline.name, "A._privateName");
+ expect(outline.nameOffset, testCode.indexOf("_privateName(num p);"));
+ expect(outline.nameLength, "_privateName".length);
+ expect(outline.parameters, "(num p)");
+ expect(outline.returnType, isNull);
+ expect(outline.isAbstract, isFalse);
+ expect(outline.isStatic, isFalse);
+ }
+ {
+ _Outline outline = outlines_A[6];
+ expect(outline.kind, _OutlineKind.METHOD);
+ expect(outline.name, "ma");
+ expect(outline.nameOffset, testCode.indexOf("ma(int pa) => null;"));
+ expect(outline.nameLength, "ma".length);
+ expect(outline.parameters, "(int pa)");
+ expect(outline.returnType, "String");
+ expect(outline.isAbstract, isFalse);
+ expect(outline.isStatic, isTrue);
+ }
+ {
+ _Outline outline = outlines_A[7];
+ expect(outline.kind, _OutlineKind.METHOD);
+ expect(outline.name, "_mb");
+ expect(outline.nameOffset, testCode.indexOf("_mb(int pb);"));
+ expect(outline.nameLength, "_mb".length);
+ expect(outline.parameters, "(int pb)");
+ expect(outline.returnType, "");
+ expect(outline.isAbstract, isTrue);
+ expect(outline.isStatic, isFalse);
+ }
+ {
+ _Outline outline = outlines_A[8];
+ expect(outline.kind, _OutlineKind.GETTER);
+ expect(outline.name, "propA");
+ expect(outline.nameOffset, testCode.indexOf("propA => null;"));
+ expect(outline.nameLength, "propA".length);
+ expect(outline.parameters, "");
+ expect(outline.returnType, "String");
+ }
+ {
+ _Outline outline = outlines_A[9];
+ expect(outline.kind, _OutlineKind.SETTER);
+ expect(outline.name, "propB");
+ expect(outline.nameOffset, testCode.indexOf("propB(int v) {}"));
+ expect(outline.nameLength, "propB".length);
+ expect(outline.parameters, "(int v)");
+ expect(outline.returnType, "");
+ }
+ }
+ // B
+ {
+ _Outline outline_B = topOutlines[1];
+ expect(outline_B.kind, _OutlineKind.CLASS);
+ expect(outline_B.name, "B");
+ expect(outline_B.nameOffset, testCode.indexOf("B {"));
+ expect(outline_B.nameLength, 1);
+ expect(outline_B.parameters, null);
+ expect(outline_B.returnType, null);
+ // B children
+ List<_Outline> outlines_B = outline_B.children;
+ expect(outlines_B, hasLength(1));
+ {
+ _Outline outline = outlines_B[0];
+ expect(outline.kind, _OutlineKind.CONSTRUCTOR);
+ expect(outline.name, "B");
+ expect(outline.nameOffset, testCode.indexOf("B(int p);"));
+ expect(outline.nameLength, "B".length);
+ expect(outline.parameters, "(int p)");
+ expect(outline.returnType, isNull);
+ }
+ }
+ });
+ }
+
+ test_sourceRange_inClass() {
+ addTestFile('''
+class A { // leftA
+ int methodA() {} // endA
+ int methodB() {} // endB
+}
+''');
+ return prepareOutline(() {
+ _Outline unitOutline = outline;
+ List<_Outline> outlines = unitOutline.children[0].children;
+ expect(outlines, hasLength(2));
+ // methodA
+ {
+ _Outline outline = outlines[0];
+ expect(outline.kind, _OutlineKind.METHOD);
+ expect(outline.name, "methodA");
+ {
+ int offset = testCode.indexOf(" // leftA");
+ int end = testCode.indexOf(" // endA");
+ expect(outline.elementOffset, offset);
+ expect(outline.elementLength, end - offset);
+ }
+ }
+ // methodB
+ {
+ _Outline outline = outlines[1];
+ expect(outline.kind, _OutlineKind.METHOD);
+ expect(outline.name, "methodB");
+ {
+ int offset = testCode.indexOf(" // endA");
+ int end = testCode.indexOf(" // endB");
+ expect(outline.elementOffset, offset);
+ expect(outline.elementLength, end - offset);
+ }
+ }
+ });
+ }
+
+ test_sourceRange_inClass_inVariableList() {
+ addTestFile('''
+class A { // leftA
+ int fieldA, fieldB, fieldC; // marker
+ int fieldD; // marker2
+}
+''');
+ return prepareOutline(() {
+ _Outline unitOutline = outline;
+ List<_Outline> outlines = unitOutline.children[0].children;
+ expect(outlines, hasLength(4));
+ // fieldA
+ {
+ _Outline outline = outlines[0];
+ expect(outline.kind, _OutlineKind.FIELD);
+ expect(outline.name, "fieldA");
+ {
+ int offset = testCode.indexOf(" // leftA");
+ int end = testCode.indexOf(", fieldB");
+ expect(outline.elementOffset, offset);
+ expect(outline.elementLength, end - offset);
+ }
+ }
+ // fieldB
+ {
+ _Outline outline = outlines[1];
+ expect(outline.kind, _OutlineKind.FIELD);
+ expect(outline.name, "fieldB");
+ {
+ int offset = testCode.indexOf(", fieldB");
+ int end = testCode.indexOf(", fieldC");
+ expect(outline.elementOffset, offset);
+ expect(outline.elementLength, end - offset);
+ }
+ }
+ // fieldC
+ {
+ _Outline outline = outlines[2];
+ expect(outline.kind, _OutlineKind.FIELD);
+ expect(outline.name, "fieldC");
+ {
+ int offset = testCode.indexOf(", fieldC");
+ int end = testCode.indexOf(" // marker");
+ expect(outline.elementOffset, offset);
+ expect(outline.elementLength, end - offset);
+ }
+ }
+ // fieldD
+ {
+ _Outline outline = outlines[3];
+ expect(outline.kind, _OutlineKind.FIELD);
+ expect(outline.name, "fieldD");
+ {
+ int offset = testCode.indexOf(" // marker");
+ int end = testCode.indexOf(" // marker2");
+ expect(outline.elementOffset, offset);
+ expect(outline.elementLength, end - offset);
+ }
+ }
+ });
+ }
+
+ test_sourceRange_inUnit() {
+ addTestFile('''
+class A {
+} // endA
+class B {
+} // endB
+''');
+ return prepareOutline(() {
+ _Outline unitOutline = outline;
+ List<_Outline> topOutlines = unitOutline.children;
+ expect(topOutlines, hasLength(2));
+ // A
+ {
+ _Outline outline = topOutlines[0];
+ expect(outline.kind, _OutlineKind.CLASS);
+ expect(outline.name, "A");
+ {
+ int offset = 0;
+ int end = testCode.indexOf(" // endA");
+ expect(outline.elementOffset, offset);
+ expect(outline.elementLength, end - offset);
+ }
+ }
+ // B
+ {
+ _Outline outline = topOutlines[1];
+ expect(outline.kind, _OutlineKind.CLASS);
+ expect(outline.name, "B");
+ {
+ int offset = testCode.indexOf(" // endA");
+ int end = testCode.indexOf(" // endB");
+ expect(outline.elementOffset, offset);
+ expect(outline.elementLength, end - offset);
+ }
+ }
+ });
+ }
+
+ test_sourceRange_inUnit_inVariableList() {
+ addTestFile('''
+int fieldA, fieldB, fieldC; // marker
+int fieldD; // marker2
+''');
+ return prepareOutline(() {
+ _Outline unitOutline = outline;
+ List<_Outline> outlines = unitOutline.children;
+ expect(outlines, hasLength(4));
+ // fieldA
+ {
+ _Outline outline = outlines[0];
+ expect(outline.kind, _OutlineKind.TOP_LEVEL_VARIABLE);
+ expect(outline.name, "fieldA");
+ {
+ int offset = 0;
+ int end = testCode.indexOf(", fieldB");
+ expect(outline.elementOffset, offset);
+ expect(outline.elementLength, end - offset);
+ }
+ }
+ // fieldB
+ {
+ _Outline outline = outlines[1];
+ expect(outline.kind, _OutlineKind.TOP_LEVEL_VARIABLE);
+ expect(outline.name, "fieldB");
+ {
+ int offset = testCode.indexOf(", fieldB");
+ int end = testCode.indexOf(", fieldC");
+ expect(outline.elementOffset, offset);
+ expect(outline.elementLength, end - offset);
+ }
+ }
+ // fieldC
+ {
+ _Outline outline = outlines[2];
+ expect(outline.kind, _OutlineKind.TOP_LEVEL_VARIABLE);
+ expect(outline.name, "fieldC");
+ {
+ int offset = testCode.indexOf(", fieldC");
+ int end = testCode.indexOf(" // marker");
+ expect(outline.elementOffset, offset);
+ expect(outline.elementLength, end - offset);
+ }
+ }
+ // fieldD
+ {
+ _Outline outline = outlines[3];
+ expect(outline.kind, _OutlineKind.TOP_LEVEL_VARIABLE);
+ expect(outline.name, "fieldD");
+ {
+ int offset = testCode.indexOf(" // marker");
+ int end = testCode.indexOf(" // marker2");
+ expect(outline.elementOffset, offset);
+ expect(outline.elementLength, end - offset);
+ }
+ }
+ });
+ }
+
+ test_localFunctions() {
+ addTestFile('''
+class A {
+ A() {
+ int local_A() {}
+ }
+ m() {
+ local_m() {}
+ }
+}
+f() {
+ local_f1(int i) {}
+ local_f2(String s) {
+ local_f21(int p) {}
+ }
+}
+''');
+ return prepareOutline(() {
+ _Outline unitOutline = outline;
+ List<_Outline> topOutlines = unitOutline.children;
+ expect(topOutlines, hasLength(2));
+ // A
+ {
+ _Outline outline_A = topOutlines[0];
+ expect(outline_A.kind, _OutlineKind.CLASS);
+ expect(outline_A.name, "A");
+ expect(outline_A.nameOffset, testCode.indexOf("A {"));
+ expect(outline_A.nameLength, "A".length);
+ expect(outline_A.parameters, null);
+ expect(outline_A.returnType, null);
+ // A children
+ List<_Outline> outlines_A = outline_A.children;
+ expect(outlines_A, hasLength(2));
+ {
+ _Outline constructorOutline = outlines_A[0];
+ expect(constructorOutline.kind, _OutlineKind.CONSTRUCTOR);
+ expect(constructorOutline.name, "A");
+ expect(constructorOutline.nameOffset, testCode.indexOf("A() {"));
+ expect(constructorOutline.nameLength, "A".length);
+ expect(constructorOutline.parameters, "()");
+ expect(constructorOutline.returnType, isNull);
+ // local function
+ List<_Outline> outlines_constructor = constructorOutline.children;
+ expect(outlines_constructor, hasLength(1));
+ {
+ _Outline outline = outlines_constructor[0];
+ expect(outline.kind, _OutlineKind.FUNCTION);
+ expect(outline.name, "local_A");
+ expect(outline.nameOffset, testCode.indexOf("local_A() {}"));
+ expect(outline.nameLength, "local_A".length);
+ expect(outline.parameters, "()");
+ expect(outline.returnType, "int");
+ }
+ }
+ {
+ _Outline outline_m = outlines_A[1];
+ expect(outline_m.kind, _OutlineKind.METHOD);
+ expect(outline_m.name, "m");
+ expect(outline_m.nameOffset, testCode.indexOf("m() {"));
+ expect(outline_m.nameLength, "m".length);
+ expect(outline_m.parameters, "()");
+ expect(outline_m.returnType, "");
+ // local function
+ List<_Outline> methodChildren = outline_m.children;
+ expect(methodChildren, hasLength(1));
+ {
+ _Outline outline = methodChildren[0];
+ expect(outline.kind, _OutlineKind.FUNCTION);
+ expect(outline.name, "local_m");
+ expect(outline.nameOffset, testCode.indexOf("local_m() {}"));
+ expect(outline.nameLength, "local_m".length);
+ expect(outline.parameters, "()");
+ expect(outline.returnType, "");
+ }
+ }
+ }
+ // f()
+ {
+ _Outline outline_f = topOutlines[1];
+ expect(outline_f.kind, _OutlineKind.FUNCTION);
+ expect(outline_f.name, "f");
+ expect(outline_f.nameOffset, testCode.indexOf("f() {"));
+ expect(outline_f.nameLength, "f".length);
+ expect(outline_f.parameters, "()");
+ expect(outline_f.returnType, "");
+ // f() children
+ List<_Outline> outlines_f = outline_f.children;
+ expect(outlines_f, hasLength(2));
+ {
+ _Outline outline_f1 = outlines_f[0];
+ expect(outline_f1.kind, _OutlineKind.FUNCTION);
+ expect(outline_f1.name, "local_f1");
+ expect(outline_f1.nameOffset, testCode.indexOf("local_f1(int i) {}"));
+ expect(outline_f1.nameLength, "local_f1".length);
+ expect(outline_f1.parameters, "(int i)");
+ expect(outline_f1.returnType, "");
+ }
+ {
+ _Outline outline_f2 = outlines_f[1];
+ expect(outline_f2.kind, _OutlineKind.FUNCTION);
+ expect(outline_f2.name, "local_f2");
+ expect(outline_f2.nameOffset, testCode.indexOf("local_f2(String s) {"));
+ expect(outline_f2.nameLength, "local_f2".length);
+ expect(outline_f2.parameters, "(String s)");
+ expect(outline_f2.returnType, "");
+ // local_f2() local function
+ List<_Outline> outlines_f2 = outline_f2.children;
+ expect(outlines_f2, hasLength(1));
+ {
+ _Outline outline_f21 = outlines_f2[0];
+ expect(outline_f21.kind, _OutlineKind.FUNCTION);
+ expect(outline_f21.name, "local_f21");
+ expect(outline_f21.nameOffset, testCode.indexOf("local_f21(int p) {"));
+ expect(outline_f21.nameLength, "local_f21".length);
+ expect(outline_f21.parameters, "(int p)");
+ expect(outline_f21.returnType, "");
+ }
+ }
+ }
+ });
+ }
+
+ test_topLevel() {
+ addTestFile('''
+typedef String FTA(int i, String s);
+typedef FTB(int p);
+class A {}
+class B {}
+class CTA = A with B;
+String fA(int i, String s) => null;
+fB(int p) => null;
+String get propA => null;
+set propB(int v) {}
+''');
+ return prepareOutline(() {
+ _Outline unitOutline = outline;
+ List<_Outline> topOutlines = unitOutline.children;
+ expect(topOutlines, hasLength(9));
+ // FTA
+ {
+ _Outline outline = topOutlines[0];
+ expect(outline.kind, _OutlineKind.FUNCTION_TYPE_ALIAS);
+ expect(outline.name, "FTA");
+ expect(outline.nameOffset, testCode.indexOf("FTA("));
+ expect(outline.nameLength, "FTA".length);
+ expect(outline.parameters, "(int i, String s)");
+ expect(outline.returnType, "String");
+ }
+ // FTB
+ {
+ _Outline outline = topOutlines[1];
+ expect(outline.kind, _OutlineKind.FUNCTION_TYPE_ALIAS);
+ expect(outline.name, "FTB");
+ expect(outline.nameOffset, testCode.indexOf("FTB("));
+ expect(outline.nameLength, "FTB".length);
+ expect(outline.parameters, "(int p)");
+ expect(outline.returnType, "");
+ }
+ // CTA
+ {
+ _Outline outline = topOutlines[4];
+ expect(outline.kind, _OutlineKind.CLASS_TYPE_ALIAS);
+ expect(outline.name, "CTA");
+ expect(outline.nameOffset, testCode.indexOf("CTA ="));
+ expect(outline.nameLength, "CTA".length);
+ expect(outline.parameters, isNull);
+ expect(outline.returnType, isNull);
+ }
+ // fA
+ {
+ _Outline outline = topOutlines[5];
+ expect(outline.kind, _OutlineKind.FUNCTION);
+ expect(outline.name, "fA");
+ expect(outline.nameOffset, testCode.indexOf("fA("));
+ expect(outline.nameLength, "fA".length);
+ expect(outline.parameters, "(int i, String s)");
+ expect(outline.returnType, "String");
+ }
+ // fB
+ {
+ _Outline outline = topOutlines[6];
+ expect(outline.kind, _OutlineKind.FUNCTION);
+ expect(outline.name, "fB");
+ expect(outline.nameOffset, testCode.indexOf("fB("));
+ expect(outline.nameLength, "fB".length);
+ expect(outline.parameters, "(int p)");
+ expect(outline.returnType, "");
+ }
+ // propA
+ {
+ _Outline outline = topOutlines[7];
+ expect(outline.kind, _OutlineKind.GETTER);
+ expect(outline.name, "propA");
+ expect(outline.nameOffset, testCode.indexOf("propA => null;"));
+ expect(outline.nameLength, "propA".length);
+ expect(outline.parameters, "");
+ expect(outline.returnType, "String");
+ }
+ // propB
+ {
+ _Outline outline = topOutlines[8];
+ expect(outline.kind, _OutlineKind.SETTER);
+ expect(outline.name, "propB");
+ expect(outline.nameOffset, testCode.indexOf("propB(int v) {}"));
+ expect(outline.nameLength, "propB".length);
+ expect(outline.parameters, "(int v)");
+ expect(outline.returnType, "");
+ }
+ });
+ }
+}
+
+
+/**
+ * Element outline kinds.
+ */
+class _OutlineKind {
+ static const _OutlineKind CLASS = const _OutlineKind('CLASS');
+ static const _OutlineKind CLASS_TYPE_ALIAS = const _OutlineKind('CLASS_TYPE_ALIAS');
+ static const _OutlineKind COMPILATION_UNIT = const _OutlineKind('COMPILATION_UNIT');
+ static const _OutlineKind CONSTRUCTOR = const _OutlineKind('CONSTRUCTOR');
+ static const _OutlineKind GETTER = const _OutlineKind('GETTER');
+ static const _OutlineKind FIELD = const _OutlineKind('FIELD');
+ static const _OutlineKind FUNCTION = const _OutlineKind('FUNCTION');
+ static const _OutlineKind FUNCTION_TYPE_ALIAS = const _OutlineKind('FUNCTION_TYPE_ALIAS');
+ static const _OutlineKind LIBRARY = const _OutlineKind('LIBRARY');
+ static const _OutlineKind METHOD = const _OutlineKind('METHOD');
+ static const _OutlineKind SETTER = const _OutlineKind('SETTER');
+ static const _OutlineKind TOP_LEVEL_VARIABLE = const _OutlineKind('TOP_LEVEL_VARIABLE');
+ static const _OutlineKind UNKNOWN = const _OutlineKind('UNKNOWN');
+ static const _OutlineKind UNIT_TEST_CASE = const _OutlineKind('UNIT_TEST_CASE');
+ static const _OutlineKind UNIT_TEST_GROUP = const _OutlineKind('UNIT_TEST_GROUP');
+
+ final String name;
+
+ const _OutlineKind(this.name);
+
+ static _OutlineKind valueOf(String name) {
+ ClassMirror classMirror = reflectClass(_OutlineKind);
+ return classMirror.getField(new Symbol(name)).reflectee;
+ }
+}
+
+
+/**
+ * An element outline.
+ */
+class _Outline {
+ static const List<_Outline> EMPTY_ARRAY = const <_Outline>[];
+
+ final _OutlineKind kind;
+ final String name;
+ final int nameOffset;
+ final int nameLength;
+ final int elementOffset;
+ final int elementLength;
+ final bool isAbstract;
+ final bool isStatic;
+ final String parameters;
+ final String returnType;
+ final List<_Outline> children = <_Outline>[];
+
+ _Outline(this.kind, this.name,
+ this.nameOffset, this.nameLength,
+ this.elementOffset, this.elementLength,
+ this.isAbstract, this.isStatic,
+ this.parameters, this.returnType);
+
+ factory _Outline.fromJson(Map<String, Object> map) {
+ _Outline outline = new _Outline(
+ _OutlineKind.valueOf(map[KIND]), map[NAME],
+ map[NAME_OFFSET], map[NAME_LENGTH],
+ map[ELEMENT_OFFSET], map[ELEMENT_LENGTH],
+ map[IS_ABSTRACT], map[IS_STATIC],
+ map[PARAMETERS], map[RETURN_TYPE]);
+ List<Map<String, Object>> childrenMaps = map[CHILDREN];
+ if (childrenMaps != null) {
+ childrenMaps.forEach((childMap) {
+ outline.children.add(new _Outline.fromJson(childMap));
+ });
+ }
+ return outline;
+ }
+}
diff --git a/pkg/http_multi_server/LICENSE b/pkg/http_multi_server/LICENSE
new file mode 100644
index 0000000..5c60afe
--- /dev/null
+++ b/pkg/http_multi_server/LICENSE
@@ -0,0 +1,26 @@
+Copyright 2014, the Dart project authors. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/pkg/http_multi_server/README.md b/pkg/http_multi_server/README.md
new file mode 100644
index 0000000..e12c8d1
--- /dev/null
+++ b/pkg/http_multi_server/README.md
@@ -0,0 +1,25 @@
+An implementation of `dart:io`'s [HttpServer][] that wraps multiple servers and
+forwards methods to all of them. It's useful for serving the same application on
+multiple network interfaces while still having a unified way of controlling the
+servers. In particular, it supports serving on both the IPv4 and IPv6 loopback
+addresses using [HttpMultiServer.loopback][].
+
+```dart
+import 'package:http_multi_server/http_multi_server.dart';
+import 'package:shelf/shelf.dart' as shelf;
+import 'package:shelf/shelf_io.dart' as shelf_io;
+
+void main() {
+ // Both http://127.0.0.1:8080 and http://[::1]:8080 will be bound to the same
+ // server.
+ HttpMultiServer.loopback(8080).then((server) {
+ shelf_io.serveRequests(server, (request) {
+ return new shelf.Response.ok("Hello, world!");
+ });
+ });
+}
+```
+
+[HttpServer]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart-io.HttpServer
+
+[HttpMultiServer.loopback]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/http_multi_server/http_multi_server.HttpMultiServer#id_loopback
diff --git a/pkg/http_multi_server/lib/http_multi_server.dart b/pkg/http_multi_server/lib/http_multi_server.dart
new file mode 100644
index 0000000..c055367
--- /dev/null
+++ b/pkg/http_multi_server/lib/http_multi_server.dart
@@ -0,0 +1,136 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library http_multi_server;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'src/utils.dart';
+
+/// An implementation of `dart:io`'s [HttpServer] that wraps multiple servers
+/// and forwards methods to all of them.
+///
+/// This is useful for serving the same application on multiple network
+/// interfaces while still having a unified way of controlling the servers. In
+/// particular, it supports serving on both the IPv4 and IPv6 loopback addresses
+/// using [HttpMultiServer.loopback].
+class HttpMultiServer extends StreamView<HttpRequest> implements HttpServer {
+ /// The wrapped servers.
+ final Set<HttpServer> _servers;
+
+ String get serverHeader => _servers.first.serverHeader;
+ set serverHeader(String value) {
+ for (var server in _servers) {
+ server.serverHeader = value;
+ }
+ }
+
+ Duration get idleTimeout => _servers.first.idleTimeout;
+ set idleTimeout(Duration value) {
+ for (var server in _servers) {
+ server.idleTimeout = value;
+ }
+ }
+
+ /// Returns the port that one of the wrapped servers is listening on.
+ ///
+ /// If the wrapped servers are listening on different ports, it's not defined
+ /// which port is returned.
+ int get port => _servers.first.port;
+
+ /// Returns the address that one of the wrapped servers is listening on.
+ ///
+ /// If the wrapped servers are listening on different addresses, it's not
+ /// defined which address is returned.
+ InternetAddress get address => _servers.first.address;
+
+ set sessionTimeout(int value) {
+ for (var server in _servers) {
+ server.sessionTimeout = value;
+ }
+ }
+
+ /// Creates an [HttpMultiServer] wrapping [servers].
+ ///
+ /// All [servers] should have the same configuration and none should be
+ /// listened to when this is called.
+ HttpMultiServer(Iterable<HttpServer> servers)
+ : _servers = servers.toSet(),
+ super(mergeStreams(servers));
+
+ /// Creates an [HttpServer] listening on all available loopback addresses for
+ /// this computer.
+ ///
+ /// If this computer supports both IPv4 and IPv6, this returns an
+ /// [HttpMultiServer] listening to [port] on both loopback addresses.
+ /// Otherwise, it returns a normal [HttpServer] listening only on the IPv4
+ /// address.
+ ///
+ /// If [port] is 0, the same ephemeral port is used for both the IPv4 and IPv6
+ /// addresses.
+ static Future<HttpServer> loopback(int port, {int backlog}) {
+ if (backlog == null) backlog = 0;
+
+ return _loopback(port, (address, port) =>
+ HttpServer.bind(address, port, backlog: backlog));
+ }
+
+ /// Like [loopback], but supports HTTPS requests.
+ ///
+ /// The certificate with nickname or distinguished name (DN) [certificateName]
+ /// is looked up in the certificate database, and is used as the server
+ /// certificate. If [requestClientCertificate] is true, the server will
+ /// request clients to authenticate with a client certificate.
+ static Future<HttpServer> loopbackSecure(int port, {int backlog,
+ String certificateName, bool requestClientCertificate: false}) {
+ if (backlog == null) backlog = 0;
+
+ return _loopback(port, (address, port) =>
+ HttpServer.bindSecure(address, port, backlog: backlog,
+ certificateName: certificateName,
+ requestClientCertificate: requestClientCertificate));
+ }
+
+ /// A helper method for initializing loopback servers.
+ ///
+ /// [bind] should forward to either [HttpServer.bind] or
+ /// [HttpServer.bindSecure].
+ static Future<HttpServer> _loopback(int port,
+ Future<HttpServer> bind(InternetAddress address, int port)) {
+ return Future.wait([
+ supportsIpV6,
+ bind(InternetAddress.LOOPBACK_IP_V4, port)
+ ]).then((results) {
+ var supportsIpV6 = results[0];
+ var v4Server = results[1];
+
+ if (!supportsIpV6) return v4Server;
+
+ // Reuse the IPv4 server's port so that if [port] is 0, both servers use
+ // the same ephemeral port.
+ return bind(InternetAddress.LOOPBACK_IP_V6, v4Server.port)
+ .then((v6Server) {
+ return new HttpMultiServer([v4Server, v6Server]);
+ });
+ });
+ }
+
+ Future close({bool force: false}) =>
+ Future.wait(_servers.map((server) => server.close(force: force)));
+
+ /// Returns an HttpConnectionsInfo object summarizing the total number of
+ /// current connections handled by all the servers.
+ HttpConnectionsInfo connectionsInfo() {
+ var info = new HttpConnectionsInfo();
+ for (var server in _servers) {
+ var subInfo = server.connectionsInfo();
+ info.total += subInfo.total;
+ info.active += subInfo.active;
+ info.idle += subInfo.idle;
+ info.closing += subInfo.closing;
+ }
+ return info;
+ }
+}
diff --git a/pkg/http_multi_server/lib/src/utils.dart b/pkg/http_multi_server/lib/src/utils.dart
new file mode 100644
index 0000000..9e95aba
--- /dev/null
+++ b/pkg/http_multi_server/lib/src/utils.dart
@@ -0,0 +1,62 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library http_multi_server.utils;
+
+import 'dart:async';
+import 'dart:io';
+
+/// Merges all streams in [streams] into a single stream that emits all of their
+/// values.
+///
+/// The returned stream will be closed only when every stream in [streams] is
+/// closed.
+Stream mergeStreams(Iterable<Stream> streams) {
+ var subscriptions = new Set();
+ var controller;
+ controller = new StreamController(onListen: () {
+ for (var stream in streams) {
+ var subscription;
+ subscription = stream.listen(controller.add,
+ onError: controller.addError,
+ onDone: () {
+ subscriptions.remove(subscription);
+ if (subscriptions.isEmpty) controller.close();
+ });
+ subscriptions.add(subscription);
+ }
+ }, onCancel: () {
+ for (var subscription in subscriptions) {
+ subscription.cancel();
+ }
+ }, onPause: () {
+ for (var subscription in subscriptions) {
+ subscription.pause();
+ }
+ }, onResume: () {
+ for (var subscription in subscriptions) {
+ subscription.resume();
+ }
+ }, sync: true);
+
+ return controller.stream;
+}
+
+/// A cache for [supportsIpV6].
+bool _supportsIpV6;
+
+/// Returns whether this computer supports binding to IPv6 addresses.
+Future<bool> get supportsIpV6 {
+ if (_supportsIpV6 != null) return new Future.value(_supportsIpV6);
+
+ return ServerSocket.bind(InternetAddress.LOOPBACK_IP_V6, 0).then((socket) {
+ _supportsIpV6 = true;
+ socket.close();
+ return true;
+ }).catchError((error) {
+ if (error is! SocketException) throw error;
+ _supportsIpV6 = false;
+ return false;
+ });
+}
diff --git a/pkg/http_multi_server/pubspec.yaml b/pkg/http_multi_server/pubspec.yaml
new file mode 100644
index 0000000..4199c58
--- /dev/null
+++ b/pkg/http_multi_server/pubspec.yaml
@@ -0,0 +1,11 @@
+name: http_multi_server
+version: 1.0.0
+author: "Dart Team <misc@dartlang.org>"
+homepage: http://www.dartlang.org
+description:
+ A dart:io HttpServer wrapper that handles requests from multiple servers.
+dev_dependencies:
+ unittest: ">=0.11.0 <0.12.0"
+ http: ">=0.11.0 <0.12.0"
+environment:
+ sdk: ">=1.4.0 <2.0.0"
diff --git a/pkg/http_multi_server/test/http_multi_server_test.dart b/pkg/http_multi_server/test/http_multi_server_test.dart
new file mode 100644
index 0000000..a3d9062
--- /dev/null
+++ b/pkg/http_multi_server/test/http_multi_server_test.dart
@@ -0,0 +1,128 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library http_multi_server.test;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:http/http.dart' as http;
+import 'package:http_multi_server/http_multi_server.dart';
+import 'package:http_multi_server/src/utils.dart';
+import 'package:unittest/unittest.dart';
+
+void main() {
+ group("with multiple HttpServers", () {
+ var multiServer;
+ var subServer1;
+ var subServer2;
+ var subServer3;
+ setUp(() {
+ return Future.wait([
+ HttpServer.bind("127.0.0.1", 0).then((server) => subServer1 = server),
+ HttpServer.bind("127.0.0.1", 0).then((server) => subServer2 = server),
+ HttpServer.bind("127.0.0.1", 0).then((server) => subServer3 = server)
+ ]).then((servers) => multiServer = new HttpMultiServer(servers));
+ });
+
+ tearDown(() => multiServer.close());
+
+ test("listen listens to all servers", () {
+ multiServer.listen((request) {
+ request.response.write("got request");
+ request.response.close();
+ });
+
+ expect(_read(subServer1), completion(equals("got request")));
+ expect(_read(subServer2), completion(equals("got request")));
+ expect(_read(subServer3), completion(equals("got request")));
+ });
+
+ test("serverHeader= sets the value for all servers", () {
+ multiServer.serverHeader = "http_multi_server test";
+
+ multiServer.listen((request) {
+ request.response.write("got request");
+ request.response.close();
+ });
+
+ expect(_get(subServer1).then((response) {
+ expect(response.headers['server'], equals("http_multi_server test"));
+ }), completes);
+
+ expect(_get(subServer2).then((response) {
+ expect(response.headers['server'], equals("http_multi_server test"));
+ }), completes);
+
+ expect(_get(subServer3).then((response) {
+ expect(response.headers['server'], equals("http_multi_server test"));
+ }), completes);
+ });
+
+ test("connectionsInfo sums the values for all servers", () {
+ var pendingRequests = 0;
+ var awaitingResponseCompleter = new Completer();
+ var sendResponseCompleter = new Completer();
+ multiServer.listen((request) {
+ sendResponseCompleter.future.then((_) {
+ request.response.write("got request");
+ request.response.close();
+ });
+
+ pendingRequests++;
+ if (pendingRequests == 2) awaitingResponseCompleter.complete();
+ });
+
+ // Queue up some requests, then wait on [awaitingResponseCompleter] to
+ // make sure they're in-flight before we check [connectionsInfo].
+ expect(_get(subServer1), completes);
+ expect(_get(subServer2), completes);
+
+ return awaitingResponseCompleter.future.then((_) {
+ var info = multiServer.connectionsInfo();
+ expect(info.total, equals(2));
+ expect(info.active, equals(2));
+ expect(info.idle, equals(0));
+ expect(info.closing, equals(0));
+
+ sendResponseCompleter.complete();
+ });
+ });
+ });
+
+ group("HttpMultiServer.loopback", () {
+ var server;
+ setUp(() {
+ return HttpMultiServer.loopback(0).then((server_) => server = server_);
+ });
+
+ tearDown(() => server.close());
+
+ test("listens on all localhost interfaces", () {
+ server.listen((request) {
+ request.response.write("got request");
+ request.response.close();
+ });
+
+ expect(http.read("http://127.0.0.1:${server.port}/"),
+ completion(equals("got request")));
+
+ return supportsIpV6.then((supportsIpV6) {
+ if (!supportsIpV6) return;
+ expect(http.read("http://[::1]:${server.port}/"),
+ completion(equals("got request")));
+ });
+ });
+ });
+}
+
+/// Makes a GET request to the root of [server] and returns the response.
+Future<http.Response> _get(HttpServer server) => http.get(_urlFor(server));
+
+/// Makes a GET request to the root of [server] and returns the response body.
+Future<String> _read(HttpServer server) => http.read(_urlFor(server));
+
+/// Returns the URL for the root of [server].
+String _urlFor(HttpServer server) =>
+ "http://${server.address.host}:${server.port}/";
diff --git a/pkg/http_server/lib/src/http_multi_server.dart b/pkg/http_server/lib/src/http_multi_server.dart
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pkg/http_server/lib/src/http_multi_server.dart
diff --git a/pkg/json_rpc_2/test/server/stream_test.dart b/pkg/json_rpc_2/test/server/stream_test.dart
new file mode 100644
index 0000000..5459e3e
--- /dev/null
+++ b/pkg/json_rpc_2/test/server/stream_test.dart
@@ -0,0 +1,98 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library json_rpc_2.test.server.stream_test;
+
+import 'dart:async';
+
+import 'package:unittest/unittest.dart';
+import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
+
+import 'utils.dart';
+
+void main() {
+ test(".withoutJson supports decoded stream and sink", () {
+ var requestController = new StreamController();
+ var responseController = new StreamController();
+ var server = new json_rpc.Server.withoutJson(
+ requestController.stream, responseController.sink);
+ server.listen();
+
+ server.registerMethod('foo', (params) {
+ return {'params': params.value};
+ });
+
+ requestController.add({
+ 'jsonrpc': '2.0',
+ 'method': 'foo',
+ 'params': {'param': 'value'},
+ 'id': 1234
+ });
+
+ expect(responseController.stream.first, completion(equals({
+ 'jsonrpc': '2.0',
+ 'result': {'params': {'param': 'value'}},
+ 'id': 1234
+ })));
+ });
+
+ test(".listen returns when the controller is closed", () {
+ var requestController = new StreamController();
+ var responseController = new StreamController();
+ var server = new json_rpc.Server(
+ requestController.stream, responseController.sink);
+
+ var hasListenCompeted = false;
+ expect(server.listen().then((_) => hasListenCompeted = true), completes);
+
+ return pumpEventQueue().then((_) {
+ expect(hasListenCompeted, isFalse);
+
+ // This should cause listen to complete.
+ return requestController.close();
+ });
+ });
+
+ test(".listen returns a stream error", () {
+ var requestController = new StreamController();
+ var responseController = new StreamController();
+ var server = new json_rpc.Server(
+ requestController.stream, responseController.sink);
+
+ expect(server.listen(), throwsA('oh no'));
+ requestController.addError('oh no');
+ });
+
+ test(".listen can't be called twice", () {
+ var requestController = new StreamController();
+ var responseController = new StreamController();
+ var server = new json_rpc.Server(
+ requestController.stream, responseController.sink);
+ server.listen();
+
+ expect(() => server.listen(), throwsStateError);
+ });
+
+ test(".close cancels the stream subscription and closes the sink", () {
+ var requestController = new StreamController();
+ var responseController = new StreamController();
+ var server = new json_rpc.Server(
+ requestController.stream, responseController.sink);
+
+ expect(server.listen(), completes);
+ expect(server.close(), completes);
+
+ expect(() => requestController.stream.listen((_) {}), throwsStateError);
+ expect(responseController.isClosed, isTrue);
+ });
+
+ test(".close can't be called before .listen", () {
+ var requestController = new StreamController();
+ var responseController = new StreamController();
+ var server = new json_rpc.Server(
+ requestController.stream, responseController.sink);
+
+ expect(() => server.close(), throwsStateError);
+ });
+}
diff --git a/pkg/matcher/CHANGELOG.md b/pkg/matcher/CHANGELOG.md
new file mode 100644
index 0000000..98cd9f4
--- /dev/null
+++ b/pkg/matcher/CHANGELOG.md
@@ -0,0 +1,21 @@
+## 0.10.0+3
+
+* Removed `@deprecated` annotation on matchers due to
+[Issue 19173](https://code.google.com/p/dart/issues/detail?id=19173)
+
+## 0.10.0+2
+
+* Added types to a number of constants.
+
+## 0.10.0+1
+
+* Matchers related to bad language use have been removed. These represent code
+structure that should rarely or never be validated in tests.
+ * `isAbstractClassInstantiationError`
+ * `throwsAbstractClassInstantiationError`
+ * `isFallThroughError`
+ * `throwsFallThroughError`
+
+* Added types to a number of method arguments.
+
+* The structure of the library and test code has been updated.
diff --git a/pkg/matcher/README.md b/pkg/matcher/README.md
new file mode 100644
index 0000000..cf165c1
--- /dev/null
+++ b/pkg/matcher/README.md
@@ -0,0 +1,7 @@
+Support for specifying test expectations, such as for unit tests.
+
+The matcher library provides a third-generation assertion mechanism, drawing
+inspiration from [Hamcrest](http://code.google.com/p/hamcrest/).
+
+For more information, see
+[Unit Testing with Dart](http://www.dartlang.org/articles/dart-unit-tests/).
diff --git a/pkg/matcher/lib/src/error_matchers.dart b/pkg/matcher/lib/src/error_matchers.dart
new file mode 100644
index 0000000..cb716f5
--- /dev/null
+++ b/pkg/matcher/lib/src/error_matchers.dart
@@ -0,0 +1,173 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library matcher.error_matchers;
+
+import 'core_matchers.dart';
+import 'interfaces.dart';
+
+/// **DEPRECATED**
+///
+/// Will be removed in the next major release.
+// TODO(kevmoo): re-deprecate once 19173 is resolved
+//@deprecated
+const Matcher isAbstractClassInstantiationError =
+ const _AbstractClassInstantiationError();
+
+/// **DEPRECATED**
+///
+/// Will be removed in the next major release.
+// TODO(kevmoo): re-deprecate once 19173 is resolved
+//@deprecated
+const Matcher throwsAbstractClassInstantiationError =
+ const Throws(isAbstractClassInstantiationError);
+
+class _AbstractClassInstantiationError extends TypeMatcher {
+ const _AbstractClassInstantiationError() :
+ super("AbstractClassInstantiationError");
+ bool matches(item, Map matchState) => item is AbstractClassInstantiationError;
+}
+
+/// A matcher for ArgumentErrors.
+const Matcher isArgumentError = const _ArgumentError();
+
+/// A matcher for functions that throw ArgumentError.
+const Matcher throwsArgumentError = const Throws(isArgumentError);
+
+class _ArgumentError extends TypeMatcher {
+ const _ArgumentError(): super("ArgumentError");
+ bool matches(item, Map matchState) => item is ArgumentError;
+}
+
+/// A matcher for ConcurrentModificationError.
+const Matcher isConcurrentModificationError =
+ const _ConcurrentModificationError();
+
+/// A matcher for functions that throw ConcurrentModificationError.
+const Matcher throwsConcurrentModificationError =
+ const Throws(isConcurrentModificationError);
+
+class _ConcurrentModificationError extends TypeMatcher {
+ const _ConcurrentModificationError(): super("ConcurrentModificationError");
+ bool matches(item, Map matchState) => item is ConcurrentModificationError;
+}
+
+/// A matcher for CyclicInitializationError.
+const Matcher isCyclicInitializationError = const _CyclicInitializationError();
+
+/// A matcher for functions that throw CyclicInitializationError.
+const Matcher throwsCyclicInitializationError =
+ const Throws(isCyclicInitializationError);
+
+class _CyclicInitializationError extends TypeMatcher {
+ const _CyclicInitializationError(): super("CyclicInitializationError");
+ bool matches(item, Map matchState) => item is CyclicInitializationError;
+}
+
+/// A matcher for Exceptions.
+const Matcher isException = const _Exception();
+
+/// A matcher for functions that throw Exception.
+const Matcher throwsException = const Throws(isException);
+
+class _Exception extends TypeMatcher {
+ const _Exception(): super("Exception");
+ bool matches(item, Map matchState) => item is Exception;
+}
+
+/// **DEPRECATED**
+///
+/// Will be removed in the next major release.
+// TODO(kevmoo): re-deprecate once 19173 is resolved
+//@deprecated
+const Matcher isFallThroughError = const _FallThroughError();
+
+/// **DEPRECATED**
+///
+/// Will be removed in the next major release.
+// TODO(kevmoo): re-deprecate once 19173 is resolved
+//@deprecated
+const Matcher throwsFallThroughError = const Throws(isFallThroughError);
+
+class _FallThroughError extends TypeMatcher {
+ const _FallThroughError(): super("FallThroughError");
+ bool matches(item, Map matchState) => item is FallThroughError;
+}
+
+/// A matcher for FormatExceptions.
+const Matcher isFormatException = const _FormatException();
+
+/// A matcher for functions that throw FormatException.
+const Matcher throwsFormatException = const Throws(isFormatException);
+
+class _FormatException extends TypeMatcher {
+ const _FormatException(): super("FormatException");
+ bool matches(item, Map matchState) => item is FormatException;
+}
+
+/// A matcher for NoSuchMethodErrors.
+const Matcher isNoSuchMethodError = const _NoSuchMethodError();
+
+/// A matcher for functions that throw NoSuchMethodError.
+const Matcher throwsNoSuchMethodError = const Throws(isNoSuchMethodError);
+
+class _NoSuchMethodError extends TypeMatcher {
+ const _NoSuchMethodError(): super("NoSuchMethodError");
+ bool matches(item, Map matchState) => item is NoSuchMethodError;
+}
+
+/// A matcher for NullThrownError.
+const Matcher isNullThrownError = const _NullThrownError();
+
+/// A matcher for functions that throw NullThrownError.
+const Matcher throwsNullThrownError = const Throws(isNullThrownError);
+
+class _NullThrownError extends TypeMatcher {
+ const _NullThrownError(): super("NullThrownError");
+ bool matches(item, Map matchState) => item is NullThrownError;
+}
+
+/// A matcher for RangeErrors.
+const Matcher isRangeError = const _RangeError();
+
+/// A matcher for functions that throw RangeError.
+const Matcher throwsRangeError = const Throws(isRangeError);
+
+class _RangeError extends TypeMatcher {
+ const _RangeError(): super("RangeError");
+ bool matches(item, Map matchState) => item is RangeError;
+}
+
+/// A matcher for StateErrors.
+const Matcher isStateError = const _StateError();
+
+/// A matcher for functions that throw StateError.
+const Matcher throwsStateError = const Throws(isStateError);
+
+class _StateError extends TypeMatcher {
+ const _StateError(): super("StateError");
+ bool matches(item, Map matchState) => item is StateError;
+}
+
+/// A matcher for UnimplementedErrors.
+const Matcher isUnimplementedError = const _UnimplementedError();
+
+/// A matcher for functions that throw Exception.
+const Matcher throwsUnimplementedError = const Throws(isUnimplementedError);
+
+class _UnimplementedError extends TypeMatcher {
+ const _UnimplementedError(): super("UnimplementedError");
+ bool matches(item, Map matchState) => item is UnimplementedError;
+}
+
+/// A matcher for UnsupportedError.
+const Matcher isUnsupportedError = const _UnsupportedError();
+
+/// A matcher for functions that throw UnsupportedError.
+const Matcher throwsUnsupportedError = const Throws(isUnsupportedError);
+
+class _UnsupportedError extends TypeMatcher {
+ const _UnsupportedError(): super("UnsupportedError");
+ bool matches(item, Map matchState) => item is UnsupportedError;
+}
diff --git a/pkg/matcher/test/core_matchers_test.dart b/pkg/matcher/test/core_matchers_test.dart
new file mode 100644
index 0000000..3f694f3
--- /dev/null
+++ b/pkg/matcher/test/core_matchers_test.dart
@@ -0,0 +1,211 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library matcher.core_matchers_test;
+
+import 'package:matcher/matcher.dart';
+import 'package:unittest/unittest.dart' show test, group;
+
+import 'test_utils.dart';
+
+void main() {
+ initUtils();
+
+ test('isTrue', () {
+ shouldPass(true, isTrue);
+ shouldFail(false, isTrue, "Expected: true Actual: <false>");
+ });
+
+ test('isFalse', () {
+ shouldPass(false, isFalse);
+ shouldFail(10, isFalse, "Expected: false Actual: <10>");
+ shouldFail(true, isFalse, "Expected: false Actual: <true>");
+ });
+
+ test('isNull', () {
+ shouldPass(null, isNull);
+ shouldFail(false, isNull, "Expected: null Actual: <false>");
+ });
+
+ test('isNotNull', () {
+ shouldPass(false, isNotNull);
+ shouldFail(null, isNotNull, "Expected: not null Actual: <null>");
+ });
+
+ test('same', () {
+ var a = new Map();
+ var b = new Map();
+ shouldPass(a, same(a));
+ shouldFail(b, same(a), "Expected: same instance as {} Actual: {}");
+ });
+
+ test('equals', () {
+ var a = new Map();
+ var b = new Map();
+ shouldPass(a, equals(a));
+ shouldPass(a, equals(b));
+ });
+
+ test('anything', () {
+ var a = new Map();
+ shouldPass(0, anything);
+ shouldPass(null, anything);
+ shouldPass(a, anything);
+ shouldFail(a, isNot(anything), "Expected: not anything Actual: {}");
+ });
+
+ test('throws', () {
+ shouldFail(doesNotThrow, throws,
+ matches(
+ r"Expected: throws"
+ r" Actual: <Closure(: \(\) => dynamic "
+ r"from Function 'doesNotThrow': static\.)?>"
+ r" Which: did not throw"));
+ shouldPass(doesThrow, throws);
+ shouldFail(true, throws,
+ "Expected: throws"
+ " Actual: <true>"
+ " Which: is not a Function or Future");
+ });
+
+ test('throwsA', () {
+ shouldPass(doesThrow, throwsA(equals('X')));
+ shouldFail(doesThrow, throwsA(equals('Y')),
+ matches(
+ r"Expected: throws 'Y'"
+ r" Actual: <Closure(: \(\) => dynamic "
+ r"from Function 'doesThrow': static\.)?>"
+ r" Which: threw 'X'"));
+ });
+
+ test('throwsA', () {
+ shouldPass(doesThrow, throwsA(equals('X')));
+ shouldFail(doesThrow, throwsA(equals('Y')),
+ matches("Expected: throws 'Y'.*"
+ "Actual: <Closure.*"
+ "Which: threw 'X'"));
+ });
+
+ test('returnsNormally', () {
+ shouldPass(doesNotThrow, returnsNormally);
+ shouldFail(doesThrow, returnsNormally,
+ matches(
+ r"Expected: return normally"
+ r" Actual: <Closure(: \(\) => dynamic "
+ r"from Function 'doesThrow': static\.)?>"
+ r" Which: threw 'X'"));
+ });
+
+
+ test('hasLength', () {
+ var a = new Map();
+ var b = new List();
+ shouldPass(a, hasLength(0));
+ shouldPass(b, hasLength(0));
+ shouldPass('a', hasLength(1));
+ shouldFail(0, hasLength(0), new PrefixMatcher(
+ "Expected: an object with length of <0> "
+ "Actual: <0> "
+ "Which: has no length property"));
+
+ b.add(0);
+ shouldPass(b, hasLength(1));
+ shouldFail(b, hasLength(2),
+ "Expected: an object with length of <2> "
+ "Actual: [0] "
+ "Which: has length of <1>");
+
+ b.add(0);
+ shouldFail(b, hasLength(1),
+ "Expected: an object with length of <1> "
+ "Actual: [0, 0] "
+ "Which: has length of <2>");
+ shouldPass(b, hasLength(2));
+ });
+
+ test('scalar type mismatch', () {
+ shouldFail('error', equals(5.1),
+ "Expected: <5.1> "
+ "Actual: 'error'");
+ });
+
+ test('nested type mismatch', () {
+ shouldFail(['error'], equals([5.1]),
+ "Expected: [5.1] "
+ "Actual: ['error'] "
+ "Which: was 'error' instead of <5.1> at location [0]");
+ });
+
+ test('doubly-nested type mismatch', () {
+ shouldFail([['error']], equals([[5.1]]),
+ "Expected: [[5.1]] "
+ "Actual: [['error']] "
+ "Which: was 'error' instead of <5.1> at location [0][0]");
+ });
+
+ test('doubly nested inequality', () {
+ var actual1 = [['foo', 'bar'], ['foo'], 3, []];
+ var expected1 = [['foo', 'bar'], ['foo'], 4, []];
+ var reason1 = "Expected: [['foo', 'bar'], ['foo'], 4, []] "
+ "Actual: [['foo', 'bar'], ['foo'], 3, []] "
+ "Which: was <3> instead of <4> at location [2]";
+
+ var actual2 = [['foo', 'barry'], ['foo'], 4, []];
+ var expected2 = [['foo', 'bar'], ['foo'], 4, []];
+ var reason2 = "Expected: [['foo', 'bar'], ['foo'], 4, []] "
+ "Actual: [['foo', 'barry'], ['foo'], 4, []] "
+ "Which: was 'barry' instead of 'bar' at location [0][1]";
+
+ var actual3 = [['foo', 'bar'], ['foo'], 4, {'foo':'bar'}];
+ var expected3 = [['foo', 'bar'], ['foo'], 4, {'foo':'barry'}];
+ var reason3 = "Expected: [['foo', 'bar'], ['foo'], 4, {'foo': 'barry'}] "
+ "Actual: [['foo', 'bar'], ['foo'], 4, {'foo': 'bar'}] "
+ "Which: was 'bar' instead of 'barry' at location [3]['foo']";
+
+ shouldFail(actual1, equals(expected1), reason1);
+ shouldFail(actual2, equals(expected2), reason2);
+ shouldFail(actual3, equals(expected3), reason3);
+ });
+
+ test('isInstanceOf', () {
+ shouldFail(0, new isInstanceOf<String>('String'),
+ "Expected: an instance of String Actual: <0>");
+ shouldPass('cow', new isInstanceOf<String>('String'));
+ });
+
+ group('Predicate Matchers', () {
+ test('isInstanceOf', () {
+ shouldFail(0, predicate((x) => x is String, "an instance of String"),
+ "Expected: an instance of String Actual: <0>");
+ shouldPass('cow', predicate((x) => x is String, "an instance of String"));
+ });
+ });
+
+ group('exception/error matchers', () {
+ test('throwsCyclicInitializationError', () {
+ expect(() => _Bicycle.foo, throwsCyclicInitializationError);
+ });
+
+ test('throwsConcurrentModificationError', () {
+ expect(() {
+ var a = { 'foo': 'bar' };
+ for (var k in a.keys) {
+ a.remove(k);
+ }
+ }, throwsConcurrentModificationError);
+ });
+
+ test('throwsNullThrownError', () {
+ expect(() => throw null, throwsNullThrownError);
+ });
+ });
+}
+
+class _Bicycle {
+ static final foo = bar();
+
+ static bar() {
+ return foo + 1;
+ }
+}
diff --git a/pkg/matcher/test/deprecated_matchers_test.dart b/pkg/matcher/test/deprecated_matchers_test.dart
new file mode 100644
index 0000000..b6acc1c
--- /dev/null
+++ b/pkg/matcher/test/deprecated_matchers_test.dart
@@ -0,0 +1,33 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library matcher.deprecated_matchers_test;
+
+import 'package:matcher/matcher.dart';
+import 'package:unittest/unittest.dart' show test, group;
+
+import 'test_utils.dart';
+
+void main() {
+ initUtils();
+
+ test('throwsAbstractClassInstantiationError', () {
+ expect(() => new _AbstractClass(), throwsAbstractClassInstantiationError);
+ });
+
+ test('throwsFallThroughError', () {
+ expect(() {
+ var a = 0;
+ switch (a) {
+ case 0:
+ a += 1;
+ case 1:
+ return;
+ }
+ }, throwsFallThroughError);
+ });
+}
+
+abstract class _AbstractClass {
+}
diff --git a/pkg/matcher/test/future_matchers_test.dart b/pkg/matcher/test/future_matchers_test.dart
new file mode 100644
index 0000000..6fac0de
--- /dev/null
+++ b/pkg/matcher/test/future_matchers_test.dart
@@ -0,0 +1,76 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library matcher.future_matchers_test;
+
+import 'dart:async';
+
+import 'package:matcher/matcher.dart';
+import 'package:unittest/unittest.dart' show test, group;
+
+import 'test_utils.dart';
+
+void main() {
+ initUtils();
+
+ test('completes - unexpected error', () {
+ var completer = new Completer();
+ completer.completeError('X');
+ shouldFail(completer.future, completes,
+ contains('Expected future to complete successfully, '
+ 'but it failed with X'),
+ isAsync: true);
+ });
+
+ test('completes - successfully', () {
+ var completer = new Completer();
+ completer.complete('1');
+ shouldPass(completer.future, completes, isAsync: true);
+ });
+
+ test('throws - unexpected to see normal completion', () {
+ var completer = new Completer();
+ completer.complete('1');
+ shouldFail(completer.future, throws,
+ contains("Expected future to fail, but succeeded with '1'"),
+ isAsync: true);
+ });
+
+ test('throws - expected to see exception', () {
+ var completer = new Completer();
+ completer.completeError('X');
+ shouldPass(completer.future, throws, isAsync: true);
+ });
+
+ test('throws - expected to see exception thrown later on', () {
+ var completer = new Completer();
+ var chained = completer.future.then((_) { throw 'X'; });
+ shouldPass(chained, throws, isAsync: true);
+ completer.complete('1');
+ });
+
+ test('throwsA - unexpected normal completion', () {
+ var completer = new Completer();
+ completer.complete('1');
+ shouldFail(completer.future, throwsA(equals('X')),
+ contains("Expected future to fail, but succeeded with '1'"),
+ isAsync: true);
+ });
+
+ test('throwsA - correct error', () {
+ var completer = new Completer();
+ completer.completeError('X');
+ shouldPass(completer.future, throwsA(equals('X')), isAsync: true);
+ });
+
+ test('throwsA - wrong error', () {
+ var completer = new Completer();
+ completer.completeError('X');
+ shouldFail(completer.future, throwsA(equals('Y')),
+ "Expected: 'Y' Actual: 'X' "
+ "Which: is different. "
+ "Expected: Y Actual: X ^ Differ at offset 0",
+ isAsync: true);
+ });
+}
diff --git a/pkg/matcher/test/iterable_matchers_test.dart b/pkg/matcher/test/iterable_matchers_test.dart
new file mode 100644
index 0000000..4570d526
--- /dev/null
+++ b/pkg/matcher/test/iterable_matchers_test.dart
@@ -0,0 +1,166 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library matcher.iterable_matchers_test;
+
+import 'package:matcher/matcher.dart';
+import 'package:unittest/unittest.dart' show test, group;
+
+import 'test_utils.dart';
+
+void main() {
+ initUtils();
+
+ test('isEmpty', () {
+ shouldPass([], isEmpty);
+ shouldFail([1], isEmpty, "Expected: empty Actual: [1]");
+ });
+
+ test('contains', () {
+ var d = [1, 2];
+ shouldPass(d, contains(1));
+ shouldFail(d, contains(0), "Expected: contains <0> "
+ "Actual: [1, 2]");
+ });
+
+ test('equals with matcher element', () {
+ var d = ['foo', 'bar'];
+ shouldPass(d, equals(['foo', startsWith('ba')]));
+ shouldFail(d, equals(['foo', endsWith('ba')]),
+ "Expected: ['foo', <a string ending with 'ba'>] "
+ "Actual: ['foo', 'bar'] "
+ "Which: does not match a string ending with 'ba' at location [1]");
+ });
+
+ test('isIn', () {
+ var d = [1, 2];
+ shouldPass(1, isIn(d));
+ shouldFail(0, isIn(d), "Expected: is in [1, 2] Actual: <0>");
+ });
+
+ test('everyElement', () {
+ var d = [1, 2];
+ var e = [1, 1, 1];
+ shouldFail(d, everyElement(1),
+ "Expected: every element(<1>) "
+ "Actual: [1, 2] "
+ "Which: has value <2> which doesn't match <1> at index 1");
+ shouldPass(e, everyElement(1));
+ });
+
+ test('nested everyElement', () {
+ var d = [['foo', 'bar'], ['foo'], []];
+ var e = [['foo', 'bar'], ['foo'], 3, []];
+ shouldPass(d, everyElement(anyOf(isEmpty, contains('foo'))));
+ shouldFail(d, everyElement(everyElement(equals('foo'))),
+ "Expected: every element(every element('foo')) "
+ "Actual: [['foo', 'bar'], ['foo'], []] "
+ "Which: has value ['foo', 'bar'] which has value 'bar' "
+ "which is different. Expected: foo Actual: bar ^ "
+ "Differ at offset 0 at index 1 at index 0");
+ shouldFail(d, everyElement(allOf(hasLength(greaterThan(0)),
+ contains('foo'))),
+ "Expected: every element((an object with length of a value "
+ "greater than <0> and contains 'foo')) "
+ "Actual: [['foo', 'bar'], ['foo'], []] "
+ "Which: has value [] which has length of <0> at index 2");
+ shouldFail(d, everyElement(allOf(contains('foo'),
+ hasLength(greaterThan(0)))),
+ "Expected: every element((contains 'foo' and "
+ "an object with length of a value greater than <0>)) "
+ "Actual: [['foo', 'bar'], ['foo'], []] "
+ "Which: has value [] which doesn't match (contains 'foo' and "
+ "an object with length of a value greater than <0>) at index 2");
+ shouldFail(e, everyElement(allOf(contains('foo'),
+ hasLength(greaterThan(0)))),
+ "Expected: every element((contains 'foo' and an object with "
+ "length of a value greater than <0>)) "
+ "Actual: [['foo', 'bar'], ['foo'], 3, []] "
+ "Which: has value <3> which is not a string, map or iterable "
+ "at index 2");
+ });
+
+ test('anyElement', () {
+ var d = [1, 2];
+ var e = [1, 1, 1];
+ shouldPass(d, anyElement(2));
+ shouldFail(e, anyElement(2),
+ "Expected: some element <2> Actual: [1, 1, 1]");
+ });
+
+ test('orderedEquals', () {
+ shouldPass([null], orderedEquals([null]));
+ var d = [1, 2];
+ shouldPass(d, orderedEquals([1, 2]));
+ shouldFail(d, orderedEquals([2, 1]),
+ "Expected: equals [2, 1] ordered "
+ "Actual: [1, 2] "
+ "Which: was <1> instead of <2> at location [0]");
+ });
+
+ test('unorderedEquals', () {
+ var d = [1, 2];
+ shouldPass(d, unorderedEquals([2, 1]));
+ shouldFail(d, unorderedEquals([1]),
+ "Expected: equals [1] unordered "
+ "Actual: [1, 2] "
+ "Which: has too many elements (2 > 1)");
+ shouldFail(d, unorderedEquals([3, 2, 1]),
+ "Expected: equals [3, 2, 1] unordered "
+ "Actual: [1, 2] "
+ "Which: has too few elements (2 < 3)");
+ shouldFail(d, unorderedEquals([3, 1]),
+ "Expected: equals [3, 1] unordered "
+ "Actual: [1, 2] "
+ "Which: has no match for <3> at index 0");
+ });
+
+ test('unorderedMatchess', () {
+ var d = [1, 2];
+ shouldPass(d, unorderedMatches([2, 1]));
+ shouldPass(d, unorderedMatches([greaterThan(1), greaterThan(0)]));
+ shouldFail(d, unorderedMatches([greaterThan(0)]),
+ "Expected: matches [a value greater than <0>] unordered "
+ "Actual: [1, 2] "
+ "Which: has too many elements (2 > 1)");
+ shouldFail(d, unorderedMatches([3, 2, 1]),
+ "Expected: matches [<3>, <2>, <1>] unordered "
+ "Actual: [1, 2] "
+ "Which: has too few elements (2 < 3)");
+ shouldFail(d, unorderedMatches([3, 1]),
+ "Expected: matches [<3>, <1>] unordered "
+ "Actual: [1, 2] "
+ "Which: has no match for <3> at index 0");
+ shouldFail(d, unorderedMatches([greaterThan(3), greaterThan(0)]),
+ "Expected: matches [a value greater than <3>, a value greater than "
+ "<0>] unordered "
+ "Actual: [1, 2] "
+ "Which: has no match for a value greater than <3> at index 0");
+ });
+
+ test('pairwise compare', () {
+ var c = [1, 2];
+ var d = [1, 2, 3];
+ var e = [1, 4, 9];
+ shouldFail('x', pairwiseCompare(e, (e,a) => a <= e,
+ "less than or equal"),
+ "Expected: pairwise less than or equal [1, 4, 9] "
+ "Actual: 'x' "
+ "Which: is not an Iterable");
+ shouldFail(c, pairwiseCompare(e, (e,a) => a <= e, "less than or equal"),
+ "Expected: pairwise less than or equal [1, 4, 9] "
+ "Actual: [1, 2] "
+ "Which: has length 2 instead of 3");
+ shouldPass(d, pairwiseCompare(e, (e,a) => a <= e, "less than or equal"));
+ shouldFail(d, pairwiseCompare(e, (e,a) => a < e, "less than"),
+ "Expected: pairwise less than [1, 4, 9] "
+ "Actual: [1, 2, 3] "
+ "Which: has <1> which is not less than <1> at index 0");
+ shouldPass(d, pairwiseCompare(e, (e,a) => a * a == e, "square root of"));
+ shouldFail(d, pairwiseCompare(e, (e,a) => a + a == e, "double"),
+ "Expected: pairwise double [1, 4, 9] "
+ "Actual: [1, 2, 3] "
+ "Which: has <1> which is not double <1> at index 0");
+ });
+}
diff --git a/pkg/matcher/test/numeric_matchers_test.dart b/pkg/matcher/test/numeric_matchers_test.dart
new file mode 100644
index 0000000..60347bd
--- /dev/null
+++ b/pkg/matcher/test/numeric_matchers_test.dart
@@ -0,0 +1,152 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library matcher.numeric_matchers_test;
+
+import 'package:matcher/matcher.dart';
+import 'package:unittest/unittest.dart' show test, group;
+
+import 'test_utils.dart';
+
+void main() {
+ initUtils();
+
+ test('greaterThan', () {
+ shouldPass(10, greaterThan(9));
+ shouldFail(9, greaterThan(10),
+ "Expected: a value greater than <10> "
+ "Actual: <9> "
+ "Which: is not a value greater than <10>");
+ });
+
+ test('greaterThanOrEqualTo', () {
+ shouldPass(10, greaterThanOrEqualTo(10));
+ shouldFail(9, greaterThanOrEqualTo(10),
+ "Expected: a value greater than or equal to <10> "
+ "Actual: <9> "
+ "Which: is not a value greater than or equal to <10>");
+ });
+
+ test('lessThan', () {
+ shouldFail(10, lessThan(9),
+ "Expected: a value less than <9> "
+ "Actual: <10> "
+ "Which: is not a value less than <9>");
+ shouldPass(9, lessThan(10));
+ });
+
+ test('lessThanOrEqualTo', () {
+ shouldPass(10, lessThanOrEqualTo(10));
+ shouldFail(11, lessThanOrEqualTo(10),
+ "Expected: a value less than or equal to <10> "
+ "Actual: <11> "
+ "Which: is not a value less than or equal to <10>");
+ });
+
+ test('isZero', () {
+ shouldPass(0, isZero);
+ shouldFail(1, isZero,
+ "Expected: a value equal to <0> "
+ "Actual: <1> "
+ "Which: is not a value equal to <0>");
+ });
+
+ test('isNonZero', () {
+ shouldFail(0, isNonZero,
+ "Expected: a value not equal to <0> "
+ "Actual: <0> "
+ "Which: is not a value not equal to <0>");
+ shouldPass(1, isNonZero);
+ });
+
+ test('isPositive', () {
+ shouldFail(-1, isPositive,
+ "Expected: a positive value "
+ "Actual: <-1> "
+ "Which: is not a positive value");
+ shouldFail(0, isPositive,
+ "Expected: a positive value "
+ "Actual: <0> "
+ "Which: is not a positive value");
+ shouldPass(1, isPositive);
+ });
+
+ test('isNegative', () {
+ shouldPass(-1, isNegative);
+ shouldFail(0, isNegative,
+ "Expected: a negative value "
+ "Actual: <0> "
+ "Which: is not a negative value");
+ });
+
+ test('isNonPositive', () {
+ shouldPass(-1, isNonPositive);
+ shouldPass(0, isNonPositive);
+ shouldFail(1, isNonPositive,
+ "Expected: a non-positive value "
+ "Actual: <1> "
+ "Which: is not a non-positive value");
+ });
+
+ test('isNonNegative', () {
+ shouldPass(1, isNonNegative);
+ shouldPass(0, isNonNegative);
+ shouldFail(-1, isNonNegative,
+ "Expected: a non-negative value "
+ "Actual: <-1> "
+ "Which: is not a non-negative value");
+ });
+
+ test('closeTo', () {
+ shouldPass(0, closeTo(0, 1));
+ shouldPass(-1, closeTo(0, 1));
+ shouldPass(1, closeTo(0, 1));
+ shouldFail(1.001, closeTo(0, 1),
+ "Expected: a numeric value within <1> of <0> "
+ "Actual: <1.001> "
+ "Which: differs by <1.001>");
+ shouldFail(-1.001, closeTo(0, 1),
+ "Expected: a numeric value within <1> of <0> "
+ "Actual: <-1.001> "
+ "Which: differs by <1.001>");
+ });
+
+ test('inInclusiveRange', () {
+ shouldFail(-1, inInclusiveRange(0,2),
+ "Expected: be in range from 0 (inclusive) to 2 (inclusive) "
+ "Actual: <-1>");
+ shouldPass(0, inInclusiveRange(0,2));
+ shouldPass(1, inInclusiveRange(0,2));
+ shouldPass(2, inInclusiveRange(0,2));
+ shouldFail(3, inInclusiveRange(0,2),
+ "Expected: be in range from 0 (inclusive) to 2 (inclusive) "
+ "Actual: <3>");
+ });
+
+ test('inExclusiveRange', () {
+ shouldFail(0, inExclusiveRange(0,2),
+ "Expected: be in range from 0 (exclusive) to 2 (exclusive) "
+ "Actual: <0>");
+ shouldPass(1, inExclusiveRange(0,2));
+ shouldFail(2, inExclusiveRange(0,2),
+ "Expected: be in range from 0 (exclusive) to 2 (exclusive) "
+ "Actual: <2>");
+ });
+
+ test('inOpenClosedRange', () {
+ shouldFail(0, inOpenClosedRange(0,2),
+ "Expected: be in range from 0 (exclusive) to 2 (inclusive) "
+ "Actual: <0>");
+ shouldPass(1, inOpenClosedRange(0,2));
+ shouldPass(2, inOpenClosedRange(0,2));
+ });
+
+ test('inClosedOpenRange', () {
+ shouldPass(0, inClosedOpenRange(0,2));
+ shouldPass(1, inClosedOpenRange(0,2));
+ shouldFail(2, inClosedOpenRange(0,2),
+ "Expected: be in range from 0 (inclusive) to 2 (exclusive) "
+ "Actual: <2>");
+ });
+}
diff --git a/pkg/matcher/test/operator_matchers_test.dart b/pkg/matcher/test/operator_matchers_test.dart
new file mode 100644
index 0000000..fd9627a
--- /dev/null
+++ b/pkg/matcher/test/operator_matchers_test.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library matcher.operator_matchers_test;
+
+import 'package:matcher/matcher.dart';
+import 'package:unittest/unittest.dart' show test, group;
+
+import 'test_utils.dart';
+
+void main() {
+ initUtils();
+
+ test('anyOf', () {
+ shouldFail(0, anyOf([equals(1), equals(2)]),
+ "Expected: (<1> or <2>) Actual: <0>");
+ shouldPass(1, anyOf([equals(1), equals(2)]));
+ });
+
+ test('allOf', () {
+ shouldPass(1, allOf([lessThan(10), greaterThan(0)]));
+ shouldFail(-1, allOf([lessThan(10), greaterThan(0)]),
+ "Expected: (a value less than <10> and a value greater than <0>) "
+ "Actual: <-1> "
+ "Which: is not a value greater than <0>");
+ });
+}
diff --git a/pkg/matcher/test/string_matchers_test.dart b/pkg/matcher/test/string_matchers_test.dart
new file mode 100644
index 0000000..1303641
--- /dev/null
+++ b/pkg/matcher/test/string_matchers_test.dart
@@ -0,0 +1,94 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library matcher.string_matchers_test;
+
+import 'package:matcher/matcher.dart';
+import 'package:unittest/unittest.dart' show test, group;
+
+import 'test_utils.dart';
+
+void main() {
+ initUtils();
+
+ test('collapseWhitespace', () {
+ var source = '\t\r\n hello\t\r\n world\r\t \n';
+ expect(collapseWhitespace(source), 'hello world');
+ });
+
+ test('isEmpty', () {
+ shouldPass('', isEmpty);
+ shouldFail(null, isEmpty, "Expected: empty Actual: <null>");
+ shouldFail(0, isEmpty, "Expected: empty Actual: <0>");
+ shouldFail('a', isEmpty, "Expected: empty Actual: 'a'");
+ });
+
+ test('equalsIgnoringCase', () {
+ shouldPass('hello', equalsIgnoringCase('HELLO'));
+ shouldFail('hi', equalsIgnoringCase('HELLO'),
+ "Expected: 'HELLO' ignoring case Actual: 'hi'");
+ });
+
+ test('equalsIgnoringWhitespace', () {
+ shouldPass(' hello world ', equalsIgnoringWhitespace('hello world'));
+ shouldFail(' helloworld ', equalsIgnoringWhitespace('hello world'),
+ "Expected: 'hello world' ignoring whitespace "
+ "Actual: ' helloworld ' "
+ "Which: is 'helloworld' with whitespace compressed");
+ });
+
+ test('startsWith', () {
+ shouldPass('hello', startsWith(''));
+ shouldPass('hello', startsWith('hell'));
+ shouldPass('hello', startsWith('hello'));
+ shouldFail('hello', startsWith('hello '),
+ "Expected: a string starting with 'hello ' "
+ "Actual: 'hello'");
+ });
+
+ test('endsWith', () {
+ shouldPass('hello', endsWith(''));
+ shouldPass('hello', endsWith('lo'));
+ shouldPass('hello', endsWith('hello'));
+ shouldFail('hello', endsWith(' hello'),
+ "Expected: a string ending with ' hello' "
+ "Actual: 'hello'");
+ });
+
+ test('contains', () {
+ shouldPass('hello', contains(''));
+ shouldPass('hello', contains('h'));
+ shouldPass('hello', contains('o'));
+ shouldPass('hello', contains('hell'));
+ shouldPass('hello', contains('hello'));
+ shouldFail('hello', contains(' '),
+ "Expected: contains ' ' Actual: 'hello'");
+ });
+
+ test('stringContainsInOrder', () {
+ shouldPass('goodbye cruel world', stringContainsInOrder(['']));
+ shouldPass('goodbye cruel world', stringContainsInOrder(['goodbye']));
+ shouldPass('goodbye cruel world', stringContainsInOrder(['cruel']));
+ shouldPass('goodbye cruel world', stringContainsInOrder(['world']));
+ shouldPass('goodbye cruel world',
+ stringContainsInOrder(['good', 'bye', 'world']));
+ shouldPass('goodbye cruel world',
+ stringContainsInOrder(['goodbye', 'cruel']));
+ shouldPass('goodbye cruel world',
+ stringContainsInOrder(['cruel', 'world']));
+ shouldPass('goodbye cruel world',
+ stringContainsInOrder(['goodbye', 'cruel', 'world']));
+ shouldFail('goodbye cruel world',
+ stringContainsInOrder(['goo', 'cruel', 'bye']),
+ "Expected: a string containing 'goo', 'cruel', 'bye' in order "
+ "Actual: 'goodbye cruel world'");
+ });
+
+ test('matches', () {
+ shouldPass('c0d', matches('[a-z][0-9][a-z]'));
+ shouldPass('c0d', matches(new RegExp('[a-z][0-9][a-z]')));
+ shouldFail('cOd', matches('[a-z][0-9][a-z]'),
+ "Expected: match '[a-z][0-9][a-z]' Actual: 'cOd'");
+ });
+}
diff --git a/pkg/polymer/lib/auto_binding.dart b/pkg/polymer/lib/auto_binding.dart
new file mode 100644
index 0000000..44dc1ca
--- /dev/null
+++ b/pkg/polymer/lib/auto_binding.dart
@@ -0,0 +1,117 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library polymer.auto_binding;
+
+import 'dart:html';
+import 'package:polymer/polymer.dart';
+import 'package:template_binding/template_binding.dart';
+
+/**
+ * The `d-auto-binding` element extends the template element. It provides a
+ * quick and easy way to do data binding without the need to setup a binding
+ * delegate or use the [templateBind] call. Both data and event handlers can be
+ * bound using the [model].
+ *
+ * The `d-auto-binding` element acts just like a template that is bound to
+ * a model. It stamps its content in the dom adjacent to itself. When the
+ * content is stamped, the `template-bound` event is fired.
+ *
+ * Example:
+ *
+ * <template is="d-auto-binding">
+ * <div>Say something: <input value="{{value}}"></div>
+ * <div>You said: {{value}}</div>
+ * <button on-tap="{{buttonTap}}">Tap me!</button>
+ * </template>
+ * <script type="application/dart">
+ * import 'dart:html';
+ * import 'package:polymer/polymer.dart';
+ *
+ * main() {
+ * var template = document.querySelector('template');
+ * template.model = new MyModel();
+ * }
+ *
+ * class MyModel {
+ * String value = 'something';
+ * buttonTap() => console.log('tap!');
+ * }
+ * </script>
+ *
+ */
+// Dart note: renamed to d-auto-binding to avoid conflict with JS auto-binding.
+class AutoBindingElement extends TemplateElement with Polymer, Observable
+ implements TemplateBindExtension {
+
+ /// Make template_binding "extension methods" friendly.
+ /// Note that [NodeBindExtension] is already implemented by [Polymer].
+ TemplateBindExtension _self;
+
+ get model => _self.model;
+ set model(value) { _self.model = value; }
+
+ BindingDelegate get bindingDelegate => _self.bindingDelegate;
+ set bindingDelegate(BindingDelegate value) { _self.bindingDelegate = value; }
+
+ void clear() => _self.clear();
+
+ @override
+ PolymerExpressions get syntax => bindingDelegate;
+
+ AutoBindingElement.created() : super.created() {
+ polymerCreated();
+
+ _self = templateBindFallback(this);
+
+ bindingDelegate = makeSyntax();
+
+ // delay stamping until polymer-ready so that auto-binding is not
+ // required to load last.
+ Polymer.onReady.then((_) {
+ attributes['bind'] = '';
+ // we don't bother with an explicit signal here, we could ust a MO
+ // if necessary
+ async((_) {
+ // note: this will marshall *all* the elements in the parentNode
+ // rather than just stamped ones. We'd need to use createInstance
+ // to fix this or something else fancier.
+ marshalNodeReferences(parentNode);
+ // template stamping is asynchronous so stamping isn't complete
+ // by polymer-ready; fire an event so users can use stamped elements
+ fire('template-bound');
+ });
+ });
+ }
+
+ PolymerExpressions makeSyntax() => new _AutoBindingSyntax(this);
+
+ DocumentFragment createInstance([model, BindingDelegate delegate]) =>
+ _self.createInstance(model, delegate);
+}
+
+// Dart note: this is implemented a little differently to keep it in classic
+// OOP style. Instead of monkeypatching findController, we override
+// getEventHandler to do the right thing.
+class _AutoBindingSyntax extends PolymerExpressions {
+ final AutoBindingElement _node;
+
+ _AutoBindingSyntax(this._node) : super();
+
+ EventListener getEventHandler(controller, target, String method) => (e) {
+ if (controller == null || controller is! Polymer) controller = _node;
+
+ if (controller is Polymer) {
+ var args = [e, e.detail, e.currentTarget];
+
+ // Dart note: make sure we dispatch to the model, not the
+ // AutoBindingElement instance.
+ var obj = controller == _node ? _node.model : controller;
+ controller.dispatchMethod(obj, method, args);
+ } else {
+ throw new StateError('controller $controller is not a '
+ 'Dart polymer-element.');
+ }
+ };
+}
diff --git a/pkg/polymer/lib/src/events.dart b/pkg/polymer/lib/src/events.dart
new file mode 100644
index 0000000..503445f
--- /dev/null
+++ b/pkg/polymer/lib/src/events.dart
@@ -0,0 +1,149 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// Code from declaration/events.js
+part of polymer;
+
+/// An extension of [polymer_expressions.PolymerExpressions] that adds support
+/// for binding events using `on-eventName` using [PolymerEventBindings].
+// TODO(jmesserly): the JS layering is a bit odd, with polymer-dev implementing
+// events and polymer-expressions implementing everything else. I don't think
+// this separation is right in the long term, so we're using the same class name
+// until we can sort it out.
+class PolymerExpressions extends BindingDelegate with PolymerEventBindings {
+
+ /// A wrapper around polymer_expressions used to implement forwarding.
+ /// Ideally we would inherit from it, but mixins can't be applied to a type
+ /// that forwards to a superclass with a constructor that has optional or
+ /// named arguments.
+ final BindingDelegate _delegate;
+
+ PolymerExpressions({Map<String, Object> globals})
+ : _delegate = new polymer_expressions.PolymerExpressions(
+ globals: globals);
+
+ prepareBinding(String path, name, node) {
+ if (_hasEventPrefix(name)) {
+ return prepareEventBinding(path, name, node);
+ }
+ return _delegate.prepareBinding(path, name, node);
+ }
+
+ prepareInstanceModel(Element template) =>
+ _delegate.prepareInstanceModel(template);
+
+ prepareInstancePositionChanged(Element template) =>
+ _delegate.prepareInstancePositionChanged(template);
+}
+
+/// A mixin for a [BindingDelegate] to add Polymer event support.
+/// This is included in [PolymerExpressions].
+abstract class PolymerEventBindings {
+ /// Finds the event controller for this node.
+ Element findController(Node node) {
+ while (node.parentNode != null) {
+ if (node is Polymer && node.eventController != null) {
+ return node.eventController;
+ }
+ node = node.parentNode;
+ }
+ return node is ShadowRoot ? node.host : null;
+ }
+
+ EventListener getEventHandler(controller, target, String method) => (e) {
+ if (controller == null || controller is! Polymer) {
+ controller = findController(target);
+ }
+
+ if (controller is Polymer) {
+ var args = [e, e.detail, e.currentTarget];
+ controller.dispatchMethod(controller, method, args);
+ } else {
+ throw new StateError('controller $controller is not a '
+ 'Dart polymer-element.');
+ }
+ };
+
+ prepareEventBinding(String path, String name, Node node) {
+ if (!_hasEventPrefix(name)) return null;
+
+ var eventType = _removeEventPrefix(name);
+ var translated = _eventTranslations[eventType];
+ eventType = translated != null ? translated : eventType;
+
+ return (model, node, oneTime) {
+ var handler = getEventHandler(null, node, path);
+ node.addEventListener(eventType, handler);
+
+ if (oneTime) return null;
+ return new _EventBindable(node, eventType, handler, path);
+ };
+ }
+}
+
+
+class _EventBindable extends Bindable {
+ final Node _node;
+ final String _eventType;
+ final Function _handler;
+ final String _path;
+
+ _EventBindable(this._node, this._eventType, this._handler, this._path);
+
+ // TODO(rafaelw): This is really pointless work. Aside from the cost
+ // of these allocations, NodeBind is going to setAttribute back to its
+ // current value. Fixing this would mean changing the TemplateBinding
+ // binding delegate API.
+ get value => '{{ $_path }}';
+
+ open(callback) => value;
+
+ void close() => _node.removeEventListener(_eventType, _handler);
+}
+
+
+/// Attribute prefix used for declarative event handlers.
+const _EVENT_PREFIX = 'on-';
+
+/// Whether an attribute declares an event.
+bool _hasEventPrefix(String attr) => attr.startsWith(_EVENT_PREFIX);
+
+String _removeEventPrefix(String name) => name.substring(_EVENT_PREFIX.length);
+
+// Dart note: polymer.js calls this mixedCaseEventTypes. But we have additional
+// things that need translation due to renames.
+final _eventTranslations = const {
+ 'domfocusout': 'DOMFocusOut',
+ 'domfocusin': 'DOMFocusIn',
+ 'dommousescroll': 'DOMMouseScroll',
+
+ // Dart note: handle Dart-specific event names.
+ 'animationend': 'webkitAnimationEnd',
+ 'animationiteration': 'webkitAnimationIteration',
+ 'animationstart': 'webkitAnimationStart',
+ 'doubleclick': 'dblclick',
+ 'fullscreenchange': 'webkitfullscreenchange',
+ 'fullscreenerror': 'webkitfullscreenerror',
+ 'keyadded': 'webkitkeyadded',
+ 'keyerror': 'webkitkeyerror',
+ 'keymessage': 'webkitkeymessage',
+ 'needkey': 'webkitneedkey',
+ 'speechchange': 'webkitSpeechChange',
+};
+
+final _reverseEventTranslations = () {
+ final map = new Map<String, String>();
+ _eventTranslations.forEach((onName, eventType) {
+ map[eventType] = onName;
+ });
+ return map;
+}();
+
+// Dart note: we need this function because we have additional renames JS does
+// not have. The JS renames are simply case differences, whereas we have ones
+// like doubleclick -> dblclick and stripping the webkit prefix.
+String _eventNameFromType(String eventType) {
+ final result = _reverseEventTranslations[eventType];
+ return result != null ? result : eventType;
+}
diff --git a/pkg/polymer/lib/src/js/polymer/layout.html b/pkg/polymer/lib/src/js/polymer/layout.html
new file mode 100644
index 0000000..4e1284b
--- /dev/null
+++ b/pkg/polymer/lib/src/js/polymer/layout.html
@@ -0,0 +1,278 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+-->
+<style shim-shadowdom>
+/*******************************
+ Flex Layout
+*******************************/
+
+html /deep/ [layout][horizontal], html /deep/ [layout][vertical] {
+ display: -ms-flexbox;
+ display: -webkit-flex;
+ display: flex;
+}
+
+html /deep/ [layout][horizontal][inline], html /deep/ [layout][vertical][inline] {
+ display: -ms-inline-flexbox;
+ display: -webkit-inline-flex;
+ display: inline-flex;
+}
+
+html /deep/ [layout][horizontal] {
+ -ms-flex-direction: row;
+ -webkit-flex-direction: row;
+ flex-direction: row;
+}
+
+html /deep/ [layout][horizontal][reverse] {
+ -ms-flex-direction: row-reverse;
+ -webkit-flex-direction: row-reverse;
+ flex-direction: row-reverse;
+}
+
+html /deep/ [layout][vertical] {
+ -ms-flex-direction: column;
+ -webkit-flex-direction: column;
+ flex-direction: column;
+}
+
+html /deep/ [layout][vertical][reverse] {
+ -ms-flex-direction: column-reverse;
+ -webkit-flex-direction: column-reverse;
+ flex-direction: column-reverse;
+}
+
+html /deep/ [layout][wrap] {
+ -ms-flex-wrap: wrap;
+ -webkit-flex-wrap: wrap;
+ flex-wrap: wrap;
+}
+
+html /deep/ [layout][wrap-reverse] {
+ -ms-flex-wrap: wrap-reverse;
+ -webkit-flex-wrap: wrap-reverse;
+ flex-wrap: wrap-reverse;
+}
+
+html /deep/ [flex] {
+ -ms-flex: 1;
+ -webkit-flex: 1;
+ flex: 1;
+}
+
+html /deep/ [flex][auto] {
+ -ms-flex: 1 1 auto;
+ -webkit-flex: 1 1 auto;
+ flex: 1 1 auto;
+}
+
+html /deep/ [flex][none] {
+ -ms-flex: none;
+ -webkit-flex: none;
+ flex: none;
+}
+
+html /deep/ [flex][one] {
+ -ms-flex: 1;
+ -webkit-flex: 1;
+ flex: 1;
+}
+
+html /deep/ [flex][two] {
+ -ms-flex: 2;
+ -webkit-flex: 2;
+ flex: 2;
+}
+
+html /deep/ [flex][three] {
+ -ms-flex: 3;
+ -webkit-flex: 3;
+ flex: 3;
+}
+
+html /deep/ [flex][four] {
+ -ms-flex: 4;
+ -webkit-flex: 4;
+ flex: 4;
+}
+
+html /deep/ [flex][five] {
+ -ms-flex: 5;
+ -webkit-flex: 5;
+ flex: 5;
+}
+
+html /deep/ [flex][six] {
+ -ms-flex: 6;
+ -webkit-flex: 6;
+ flex: 6;
+}
+
+html /deep/ [flex][seven] {
+ -ms-flex: 7;
+ -webkit-flex: 7;
+ flex: 7;
+}
+
+html /deep/ [flex][eight] {
+ -ms-flex: 8;
+ -webkit-flex: 8;
+ flex: 8;
+}
+
+html /deep/ [flex][nine] {
+ -ms-flex: 9;
+ -webkit-flex: 9;
+ flex: 9;
+}
+
+html /deep/ [flex][ten] {
+ -ms-flex: 10;
+ -webkit-flex: 10;
+ flex: 10;
+}
+
+html /deep/ [flex][eleven] {
+ -ms-flex: 11;
+ -webkit-flex: 11;
+ flex: 11;
+}
+
+html /deep/ [flex][twelve] {
+ -ms-flex: 12;
+ -webkit-flex: 12;
+ flex: 12;
+}
+
+/* alignment in cross axis */
+
+html /deep/ [layout][start] {
+ -ms-flex-align: start;
+ -webkit-align-items: flex-start;
+ align-items: flex-start;
+}
+
+html /deep/ [layout][center] {
+ -ms-flex-align: center;
+ -webkit-align-items: center;
+ align-items: center;
+}
+
+html /deep/ [layout][end] {
+ -ms-flex-align: end;
+ -webkit-align-items: flex-end;
+ align-items: flex-end;
+}
+
+/* alignment in main axis */
+
+html /deep/ [layout][start-justified] {
+ -ms-flex-pack: start;
+ -webkit-justify-content: flex-start;
+ justify-content: flex-start;
+}
+
+html /deep/ [layout][center-justified] {
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+}
+
+html /deep/ [layout][end-justified] {
+ -ms-flex-pack: end;
+ -webkit-justify-content: flex-end;
+ justify-content: flex-end;
+}
+
+html /deep/ [layout][around-justified] {
+ -ms-flex-pack: around;
+ -webkit-justify-content: space-around;
+ justify-content: space-around;
+}
+
+html /deep/ [layout][justified] {
+ -ms-flex-pack: justify;
+ -webkit-justify-content: space-between;
+ justify-content: space-between;
+}
+
+/* self alignment */
+
+html /deep/ [self-start] {
+ -ms-align-self: flex-start;
+ -webkit-align-self: flex-start;
+ align-self: flex-start;
+}
+
+html /deep/ [self-center] {
+ -ms-align-self: center;
+ -webkit-align-self: center;
+ align-self: center;
+}
+
+html /deep/ [self-end] {
+ -ms-align-self: flex-end;
+ -webkit-align-self: flex-end;
+ align-self: flex-end;
+}
+
+html /deep/ [self-stretch] {
+ -ms-align-self: stretch;
+ -webkit-align-self: stretch;
+ align-self: stretch;
+}
+
+/*******************************
+ Other Layout
+*******************************/
+
+html /deep/ [block] {
+ display: block;
+}
+
+/* ie support for hidden */
+html /deep/ [hidden] {
+ display: none;
+}
+
+html /deep/ [relative] {
+ position: relative;
+}
+
+html /deep/ [fit] {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+}
+
+body[fullbleed] {
+ margin: 0;
+ height: 100vh;
+}
+
+/*******************************
+ Other
+*******************************/
+
+html /deep/ [segment], html /deep/ segment {
+ display: block;
+ position: relative;
+ -webkit-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ box-sizing: border-box;
+ margin: 1em 0.5em;
+ padding: 1em;
+ background-color: white;
+ -webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
+ box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
+ border-radius: 5px 5px 5px 5px;
+}
+
+</style>
\ No newline at end of file
diff --git a/pkg/polymer/test/auto_binding_test.dart b/pkg/polymer/test/auto_binding_test.dart
new file mode 100644
index 0000000..b563814
--- /dev/null
+++ b/pkg/polymer/test/auto_binding_test.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:html';
+import 'package:polymer/auto_binding.dart';
+import 'package:polymer/polymer.dart';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+
+class TestModel {
+ var greeting = 'Hi';
+ eventAction(e) {
+ e.detail.add('handled');
+ }
+}
+
+main() => initPolymer().run(() {
+ useHtmlConfiguration();
+
+ setUp(() => Polymer.onReady);
+
+ test('elements upgraded', () {
+ AutoBindingElement template = document.getElementById('one');
+ template.model = new TestModel();
+
+ var completer = new Completer();
+ var events = 0;
+ window.addEventListener('template-bound', (e) {
+ events++;
+ if (e.target.id == 'one') {
+ expect(e.target, template);
+
+ var t = template;
+ var h = t.$['h'];
+ expect(h.text, t.model.greeting, reason: 'binding applied');
+ var ce = t.fire('tap', onNode: h, detail: []);
+ expect(ce.detail, ['handled'], reason: 'element event handler fired');
+ }
+
+ if (events == 2) completer.complete();
+ });
+
+ return completer.future;
+ });
+});
diff --git a/pkg/polymer/test/auto_binding_test.html b/pkg/polymer/test/auto_binding_test.html
new file mode 100644
index 0000000..822d290
--- /dev/null
+++ b/pkg/polymer/test/auto_binding_test.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<!--
+Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+for details. All rights reserved. Use of this source code is governed by a
+BSD-style license that can be found in the LICENSE file.
+-->
+<html>
+ <head>
+ <!--polymer-test: this comment is needed for test_suite.dart-->
+ <title>auto-binding test</title>
+ <link rel="import" href="packages/polymer/polymer.html">
+ <script src="/root_dart/tools/testing/dart/test_controller.js"></script>
+ </head>
+ <body unresolved>
+
+ <div>top</div>
+
+ <template id="one" is="d-auto-binding">
+ <h2 id="h" on-tap="{{eventAction}}">{{greeting}}</h2>
+ </template>
+
+ <template id="two" is="d-auto-binding">
+ <div>Say something: <input value="{{value}}"></div>
+ <div>You said: {{value}}</div>
+ </template>
+
+ <div>bottom</div>
+
+ <script type="application/dart" src="auto_binding_test.dart"></script>
+
+ </body>
+</html>
diff --git a/runtime/bin/net/os_windows.c b/runtime/bin/net/os_windows.c
new file mode 100644
index 0000000..07ccdc5
--- /dev/null
+++ b/runtime/bin/net/os_windows.c
@@ -0,0 +1,11 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#if !defined(_MSC_VER)
+#error "This file should only be compiled under MSVC"
+#endif
+
+#if defined(_X86_)
+#include "../../../third_party/nss/nss/lib/freebl/mpi/mpi_x86_asm.c"
+#endif
diff --git a/runtime/bin/vmservice/client/deployed/web/packages/observatory/src/elements/class_tree.html b/runtime/bin/vmservice/client/deployed/web/packages/observatory/src/elements/class_tree.html
new file mode 100644
index 0000000..8ede1e6
--- /dev/null
+++ b/runtime/bin/vmservice/client/deployed/web/packages/observatory/src/elements/class_tree.html
@@ -0,0 +1,119 @@
+<link rel="import" href="nav_bar.html">
+<link rel="import" href="observatory_element.html">
+<link rel="import" href="class_ref.html">
+
+<polymer-element name="class-tree" extends="observatory-element">
+ <template>
+ <link rel="stylesheet" href="css/shared.css">
+ <style>
+ .table {
+ border-collapse: collapse!important;
+ width: 100%;
+ margin-bottom: 20px
+ }
+ .table thead > tr > th,
+ .table tbody > tr > th,
+ .table tfoot > tr > th,
+ .table thead > tr > td,
+ .table tbody > tr > td,
+ .table tfoot > tr > td {
+ padding: 8px;
+ vertical-align: top;
+ }
+ .table thead > tr > th {
+ vertical-align: bottom;
+ text-align: left;
+ border-bottom:2px solid #ddd;
+ }
+
+ tr:hover > td {
+ background-color: #FFF3E3;
+ }
+ .rowColor0 {
+ background-color: #FFE9CC;
+ }
+ .rowColor1 {
+ background-color: #FFDEB2;
+ }
+ .rowColor2 {
+ background-color: #FFD399;
+ }
+ .rowColor3 {
+ background-color: #FFC87F;
+ }
+ .rowColor4 {
+ background-color: #FFBD66;
+ }
+ .rowColor5 {
+ background-color: #FFB24C;
+ }
+ .rowColor6 {
+ background-color: #FFA733;
+ }
+ .rowColor7 {
+ background-color: #FF9C19;
+ }
+ .rowColor8 {
+ background-color: #FF9100;
+ }
+
+ .tooltip {
+ display: block;
+ position: absolute;
+ visibility: hidden;
+ opacity: 0;
+ transition: visibility 0s linear 0.5s;
+ transition: opacity .4s ease-in-out;
+ }
+
+ tr:hover .tooltip {
+ display: block;
+ position: absolute;
+ top: 100%;
+ right: 100%;
+ visibility: visible;
+ z-index: 999;
+ width: 400px;
+ color: #ffffff;
+ background-color: #0489c3;
+ border-top-right-radius: 8px;
+ border-top-left-radius: 8px;
+ border-bottom-right-radius: 8px;
+ border-bottom-left-radius: 8px;
+ transition: visibility 0s linear 0.5s;
+ transition: opacity .4s ease-in-out;
+ opacity: 1;
+ }
+
+ .white {
+ color: #ffffff;
+ }
+ </style>
+ <nav-bar>
+ <top-nav-menu last="{{ true }}"></top-nav-menu>
+ <nav-control></nav-control>
+ </nav-bar>
+ <div class="content-centered">
+ <h1>Class Hierarchy</h1>
+ <table id="tableTree" class="table">
+ <thead>
+ <tr>
+ <th>Class</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr template repeat="{{row in tree.rows }}">
+ <td on-click="{{toggleExpanded}}"
+ class="{{ coloring(row) }}"
+ style="{{ padding(row) }}">
+ <span id="expand" style="{{ row.expanderStyle }}">{{ row.expander }}</span>
+ <class-ref ref="{{ row.cls }}"></class-ref>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </template>
+</polymer-element>
+
+<script type="application/dart" src="class_tree.dart"></script>
diff --git a/runtime/bin/vmservice/client/lib/src/elements/class_tree.dart b/runtime/bin/vmservice/client/lib/src/elements/class_tree.dart
new file mode 100644
index 0000000..aeb29fa
--- /dev/null
+++ b/runtime/bin/vmservice/client/lib/src/elements/class_tree.dart
@@ -0,0 +1,113 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library class_tree_element;
+
+import 'observatory_element.dart';
+import 'dart:html';
+import 'package:logging/logging.dart';
+import 'package:observatory/app.dart';
+import 'package:observatory/service.dart';
+import 'package:polymer/polymer.dart';
+
+class ClassTreeRow extends TableTreeRow {
+ @reflectable final Isolate isolate;
+ @reflectable final Class cls;
+ ClassTreeRow(this.isolate, this.cls, ClassTreeRow parent) : super(parent) {
+ assert(isolate != null);
+ assert(cls != null);
+ }
+
+ void onShow() {
+ if (children.length > 0) {
+ // Child rows already created.
+ return;
+ }
+ for (var subClass in cls.children) {
+ if (subClass.isPatch) {
+ continue;
+ }
+ var row = new ClassTreeRow(isolate, subClass, this);
+ children.add(row);
+ }
+ }
+
+ void onHide() {
+ }
+
+ bool hasChildren() {
+ return cls.children.length > 0;
+ }
+}
+
+
+@CustomTag('class-tree')
+class ClassTreeElement extends ObservatoryElement {
+ @observable Isolate isolate;
+
+ TableTree tree;
+
+ ClassTreeElement.created() : super.created();
+
+ void enteredView() {
+ tree = new TableTree();
+ if (isolate != null) {
+ _update(isolate.objectClass);
+ }
+ }
+
+ isolateChanged(oldValue) {
+ isolate.getClassHierarchy().then((objectClass) {
+ _update(objectClass);
+ });
+ }
+
+ void _update(Class root) {
+ try {
+ var rootRow = new ClassTreeRow(isolate, root, null);
+ rootRow.children.add(new ClassTreeRow(isolate, root, rootRow));
+ tree.initialize(rootRow);
+ } catch (e, stackTrace) {
+ Logger.root.warning('_update', e, stackTrace);
+ }
+ // Check if we only have one node at the root and expand it.
+ if (tree.rows.length == 1) {
+ tree.toggle(0);
+ }
+ notifyPropertyChange(#tree, null, tree);
+ }
+
+ @observable String padding(TableTreeRow row) {
+ return 'padding-left: ${row.depth * 16}px;';
+ }
+
+ @observable String coloring(TableTreeRow row) {
+ const colors = const ['rowColor0', 'rowColor1', 'rowColor2', 'rowColor3',
+ 'rowColor4', 'rowColor5', 'rowColor6', 'rowColor7',
+ 'rowColor8'];
+ var index = (row.depth - 1) % colors.length;
+ return colors[index];
+ }
+
+ @observable void toggleExpanded(Event e, var detail, Element target) {
+ // We only want to expand a tree row if the target of the click is
+ // the table cell (passed in as target) or the span containing the
+ // expander symbol (#expand).
+ var eventTarget = e.target;
+ if ((eventTarget.id != 'expand') && (e.target != target)) {
+ // Target of click was not the expander span or the table cell.
+ return;
+ }
+ var row = target.parent;
+ if (row is TableRowElement) {
+ try {
+ // Subtract 1 to get 0 based indexing.
+ tree.toggle(row.rowIndex - 1);
+ } catch (e, stackTrace) {
+ Logger.root.warning('toggleExpanded', e, stackTrace);
+ }
+ }
+ }
+
+}
diff --git a/runtime/bin/vmservice/client/lib/src/elements/class_tree.html b/runtime/bin/vmservice/client/lib/src/elements/class_tree.html
new file mode 100644
index 0000000..8ede1e6
--- /dev/null
+++ b/runtime/bin/vmservice/client/lib/src/elements/class_tree.html
@@ -0,0 +1,119 @@
+<link rel="import" href="nav_bar.html">
+<link rel="import" href="observatory_element.html">
+<link rel="import" href="class_ref.html">
+
+<polymer-element name="class-tree" extends="observatory-element">
+ <template>
+ <link rel="stylesheet" href="css/shared.css">
+ <style>
+ .table {
+ border-collapse: collapse!important;
+ width: 100%;
+ margin-bottom: 20px
+ }
+ .table thead > tr > th,
+ .table tbody > tr > th,
+ .table tfoot > tr > th,
+ .table thead > tr > td,
+ .table tbody > tr > td,
+ .table tfoot > tr > td {
+ padding: 8px;
+ vertical-align: top;
+ }
+ .table thead > tr > th {
+ vertical-align: bottom;
+ text-align: left;
+ border-bottom:2px solid #ddd;
+ }
+
+ tr:hover > td {
+ background-color: #FFF3E3;
+ }
+ .rowColor0 {
+ background-color: #FFE9CC;
+ }
+ .rowColor1 {
+ background-color: #FFDEB2;
+ }
+ .rowColor2 {
+ background-color: #FFD399;
+ }
+ .rowColor3 {
+ background-color: #FFC87F;
+ }
+ .rowColor4 {
+ background-color: #FFBD66;
+ }
+ .rowColor5 {
+ background-color: #FFB24C;
+ }
+ .rowColor6 {
+ background-color: #FFA733;
+ }
+ .rowColor7 {
+ background-color: #FF9C19;
+ }
+ .rowColor8 {
+ background-color: #FF9100;
+ }
+
+ .tooltip {
+ display: block;
+ position: absolute;
+ visibility: hidden;
+ opacity: 0;
+ transition: visibility 0s linear 0.5s;
+ transition: opacity .4s ease-in-out;
+ }
+
+ tr:hover .tooltip {
+ display: block;
+ position: absolute;
+ top: 100%;
+ right: 100%;
+ visibility: visible;
+ z-index: 999;
+ width: 400px;
+ color: #ffffff;
+ background-color: #0489c3;
+ border-top-right-radius: 8px;
+ border-top-left-radius: 8px;
+ border-bottom-right-radius: 8px;
+ border-bottom-left-radius: 8px;
+ transition: visibility 0s linear 0.5s;
+ transition: opacity .4s ease-in-out;
+ opacity: 1;
+ }
+
+ .white {
+ color: #ffffff;
+ }
+ </style>
+ <nav-bar>
+ <top-nav-menu last="{{ true }}"></top-nav-menu>
+ <nav-control></nav-control>
+ </nav-bar>
+ <div class="content-centered">
+ <h1>Class Hierarchy</h1>
+ <table id="tableTree" class="table">
+ <thead>
+ <tr>
+ <th>Class</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr template repeat="{{row in tree.rows }}">
+ <td on-click="{{toggleExpanded}}"
+ class="{{ coloring(row) }}"
+ style="{{ padding(row) }}">
+ <span id="expand" style="{{ row.expanderStyle }}">{{ row.expander }}</span>
+ <class-ref ref="{{ row.cls }}"></class-ref>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </template>
+</polymer-element>
+
+<script type="application/dart" src="class_tree.dart"></script>
diff --git a/runtime/vm/code_generator.cc b/runtime/vm/code_generator.cc
index 417a905..72706ac 100644
--- a/runtime/vm/code_generator.cc
+++ b/runtime/vm/code_generator.cc
@@ -633,12 +633,10 @@
caller_code.SetStaticCallTargetCodeAt(caller_frame->pc(), target_code);
}
if (FLAG_trace_patching) {
- OS::PrintErr("PatchStaticCall: patching caller pc %#" Px ""
- " to '%s' new entry point %#" Px " (%s)\n",
+ OS::PrintErr("PatchStaticCall: patching from %#" Px " to '%s' %#" Px "\n",
caller_frame->pc(),
target_function.ToFullyQualifiedCString(),
- target_code.EntryPoint(),
- target_code.is_optimized() ? "optimized" : "unoptimized");
+ target_code.EntryPoint());
}
arguments.SetReturn(target_code);
}
@@ -1295,11 +1293,9 @@
caller_code.SetStaticCallTargetCodeAt(frame->pc(), current_target_code);
}
if (FLAG_trace_patching) {
- OS::PrintErr("FixCallersTarget: caller %#" Px " "
- "target '%s' %#" Px " -> %#" Px "\n",
+ OS::PrintErr("FixCallersTarget: patching from %#" Px " to '%s' %#" Px "\n",
frame->pc(),
target_function.ToFullyQualifiedCString(),
- target_code.EntryPoint(),
current_target_code.EntryPoint());
}
arguments.SetReturn(current_target_code);
diff --git a/runtime/vm/compiler.cc b/runtime/vm/compiler.cc
index b8805f8..da3feb1 100644
--- a/runtime/vm/compiler.cc
+++ b/runtime/vm/compiler.cc
@@ -59,7 +59,6 @@
"Enable compiler verification assertions");
DECLARE_FLAG(bool, trace_failed_optimization_attempts);
-DECLARE_FLAG(bool, trace_patching);
DECLARE_FLAG(bool, warn_on_javascript_compatibility);
DECLARE_FLAG(bool, warning_as_error);
@@ -557,12 +556,8 @@
if (optimized) {
if (osr_id == Isolate::kNoDeoptId) {
CodePatcher::PatchEntry(Code::Handle(function.CurrentCode()));
- if (FLAG_trace_compiler || FLAG_trace_patching) {
- if (FLAG_trace_compiler) {
- OS::Print(" ");
- }
- OS::Print("Patch unoptimized '%s' entry point %#" Px "\n",
- function.ToFullyQualifiedCString(),
+ if (FLAG_trace_compiler) {
+ OS::Print("--> patching entry %#" Px "\n",
Code::Handle(function.unoptimized_code()).EntryPoint());
}
}
@@ -580,10 +575,11 @@
ASSERT(CodePatcher::CodeIsPatchable(code));
}
if (parsed_function->HasDeferredPrefixes()) {
- ZoneGrowableArray<const LibraryPrefix*>* prefixes =
- parsed_function->deferred_prefixes();
- for (intptr_t i = 0; i < prefixes->length(); i++) {
- (*prefixes)[i]->RegisterDependentCode(code);
+ GrowableObjectArray* prefixes = parsed_function->DeferredPrefixes();
+ LibraryPrefix& prefix = LibraryPrefix::Handle();
+ for (intptr_t i = 0; i < prefixes->Length(); i++) {
+ prefix ^= prefixes->At(i);
+ prefix.RegisterDependentCode(code);
}
}
}
diff --git a/runtime/vm/flow_graph.cc b/runtime/vm/flow_graph.cc
index c0c74a6..afaffa1 100644
--- a/runtime/vm/flow_graph.cc
+++ b/runtime/vm/flow_graph.cc
@@ -42,8 +42,7 @@
use_far_branches_(false),
loop_headers_(NULL),
loop_invariant_loads_(NULL),
- guarded_fields_(builder.guarded_fields()),
- deferred_prefixes_(builder.deferred_prefixes()) {
+ guarded_fields_(builder.guarded_fields()) {
DiscoverBlocks();
}
@@ -64,21 +63,6 @@
}
-void FlowGraph::AddToDeferredPrefixes(
- ZoneGrowableArray<const LibraryPrefix*>* from) {
- ZoneGrowableArray<const LibraryPrefix*>* to = deferred_prefixes();
- for (intptr_t i = 0; i < from->length(); i++) {
- const LibraryPrefix* prefix = (*from)[i];
- for (intptr_t j = 0; j < to->length(); j++) {
- if ((*to)[j]->raw() == prefix->raw()) {
- return;
- }
- }
- to->Add(prefix);
- }
-}
-
-
bool FlowGraph::ShouldReorderBlocks(const Function& function,
bool is_optimized) {
return is_optimized && FLAG_reorder_basic_blocks && !function.is_intrinsic();
diff --git a/runtime/vm/flow_graph.h b/runtime/vm/flow_graph.h
index 1868d0a..9d2d42e 100644
--- a/runtime/vm/flow_graph.h
+++ b/runtime/vm/flow_graph.h
@@ -216,16 +216,11 @@
static void AddToGuardedFields(ZoneGrowableArray<const Field*>* array,
const Field* field);
- void AddToDeferredPrefixes(ZoneGrowableArray<const LibraryPrefix*>* from);
ZoneGrowableArray<const Field*>* guarded_fields() const {
return guarded_fields_;
}
- ZoneGrowableArray<const LibraryPrefix*>* deferred_prefixes() const {
- return deferred_prefixes_;
- }
-
private:
friend class IfConverter;
friend class BranchSimplifier;
@@ -305,7 +300,6 @@
ZoneGrowableArray<BlockEntryInstr*>* loop_headers_;
ZoneGrowableArray<BitVector*>* loop_invariant_loads_;
ZoneGrowableArray<const Field*>* guarded_fields_;
- ZoneGrowableArray<const LibraryPrefix*>* deferred_prefixes_;
};
diff --git a/runtime/vm/flow_graph_builder.h b/runtime/vm/flow_graph_builder.h
index 8467cb5..7632971 100644
--- a/runtime/vm/flow_graph_builder.h
+++ b/runtime/vm/flow_graph_builder.h
@@ -199,10 +199,6 @@
return guarded_fields_;
}
- ZoneGrowableArray<const LibraryPrefix*>* deferred_prefixes() const {
- return parsed_function_->deferred_prefixes();
- }
-
intptr_t temp_count() const { return temp_count_; }
intptr_t AllocateTemp() { return temp_count_++; }
void DeallocateTemps(intptr_t count) {
diff --git a/runtime/vm/flow_graph_inliner.cc b/runtime/vm/flow_graph_inliner.cc
index 11fedc1..b07c635 100644
--- a/runtime/vm/flow_graph_inliner.cc
+++ b/runtime/vm/flow_graph_inliner.cc
@@ -802,9 +802,6 @@
FlowGraph::AddToGuardedFields(caller_graph_->guarded_fields(),
(*callee_graph->guarded_fields())[i]);
}
- // When inlined, we add the deferred prefixes of the callee to the
- // caller's list of deferred prefixes.
- caller_graph()->AddToDeferredPrefixes(callee_graph->deferred_prefixes());
// We allocate a ZoneHandle for the unoptimized code so that it cannot be
// disconnected from its function during the rest of compilation.
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index cc02d9a..2f9559b 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -2523,13 +2523,6 @@
if (!CodePatcher::IsEntryPatched(code)) {
CodePatcher::PatchEntry(code);
}
- } else if (!function.HasCode() && (code.GetEntryPatchPc() != 0)) {
- // The code has already been disconnected, make it invalid. Do not
- // bother with OSR compiled code that has no valid entry-patch.
- ReportSwitchingCode(code);
- if (!CodePatcher::IsEntryPatched(code)) {
- CodePatcher::PatchEntry(code);
- }
}
}
}
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index c41e5c1..8667c20 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -135,12 +135,16 @@
void ParsedFunction::AddDeferredPrefix(const LibraryPrefix& prefix) {
ASSERT(prefix.is_deferred_load());
ASSERT(!prefix.is_loaded());
- for (intptr_t i = 0; i < deferred_prefixes_->length(); i++) {
- if ((*deferred_prefixes_)[i]->raw() == prefix.raw()) {
+ if (deferred_prefixes_ == NULL) {
+ deferred_prefixes_ =
+ &GrowableObjectArray::ZoneHandle(GrowableObjectArray::New());
+ }
+ for (intptr_t i = 0; i < deferred_prefixes_->Length(); i++) {
+ if (deferred_prefixes_->At(i) == prefix.raw()) {
return;
}
}
- deferred_prefixes_->Add(&LibraryPrefix::ZoneHandle(I, prefix.raw()));
+ deferred_prefixes_->Add(prefix);
}
diff --git a/runtime/vm/parser.h b/runtime/vm/parser.h
index 15de41f..f187032 100644
--- a/runtime/vm/parser.h
+++ b/runtime/vm/parser.h
@@ -47,7 +47,7 @@
saved_current_context_var_(NULL),
saved_entry_context_var_(NULL),
expression_temp_var_(NULL),
- deferred_prefixes_(new ZoneGrowableArray<const LibraryPrefix*>()),
+ deferred_prefixes_(NULL),
first_parameter_index_(0),
first_stack_local_index_(0),
num_copied_params_(0),
@@ -110,10 +110,8 @@
static LocalVariable* CreateExpressionTempVar(intptr_t token_pos);
LocalVariable* EnsureExpressionTemp();
- bool HasDeferredPrefixes() const { return deferred_prefixes_->length() != 0; }
- ZoneGrowableArray<const LibraryPrefix*>* deferred_prefixes() const {
- return deferred_prefixes_;
- }
+ bool HasDeferredPrefixes() const { return deferred_prefixes_ != NULL; }
+ GrowableObjectArray* DeferredPrefixes() const { return deferred_prefixes_; }
void AddDeferredPrefix(const LibraryPrefix& prefix);
int first_parameter_index() const { return first_parameter_index_; }
@@ -134,7 +132,7 @@
LocalVariable* saved_current_context_var_;
LocalVariable* saved_entry_context_var_;
LocalVariable* expression_temp_var_;
- ZoneGrowableArray<const LibraryPrefix*>* deferred_prefixes_;
+ GrowableObjectArray* deferred_prefixes_;
int first_parameter_index_;
int first_stack_local_index_;
diff --git a/sdk/lib/internal/lru.dart b/sdk/lib/internal/lru.dart
new file mode 100644
index 0000000..8f8a64a
--- /dev/null
+++ b/sdk/lib/internal/lru.dart
@@ -0,0 +1,125 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of dart._internal;
+
+class LRUAssociation<K,V> {
+ K key;
+ V value;
+ LRUAssociation previous;
+ LRUAssociation next;
+
+ void insertBetween(before, after) {
+ after.previous = this;
+ before.next = this;
+ this.next = after;
+ this.previous = before;
+ }
+
+ void remove() {
+ var after = next;
+ var before = previous;
+ after.previous = before;
+ before.next = after;
+ }
+}
+
+/**
+ * A map with a fixed capacity that evicts associations when capacity is reached
+ * on a least-recently-used basis. Implemented as an open addressing hash table
+ * with doubly-linked entries forming the LRU queue.
+ */
+class LRUMap<K,V> {
+ final LRUAssociation<K,V> _head;
+ final List _table;
+ final int _mask;
+ final int _capacity; // Max number of associations before we start evicting.
+ int _size = 0; // Current number of associations.
+
+ /**
+ * Create an LRUMap whose capacity is 75% of 2^shift.
+ */
+ LRUMap.withShift(int shift)
+ : this._mask = (1 << shift) - 1
+ , this._capacity = (1 << shift) * 3 ~/ 4
+ , this._table = new List(1 << shift)
+ , this._head = new LRUAssociation() {
+ // The scheme used here for handling collisions relies on there always
+ // being at least one empty slot.
+ if (shift < 1) throw new Exception("LRUMap requires a shift >= 1");
+ assert(_table.length > _capacity);
+ _head.insertBetween(_head, _head);
+ }
+
+ int _scanFor(K key) {
+ var start = key.hashCode & _mask;
+ var index = start;
+ do {
+ var assoc = _table[index];
+ if (null == assoc || assoc.key == key) {
+ return index;
+ }
+ index = (index + 1) & _mask;
+ } while (index != start);
+ // Should never happen because we start evicting associations before the
+ // table is full.
+ throw new Exception("Internal error: LRUMap table full");
+ }
+
+ void _fixCollisionsAfter(start) {
+ var assoc;
+ var index = (start + 1) & _mask;
+ while (null != (assoc = _table[index])) {
+ var newIndex = _scanFor(assoc.key);
+ if (newIndex != index) {
+ assert(_table[newIndex] == null);
+ _table[newIndex] = assoc;
+ _table[index] = null;
+ }
+ index = (index + 1) & _mask;
+ }
+ }
+
+ operator []=(K key, V value) {
+ int index = _scanFor(key);
+ var assoc = _table[index];
+ if (null != assoc) {
+ // Existing key, replace value.
+ assert(assoc.key == key);
+ assoc.value = value;
+ assoc.remove();
+ assoc.insertBetween(_head, _head.next);
+ } else {
+ // New key.
+ var newAssoc;
+ if (_size == _capacity) {
+ // Knock out the oldest association.
+ var lru = _head.previous;
+ lru.remove();
+ index = _scanFor(lru.key);
+ _table[index] = null;
+ _fixCollisionsAfter(index);
+ index = _scanFor(key);
+ newAssoc = lru; // Recycle the association.
+ } else {
+ newAssoc = new LRUAssociation();
+ _size++;
+ }
+ newAssoc.key = key;
+ newAssoc.value = value;
+ newAssoc.insertBetween(_head, _head.next);
+ _table[index] = newAssoc;
+ }
+ }
+
+ V operator [](K key) {
+ var index = _scanFor(key);
+ var assoc = _table[index];
+ if (null == assoc) return null;
+ // Move to front of LRU queue.
+ assoc.remove();
+ assoc.insertBetween(_head, _head.next);
+ return assoc.value;
+ }
+}
diff --git a/tests/lib/mirrors/lru_test.dart b/tests/lib/mirrors/lru_test.dart
new file mode 100644
index 0000000..141fc98
--- /dev/null
+++ b/tests/lib/mirrors/lru_test.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:mirrors";
+import "package:expect/expect.dart";
+
+newLRUMapWithShift(int shift) {
+ var lib = currentMirrorSystem().libraries[Uri.parse("dart:_internal")];
+ var cls = lib.declarations[#LRUMap];
+ return cls.newInstance(#withShift, [shift]).reflectee;
+}
+
+main() {
+ Expect.throws(() => newLRUMapWithShift(0), (e) => e is Exception);
+
+ for (int shift = 1; shift < 5; shift++) {
+ var map = newLRUMapWithShift(shift);
+ var capacity = (1 << shift) * 3 ~/ 4;
+ for (int value = 0; value < 100; value++) {
+ var key = "$value";
+ map[key] = value;
+ Expect.equals(value, map[key]);
+ }
+ for (int value = 0; value < 100 - capacity - 1; value++) {
+ var key = "$value";
+ Expect.equals(null, map[key]);
+ }
+ for (int value = 100 - capacity; value < 100; value++) {
+ var key = "$value";
+ Expect.equals(value, map[key]);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/VERSION b/tools/VERSION
index dbc0a4a..cd798b5c 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
MINOR 5
PATCH 0
PRERELEASE 4
-PRERELEASE_PATCH 3
+PRERELEASE_PATCH 4