Version 1.6.0-dev.1.0 svn merge -r 37722:37934 https://dart.googlecode.com/svn/branches/bleeding_edge trunk git-svn-id: http://dart.googlecode.com/svn/trunk@37935 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/lib/compiler/implementation/lib/web.dart b/lib/compiler/implementation/lib/web.dart new file mode 100644 index 0000000..a75db7b --- /dev/null +++ b/lib/compiler/implementation/lib/web.dart
@@ -0,0 +1,9 @@ +// 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("web"); + +String htmlEscape(String text) { + throw "Unimplemented: web::htmlEscape(String)."; +}
diff --git a/lib/compiler/implementation/lib/web.dartp b/lib/compiler/implementation/lib/web.dartp new file mode 100644 index 0000000..c3ba2ad --- /dev/null +++ b/lib/compiler/implementation/lib/web.dartp
@@ -0,0 +1,13 @@ +// 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. + +// Patch file for dart:web + +/*patch*/ String htmlEscape(String text) { + return text.replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll('"', """) + .replaceAll("'", "'"); // Different from original. +}
diff --git a/lib/dom/templates/html/dartium/factoryprovider__Elements.darttemplate b/lib/dom/templates/html/dartium/factoryprovider__Elements.darttemplate new file mode 100644 index 0000000..8fe27e5 --- /dev/null +++ b/lib/dom/templates/html/dartium/factoryprovider__Elements.darttemplate
@@ -0,0 +1,7 @@ +// 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. + +class _Elements { + +$!FACTORY_METHODS}
diff --git a/lib/dom/templates/html/dartium/impl_EventTarget.darttemplate b/lib/dom/templates/html/dartium/impl_EventTarget.darttemplate new file mode 100644 index 0000000..1b4a00d --- /dev/null +++ b/lib/dom/templates/html/dartium/impl_EventTarget.darttemplate
@@ -0,0 +1,106 @@ +// 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. + +class _EventsImpl implements Events { + // TODO(podivilov): add type. + final _ptr; + + final Map<String, EventListenerList> _listenerMap; + + _EventsImpl(this._ptr) : _listenerMap = <EventListenerList>{}; + + EventListenerList operator [](String type) { + return _listenerMap.putIfAbsent(type, + () => new _EventListenerListImpl(_ptr, type)); + } +} + +class _EventListenerWrapper { + final EventListener raw; + final Function wrapped; + final bool useCapture; + _EventListenerWrapper(this.raw, this.wrapped, this.useCapture); +} + +class _EventListenerListImpl implements EventListenerList { + // TODO(podivilov): add type. + final _ptr; + final String _type; + List<_EventListenerWrapper> _wrappers; + + _EventListenerListImpl(this._ptr, this._type) : + // TODO(jacobr): switch to <_EventListenerWrapper>[] when the VM allow it. + _wrappers = new List<_EventListenerWrapper>(); + + EventListenerList add(EventListener listener, [bool useCapture = false]) { + _add(listener, useCapture); + return this; + } + + EventListenerList remove(EventListener listener, [bool useCapture = false]) { + _remove(listener, useCapture); + return this; + } + + bool dispatch(Event evt) { + // TODO(jacobr): what is the correct behavior here. We could alternately + // force the event to have the expected type. + assert(evt.type == _type); + return _ptr.$dom_dispatchEvent(evt); + } + + void _add(EventListener listener, bool useCapture) { + _ptr.$dom_addEventListener(_type, + _findOrAddWrapper(listener, useCapture), + useCapture); + } + + void _remove(EventListener listener, bool useCapture) { + Function wrapper = _removeWrapper(listener, useCapture); + if (wrapper !== null) { + _ptr.$dom_removeEventListener(_type, wrapper, useCapture); + } + } + + Function _removeWrapper(EventListener listener, bool useCapture) { + if (_wrappers === null) { + return null; + } + for (int i = 0; i < _wrappers.length; i++) { + _EventListenerWrapper wrapper = _wrappers[i]; + if (wrapper.raw === listener && wrapper.useCapture == useCapture) { + // Order doesn't matter so we swap with the last element instead of + // performing a more expensive remove from the middle of the list. + if (i + 1 != _wrappers.length) { + _wrappers[i] = _wrappers.removeLast(); + } else { + _wrappers.removeLast(); + } + return wrapper.wrapped; + } + } + return null; + } + + Function _findOrAddWrapper(EventListener listener, bool useCapture) { + if (_wrappers === null) { + _wrappers = <_EventListenerWrapper>[]; + } else { + for (_EventListenerWrapper wrapper in _wrappers) { + if (wrapper.raw === listener && wrapper.useCapture == useCapture) { + return wrapper.wrapped; + } + } + } + final wrapped = (e) { listener(e); }; + _wrappers.add(new _EventListenerWrapper(listener, wrapped, useCapture)); + return wrapped; + } +} + +class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC { +/* +$!MEMBERS +*/ +}
diff --git a/lib/dom/templates/html/frog/factoryprovider__Elements.darttemplate b/lib/dom/templates/html/frog/factoryprovider__Elements.darttemplate new file mode 100644 index 0000000..8fe27e5 --- /dev/null +++ b/lib/dom/templates/html/frog/factoryprovider__Elements.darttemplate
@@ -0,0 +1,7 @@ +// 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. + +class _Elements { + +$!FACTORY_METHODS}
diff --git a/lib/dom/templates/html/frog/impl_EventTarget.darttemplate b/lib/dom/templates/html/frog/impl_EventTarget.darttemplate new file mode 100644 index 0000000..893f3d2 --- /dev/null +++ b/lib/dom/templates/html/frog/impl_EventTarget.darttemplate
@@ -0,0 +1,61 @@ +// 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. + +class _EventsImpl implements Events { + /* Raw event target. */ + // TODO(jacobr): it would be nice if we could specify this as + // _EventTargetImpl or EventTarget + final Dynamic _ptr; + + _EventsImpl(this._ptr); + + _EventListenerListImpl operator [](String type) { + return new _EventListenerListImpl(_ptr, type); + } +} + +class _EventListenerListImpl implements EventListenerList { + + // TODO(jacobr): make this _EventTargetImpl + final Dynamic _ptr; + final String _type; + + _EventListenerListImpl(this._ptr, this._type); + + // TODO(jacobr): implement equals. + + _EventListenerListImpl add(EventListener listener, + [bool useCapture = false]) { + _add(listener, useCapture); + return this; + } + + _EventListenerListImpl remove(EventListener listener, + [bool useCapture = false]) { + _remove(listener, useCapture); + return this; + } + + bool dispatch(Event evt) { + // TODO(jacobr): what is the correct behavior here. We could alternately + // force the event to have the expected type. + assert(evt.type == _type); + return _ptr.$dom_dispatchEvent(evt); + } + + void _add(EventListener listener, bool useCapture) { + _ptr.$dom_addEventListener(_type, listener, useCapture); + } + + void _remove(EventListener listener, bool useCapture) { + _ptr.$dom_removeEventListener(_type, listener, useCapture); + } +} + + +class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC { + + Events get on() => new _EventsImpl(this); +$!MEMBERS +}
diff --git a/pkg/analysis_server/lib/src/computers.dart b/pkg/analysis_server/lib/src/computers.dart new file mode 100644 index 0000000..6b954dc --- /dev/null +++ b/pkg/analysis_server/lib/src/computers.dart
@@ -0,0 +1,712 @@ +// 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 computers; + +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 [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); +} + + +/** + * A computer for navigation regions in a Dart [CompilationUnit]. + */ +class DartUnitNavigationComputer { + final CompilationUnit _unit; + + List<Map<String, Object>> _regions = []; + + 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) { + computer._addRegion_nodeStart_nodeStart(node, node.argumentList, node.staticElement); + 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/test/analysis_abstract_test.dart b/pkg/analysis_server/test/analysis_abstract_test.dart new file mode 100644 index 0000000..bc26d3e --- /dev/null +++ b/pkg/analysis_server/test/analysis_abstract_test.dart
@@ -0,0 +1,284 @@ +// 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'; + + +main() { +} + + +/** + * 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/analyzer_experimental/lib/src/services/runtime/coverage_impl.dart b/pkg/analyzer_experimental/lib/src/services/runtime/coverage_impl.dart new file mode 100644 index 0000000..df37f7b --- /dev/null +++ b/pkg/analyzer_experimental/lib/src/services/runtime/coverage_impl.dart
@@ -0,0 +1,157 @@ +// 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. + +/// A library for code coverage support for Dart. +library runtime.coverage_impl; + +import "dart:io"; + +import "package:logging/logging.dart" as log; +import "package:pathos/path.dart" as po; + +import 'package:analyzer_experimental/src/generated/source.dart' show Source; +import 'package:analyzer_experimental/src/generated/scanner.dart' show StringScanner; +import 'package:analyzer_experimental/src/generated/parser.dart' show Parser; +import 'package:analyzer_experimental/src/generated/ast.dart'; +import 'package:analyzer_experimental/src/generated/engine.dart' show RecordingErrorListener; + + +log.Logger logger = log.Logger.root; + +/// Abstract server that listens requests and serves files, may be rewriting them. +abstract class RewriteServer { + String _basePath; + RewriteServer(this._basePath); + void start() { + HttpServer.bind("127.0.0.1", 3445).then((HttpServer server) { + logger.info('RewriteServer is listening at: ${server.port}.'); + server.listen((HttpRequest request) { + var response = request.response; + // Prepare path. + var path = _basePath + '/' + request.uri.path; + path = po.normalize(path); + logger.info('[$path] Requested.'); + // May be serve using just path. + { + String content = rewritePathContent(path); + if (content != null) { + logger.info('[$path] Request served by path.'); + response.write(content); + response.close(); + return; + } + } + // Serve from file. + logger.info('[$path] Serving file.'); + var file = new File(path); + file.exists().then((bool found) { + if (found) { + logger.finest('[$path] Found file.'); + file.readAsString().then((String content) { + logger.finest('[$path] Got file content.'); + var sw = new Stopwatch(); + sw.start(); + try { + content = rewriteFileContent(path, content); + } finally { + sw.stop(); + logger.fine('[$path] Rewritten in ${sw.elapsedMilliseconds} ms.'); + } + response.write(content); + response.close(); + }); + } else { + logger.severe('[$path] File not found.'); + response.statusCode = HttpStatus.NOT_FOUND; + response.close(); + } + }); + }); + }); + } + + /// Subclasses implement this method to rewrite the provided [code] of the file with [path]. + /// Returns some content or `null` if file content should be requested. + String rewritePathContent(String path); + + /// Subclasses implement this method to rewrite the provided [code] of the file with [path]. + String rewriteFileContent(String path, String code); +} + +/// Server that rewrites Dart code so that it reports execution of statements and other nodes. +class CoverageServer extends RewriteServer { + CoverageServer(String basePath) : super(basePath); + + String rewritePathContent(String path) { + if (path.endsWith('__coverage_impl.dart')) { + String implPath = po.joinAll([ + po.dirname(new Options().script), + '..', 'lib', 'src', 'services', 'runtime', 'coverage_lib.dart']); + return new File(implPath).readAsStringSync(); + } + return null; + } + + String rewriteFileContent(String path, String code) { + if (po.extension(path).toLowerCase() != '.dart') return code; + if (path.contains('packages')) return code; + var unit = _parseCode(code); + var injector = new StringInjector(code); + // Inject coverage library import. + var directives = unit.directives; + if (directives.isNotEmpty && directives[0] is LibraryDirective) { + injector.inject(directives[0].end, 'import "__coverage_impl.dart" as __cc;'); + } else { + throw new Exception('Only single library coverage is implemented.'); + } + // Insert touch() invocations. + unit.accept(new InsertTouchInvocationsVisitor(injector)); + // Done. + code = injector.code; + logger.finest('[$path] Rewritten content\n$code'); + return code; + } + + CompilationUnit _parseCode(String code) { + var source = null; + var errorListener = new RecordingErrorListener(); + var parser = new Parser(source, errorListener); + var scanner = new StringScanner(source, code, errorListener); + var token = scanner.tokenize(); + return parser.parseCompilationUnit(token); + } +} + +/// The visitor that inserts `touch` method invocations. +class InsertTouchInvocationsVisitor extends GeneralizingASTVisitor { + StringInjector injector; + InsertTouchInvocationsVisitor(this.injector); + visitStatement(Statement node) { + super.visitStatement(node); + var offset = node.end; + if (node is Block) { + offset--; + } + if (node is Block && node.parent is BlockFunctionBody) return null; + injector.inject(offset, '__cc.touch(${node.offset});'); + return null; + } +} + +/// Helper for injecting fragments into some existing [String]. +class StringInjector { + String code; + int _lastOffset = -1; + int _delta = 0; + StringInjector(this.code); + void inject(int offset, String fragment) { + if (offset < _lastOffset) { + throw new ArgumentError('Only forward inserts are supported, was $_lastOffset given $offset'); + } + _lastOffset = offset; + offset += _delta; + code = code.substring(0, offset) + fragment + code.substring(offset); + _delta += fragment.length; + } +} \ No newline at end of file
diff --git a/pkg/analyzer_experimental/lib/src/services/runtime/coverage_lib.dart b/pkg/analyzer_experimental/lib/src/services/runtime/coverage_lib.dart new file mode 100644 index 0000000..945b38f --- /dev/null +++ b/pkg/analyzer_experimental/lib/src/services/runtime/coverage_lib.dart
@@ -0,0 +1,11 @@ +// 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. + +/// This library is injected into the applications under coverage. +library coverage_lib; + +/// Notifies that the object with the given [id] - statement, token, etc was executed. +touch(int id) { + print('touch: $id'); +}
diff --git a/pkg/analyzer_experimental/lib/src/utils.dart b/pkg/analyzer_experimental/lib/src/utils.dart new file mode 100644 index 0000000..2b5053e --- /dev/null +++ b/pkg/analyzer_experimental/lib/src/utils.dart
@@ -0,0 +1,22 @@ +// 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. +library utils; + +import 'dart:io'; + +import 'package:pathos/path.dart' as pathos; + +/// Converts a local path string to a `file:` [Uri]. +Uri pathToFileUri(String pathString) { + pathString = pathos.absolute(pathString); + if (Platform.operatingSystem != 'windows') { + return Uri.parse('file://$pathString'); + } else if (pathos.rootPrefix(pathString).startsWith('\\\\')) { + // Network paths become "file://hostname/path/to/file". + return Uri.parse('file:${pathString.replaceAll("\\", "/")}'); + } else { + // Drive-letter paths become "file:///C:/path/to/file". + return Uri.parse('file:///${pathString.replaceAll("\\", "/")}'); + } +}
diff --git a/pkg/docgen/bin/generate_all_docs.sh b/pkg/docgen/bin/generate_all_docs.sh new file mode 100755 index 0000000..8d5d82c --- /dev/null +++ b/pkg/docgen/bin/generate_all_docs.sh
@@ -0,0 +1,6 @@ +# A simple shell script to generate all docs for the sdk and pkg directories +# into the docs folder in this directory. +# TODO(alanknight): This should get subsumed into the python scripts +dart --package-root=$DART_SDK/../packages/ docgen.dart --parse-sdk --json +dart --old_gen_heap_size=1024 --package-root=$DART_SDK/../packages/ docgen.dart \ + --package-root=$DART_SDK/../packages/ --append --json $DART_SDK/../../../pkg
diff --git a/pkg/matcher/lib/src/utils.dart b/pkg/matcher/lib/src/utils.dart new file mode 100644 index 0000000..c2182c2 --- /dev/null +++ b/pkg/matcher/lib/src/utils.dart
@@ -0,0 +1,69 @@ +// 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. + +library matcher.utils; + +/** + * Returns the name of the type of [x], or "Unknown" if the type name can't be + * determined. + */ +String typeName(x) { + // dart2js blows up on some objects (e.g. window.navigator). + // So we play safe here. + try { + if (x == null) return "null"; + var type = x.runtimeType.toString(); + // TODO(nweiz): if the object's type is private, find a public superclass to + // display once there's a portable API to do that. + return type.startsWith("_") ? "?" : type; + } catch (e) { + return "?"; + } +} + +/** + * Returns [source] with any control characters replaced by their escape + * sequences. + * + * This doesn't add quotes to the string, but it does escape single quote + * characters so that single quotes can be applied externally. + */ +String escapeString(String source) => + source.split("").map(_escapeChar).join(""); + +/** Return the escaped form of a character [ch]. */ +String _escapeChar(String ch) { + if (ch == "'") + return "\\'"; + else if (ch == '\n') + return '\\n'; + else if (ch == '\r') + return '\\r'; + else if (ch == '\t') + return '\\t'; + else + return ch; +} + +/** Indent each line in [str] by two spaces. */ +String indent(String str) => + str.replaceAll(new RegExp("^", multiLine: true), " "); + +/** A pair of values. */ +class Pair<E, F> { + E first; + F last; + + Pair(this.first, this.last); + + String toString() => '($first, $last)'; + + bool operator ==(other) { + if (other is! Pair) return false; + return other.first == first && other.last == last; + } + + int get hashCode => first.hashCode ^ last.hashCode; +} +
diff --git a/pkg/matcher/test/matchers_test.dart b/pkg/matcher/test/matchers_test.dart new file mode 100644 index 0000000..e74d048 --- /dev/null +++ b/pkg/matcher/test/matchers_test.dart
@@ -0,0 +1,803 @@ +// 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.test; + +import 'dart:async'; +import 'dart:collection'; + +import 'package:matcher/matcher.dart'; +import 'package:unittest/unittest.dart' show test, group; + +import 'test_utils.dart'; + +void main() { + + initUtils(); + + // Core matchers + + group('Core matchers', () { + + 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('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); + }); + }); + + group('Numeric Matchers', () { + + 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>"); + }); + }); + + group('String Matchers', () { + + 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'"); + }); + }); + + group('Iterable Matchers', () { + + 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"); + }); + }); + + group('Map Matchers', () { + + test('isEmpty', () { + var a = new Map(); + shouldPass({}, isEmpty); + shouldPass(a, isEmpty); + a['foo'] = 'bar'; + shouldFail(a, isEmpty, "Expected: empty " + "Actual: {'foo': 'bar'}"); + }); + + test('equals', () { + var a = new Map(); + a['foo'] = 'bar'; + var b = new Map(); + b['foo'] = 'bar'; + var c = new Map(); + c['bar'] = 'foo'; + shouldPass(a, equals(b)); + shouldFail(b, equals(c), + "Expected: {'bar': 'foo'} " + "Actual: {'foo': 'bar'} " + "Which: is missing map key 'bar'"); + }); + + test('equals with different lengths', () { + var a = new LinkedHashMap(); + a['foo'] = 'bar'; + var b = new LinkedHashMap(); + b['foo'] = 'bar'; + b['bar'] = 'foo'; + var c = new LinkedHashMap(); + c['bar'] = 'foo'; + c['barrista'] = 'caffeine'; + shouldFail(a, equals(b), + "Expected: {'foo': 'bar', 'bar': 'foo'} " + "Actual: {'foo': 'bar'} " + "Which: has different length and is missing map key 'bar'"); + shouldFail(b, equals(a), + "Expected: {'foo': 'bar'} " + "Actual: {'foo': 'bar', 'bar': 'foo'} " + "Which: has different length and has extra map key 'bar'"); + shouldFail(b, equals(c), + "Expected: {'bar': 'foo', 'barrista': 'caffeine'} " + "Actual: {'foo': 'bar', 'bar': 'foo'} " + "Which: is missing map key 'barrista'"); + shouldFail(c, equals(b), + "Expected: {'foo': 'bar', 'bar': 'foo'} " + "Actual: {'bar': 'foo', 'barrista': 'caffeine'} " + "Which: is missing map key 'foo'"); + shouldFail(a, equals(c), + "Expected: {'bar': 'foo', 'barrista': 'caffeine'} " + "Actual: {'foo': 'bar'} " + "Which: has different length and is missing map key 'bar'"); + shouldFail(c, equals(a), + "Expected: {'foo': 'bar'} " + "Actual: {'bar': 'foo', 'barrista': 'caffeine'} " + "Which: has different length and is missing map key 'foo'"); + }); + + test('equals with matcher value', () { + var a = new Map(); + a['foo'] = 'bar'; + shouldPass(a, equals({'foo': startsWith('ba')})); + shouldFail(a, 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 ['foo']"); + }); + + test('contains', () { + var a = new Map(); + a['foo'] = 'bar'; + var b = new Map(); + shouldPass(a, contains('foo')); + shouldFail(b, contains('foo'), + "Expected: contains 'foo' Actual: {}"); + shouldFail(10, contains('foo'), + "Expected: contains 'foo' Actual: <10> " + "Which: is not a string, map or iterable"); + }); + + test('containsValue', () { + var a = new Map(); + a['foo'] = 'bar'; + shouldPass(a, containsValue('bar')); + shouldFail(a, containsValue('ba'), + "Expected: contains value 'ba' " + "Actual: {'foo': 'bar'}"); + }); + + test('containsPair', () { + var a = new Map(); + a['foo'] = 'bar'; + shouldPass(a, containsPair('foo', 'bar')); + shouldFail(a, containsPair('foo', 'ba'), + "Expected: contains pair 'foo' => 'ba' " + "Actual: {'foo': 'bar'} " + "Which: is different. Both strings start the same, but " + "the given value also has the following trailing characters: r"); + shouldFail(a, containsPair('fo', 'bar'), + "Expected: contains pair 'fo' => 'bar' " + "Actual: {'foo': 'bar'} " + "Which: doesn't contain key 'fo'"); + }); + + test('hasLength', () { + var a = new Map(); + a['foo'] = 'bar'; + var b = new Map(); + shouldPass(a, hasLength(1)); + shouldFail(b, hasLength(1), + "Expected: an object with length of <1> " + "Actual: {} " + "Which: has length of <0>"); + }); + }); + + group('Operator Matchers', () { + + 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>"); + }); + }); + + group('Future Matchers', () { + + 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); + }); + }); + + 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', () { + // TODO(gram): extend this to more types; for now this is just + // the types being added in this CL. + + test('throwsCyclicInitializationError', () { + expect(() => _Bicycle.foo, throwsCyclicInitializationError); + }); + + test('throwsAbstractClassInstantiationError', () { + expect(() => new _AbstractClass(), throwsAbstractClassInstantiationError); + }); + + test('throwsConcurrentModificationError', () { + expect(() { + var a = { 'foo': 'bar' }; + for (var k in a.keys) { + a.remove(k); + } + }, throwsConcurrentModificationError); + }); + + test('throwsNullThrownError', () { + expect(() => throw null, throwsNullThrownError); + }); + + test('throwsFallThroughError', () { + expect(() { + var a = 0; + switch (a) { + case 0: + a += 1; + case 1: + return; + } + }, throwsFallThroughError); + }); + }); +} + +class _Bicycle { + static final foo = bar(); + + static bar() { + return foo + 1; + } +} + +abstract class _AbstractClass { +}
diff --git a/runtime/bin/vmservice/client/lib/src/elements/response_viewer.dart b/runtime/bin/vmservice/client/lib/src/elements/response_viewer.dart new file mode 100644 index 0000000..a541b35 --- /dev/null +++ b/runtime/bin/vmservice/client/lib/src/elements/response_viewer.dart
@@ -0,0 +1,15 @@ +// 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. + +library response_viewer_element; + +import 'observatory_element.dart'; +import 'package:observatory/app.dart'; +import 'package:polymer/polymer.dart'; + +@CustomTag('response-viewer') +class ResponseViewerElement extends ObservatoryElement { + @published ObservatoryApplication app; + ResponseViewerElement.created() : super.created(); +}
diff --git a/runtime/bin/vmservice/client/lib/src/elements/response_viewer.html b/runtime/bin/vmservice/client/lib/src/elements/response_viewer.html new file mode 100644 index 0000000..230120a --- /dev/null +++ b/runtime/bin/vmservice/client/lib/src/elements/response_viewer.html
@@ -0,0 +1,10 @@ +<head> + <link rel="import" href="observatory_element.html"> + <link rel="import" href="service_view.html"> +</head> +<polymer-element name="response-viewer" extends="observatory-element"> + <template> + <service-view object="{{ app.response }}"></service-view> + </template> + <script type="application/dart" src="response_viewer.dart"></script> +</polymer-element> \ No newline at end of file
diff --git a/site/try/create_manifest.sh b/site/try/create_manifest.sh old mode 100644 new mode 100755
diff --git a/tests/html/path_observer_test.dart b/tests/html/path_observer_test.dart new file mode 100644 index 0000000..d6db89b --- /dev/null +++ b/tests/html/path_observer_test.dart
@@ -0,0 +1,255 @@ +// 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. + +import 'dart:html' show PathObserver; +import 'package:unittest/html_config.dart'; +import 'package:mdv_observe/mdv_observe.dart'; +import 'package:unittest/unittest.dart'; + +// This file contains code ported from: +// https://github.com/rafaelw/ChangeSummary/blob/master/tests/test.js + +main() { + useHtmlConfiguration(); + group('PathObserver', observePathTests); +} + +observePath(obj, path) => new PathObserver(obj, path); + +sym(x) => new Symbol(x); + +toSymbolMap(Map map) { + var result = new ObservableMap.linked(); + map.forEach((key, value) { + if (value is Map) value = toSymbolMap(value); + result[new Symbol(key)] = value; + }); + return result; +} + +observePathTests() { + + test('Degenerate Values', () { + expect(observePath(null, '').value, null); + expect(observePath(123, '').value, 123); + expect(observePath(123, 'foo.bar.baz').value, null); + + // shouldn't throw: + observePath(123, '').values.listen((_) {}).cancel(); + observePath(null, '').value = null; + observePath(123, '').value = 42; + observePath(123, 'foo.bar.baz').value = 42; + + var foo = {}; + expect(observePath(foo, '').value, foo); + + foo = new Object(); + expect(observePath(foo, '').value, foo); + + expect(observePath(foo, 'a/3!').value, null); + }); + + test('get value at path ObservableBox', () { + var obj = new ObservableBox(new ObservableBox(new ObservableBox(1))); + + expect(observePath(obj, '').value, obj); + expect(observePath(obj, 'value').value, obj.value); + expect(observePath(obj, 'value.value').value, obj.value.value); + expect(observePath(obj, 'value.value.value').value, 1); + + obj.value.value.value = 2; + expect(observePath(obj, 'value.value.value').value, 2); + + obj.value.value = new ObservableBox(3); + expect(observePath(obj, 'value.value.value').value, 3); + + obj.value = new ObservableBox(4); + expect(observePath(obj, 'value.value.value').value, null); + expect(observePath(obj, 'value.value').value, 4); + }); + + + test('get value at path ObservableMap', () { + var obj = toSymbolMap({'a': {'b': {'c': 1}}}); + + expect(observePath(obj, '').value, obj); + expect(observePath(obj, 'a').value, obj[sym('a')]); + expect(observePath(obj, 'a.b').value, obj[sym('a')][sym('b')]); + expect(observePath(obj, 'a.b.c').value, 1); + + obj[sym('a')][sym('b')][sym('c')] = 2; + expect(observePath(obj, 'a.b.c').value, 2); + + obj[sym('a')][sym('b')] = toSymbolMap({'c': 3}); + expect(observePath(obj, 'a.b.c').value, 3); + + obj[sym('a')] = toSymbolMap({'b': 4}); + expect(observePath(obj, 'a.b.c').value, null); + expect(observePath(obj, 'a.b').value, 4); + }); + + test('set value at path', () { + var obj = toSymbolMap({}); + observePath(obj, 'foo').value = 3; + expect(obj[sym('foo')], 3); + + var bar = toSymbolMap({ 'baz': 3 }); + observePath(obj, 'bar').value = bar; + expect(obj[sym('bar')], bar); + + observePath(obj, 'bar.baz.bat').value = 'not here'; + expect(observePath(obj, 'bar.baz.bat').value, null); + }); + + test('set value back to same', () { + var obj = toSymbolMap({}); + var path = observePath(obj, 'foo'); + var values = []; + path.values.listen((v) { values.add(v); }); + + path.value = 3; + expect(obj[sym('foo')], 3); + expect(path.value, 3); + + observePath(obj, 'foo').value = 2; + deliverChangeRecords(); + expect(path.value, 2); + expect(observePath(obj, 'foo').value, 2); + + observePath(obj, 'foo').value = 3; + deliverChangeRecords(); + expect(path.value, 3); + + deliverChangeRecords(); + expect(values, [2, 3]); + }); + + test('Observe and Unobserve - Paths', () { + var arr = toSymbolMap({}); + + arr[sym('foo')] = 'bar'; + var fooValues = []; + var fooPath = observePath(arr, 'foo'); + var fooSub = fooPath.values.listen((v) { + fooValues.add(v); + }); + arr[sym('foo')] = 'baz'; + arr[sym('bat')] = 'bag'; + var batValues = []; + var batPath = observePath(arr, 'bat'); + var batSub = batPath.values.listen((v) { + batValues.add(v); + }); + + deliverChangeRecords(); + expect(fooValues, ['baz']); + expect(batValues, []); + + arr[sym('foo')] = 'bar'; + fooSub.cancel(); + arr[sym('bat')] = 'boo'; + batSub.cancel(); + arr[sym('bat')] = 'boot'; + + deliverChangeRecords(); + expect(fooValues, ['baz']); + expect(batValues, []); + }); + + test('Path Value With Indices', () { + var model = toObservable([]); + observePath(model, '0').values.listen(expectAsync1((v) { + expect(v, 123); + })); + model.add(123); + }); + + test('Path Observation', () { + var model = new TestModel(const Symbol('a'), + new TestModel(const Symbol('b'), + new TestModel(const Symbol('c'), 'hello, world'))); + + var path = observePath(model, 'a.b.c'); + var lastValue = null; + var sub = path.values.listen((v) { lastValue = v; }); + + model.value.value.value = 'hello, mom'; + + expect(lastValue, null); + deliverChangeRecords(); + expect(lastValue, 'hello, mom'); + + model.value.value = new TestModel(const Symbol('c'), 'hello, dad'); + deliverChangeRecords(); + expect(lastValue, 'hello, dad'); + + model.value = new TestModel(const Symbol('b'), + new TestModel(const Symbol('c'), 'hello, you')); + deliverChangeRecords(); + expect(lastValue, 'hello, you'); + + model.value.value = 1; + deliverChangeRecords(); + expect(lastValue, null); + + // Stop observing + sub.cancel(); + + model.value.value = new TestModel(const Symbol('c'), + 'hello, back again -- but not observing'); + deliverChangeRecords(); + expect(lastValue, null); + + // Resume observing + sub = path.values.listen((v) { lastValue = v; }); + + model.value.value.value = 'hello. Back for reals'; + deliverChangeRecords(); + expect(lastValue, 'hello. Back for reals'); + }); + + test('observe map', () { + var model = toSymbolMap({'a': 1}); + var path = observePath(model, 'a'); + + var values = [path.value]; + var sub = path.values.listen((v) { values.add(v); }); + expect(values, [1]); + + model[sym('a')] = 2; + deliverChangeRecords(); + expect(values, [1, 2]); + + sub.cancel(); + model[sym('a')] = 3; + deliverChangeRecords(); + expect(values, [1, 2]); + }); +} + +class TestModel extends ObservableBase { + final Symbol fieldName; + var _value; + + TestModel(this.fieldName, [initialValue]) : _value = initialValue; + + get value => _value; + + void set value(newValue) { + _value = notifyPropertyChange(fieldName, _value, newValue); + } + + getValueWorkaround(key) { + if (key == fieldName) return value; + return null; + } + void setValueWorkaround(key, newValue) { + if (key == fieldName) value = newValue; + } + + toString() => '#<$runtimeType $fieldName: $_value>'; +} + +_record(key, oldValue, newValue, [kind = ChangeRecord.FIELD]) => + new ChangeRecord(key, oldValue, newValue, kind: kind);
diff --git a/tests/language/reflect_core_vm_test.dart b/tests/language/reflect_core_vm_test.dart new file mode 100644 index 0000000..d6e52f1 --- /dev/null +++ b/tests/language/reflect_core_vm_test.dart
@@ -0,0 +1,20 @@ +// 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. + +// Test reflection of private functions in core classes. + +import "package:expect/expect.dart"; +import "dart:mirrors"; + +main() { + var s = "string"; + var im = reflect(s); + try { + im.invoke(const Symbol("_setAt"), [0, 65]); + Expect.isTrue(false); // Unreachable. + } catch (e) { + Expect.equals(true, e is NoSuchMethodError); + } +} +
diff --git a/tools/VERSION b/tools/VERSION index ca078a9..fc18678 100644 --- a/tools/VERSION +++ b/tools/VERSION
@@ -23,9 +23,9 @@ # * Making cherry-picks to stable channel # - increase PATCH by 1 # -CHANNEL be +CHANNEL dev MAJOR 1 MINOR 6 PATCH 0 -PRERELEASE 0 -PRERELEASE_PATCH 0 +PRERELEASE 1 +PRERELEASE_PATCH 1
diff --git a/tools/dartium/download_shellscript_template.bat b/tools/dartium/download_shellscript_template.bat index bf81832..d41951d 100644 --- a/tools/dartium/download_shellscript_template.bat +++ b/tools/dartium/download_shellscript_template.bat
@@ -1,16 +1,16 @@ -@REM Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -@REM for details. All rights reserved. Use of this source code is governed by a -@REM BSD-style license that can be found in the LICENSE file. - -@REM This script will download VAR_DOWNLOAD_URL to VAR_DESTINATION in the -@REM current working directory. - -@echo off - -set "CHROMIUM_DIR=%~dp0" -set "SDK_BIN=%CHROMIUM_DIR%\..\dart-sdk\bin" - -set "DART=%SDK_BIN%\dart.exe" -set "DOWNLOAD_SCRIPT=%CHROMIUM_DIR%\download_file.dart" - -"%DART%" "%DOWNLOAD_SCRIPT%" "VAR_DOWNLOAD_URL" "VAR_DESTINATION" +@REM Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +@REM for details. All rights reserved. Use of this source code is governed by a +@REM BSD-style license that can be found in the LICENSE file. + +@REM This script will download VAR_DOWNLOAD_URL to VAR_DESTINATION in the +@REM current working directory. + +@echo off + +set "CHROMIUM_DIR=%~dp0" +set "SDK_BIN=%CHROMIUM_DIR%\..\dart-sdk\bin" + +set "DART=%SDK_BIN%\dart.exe" +set "DOWNLOAD_SCRIPT=%CHROMIUM_DIR%\download_file.dart" + +"%DART%" "%DOWNLOAD_SCRIPT%" "VAR_DOWNLOAD_URL" "VAR_DESTINATION"
diff --git a/tools/dom/src/PathObserver.dart b/tools/dom/src/PathObserver.dart new file mode 100644 index 0000000..bc07ac1 --- /dev/null +++ b/tools/dom/src/PathObserver.dart
@@ -0,0 +1,288 @@ +// 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. + +part of html; + +// This code is inspired by ChangeSummary: +// https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js +// ...which underlies MDV. Since we don't need the functionality of +// ChangeSummary, we just implement what we need for data bindings. +// This allows our implementation to be much simpler. + +// TODO(jmesserly): should we make these types stronger, and require +// Observable objects? Currently, it is fine to say something like: +// var path = new PathObserver(123, ''); +// print(path.value); // "123" +// +// Furthermore this degenerate case is allowed: +// var path = new PathObserver(123, 'foo.bar.baz.qux'); +// print(path.value); // "null" +// +// Here we see that any invalid (i.e. not Observable) value will break the +// path chain without producing an error or exception. +// +// Now the real question: should we do this? For the former case, the behavior +// is correct but we could chose to handle it in the dart:html bindings layer. +// For the latter case, it might be better to throw an error so users can find +// the problem. + + +/** + * A data-bound path starting from a view-model or model object, for example + * `foo.bar.baz`. + * + * When the [values] stream is being listened to, this will observe changes to + * the object and any intermediate object along the path, and send [values] + * accordingly. When all listeners are unregistered it will stop observing + * the objects. + * + * This class is used to implement [Node.bind] and similar functionality. + */ +// TODO(jmesserly): find a better home for this type. +@Experimental +class PathObserver { + /** The object being observed. */ + final object; + + /** The path string. */ + final String path; + + /** True if the path is valid, otherwise false. */ + final bool _isValid; + + // TODO(jmesserly): same issue here as ObservableMixin: is there an easier + // way to get a broadcast stream? + StreamController _values; + Stream _valueStream; + + _PropertyObserver _observer, _lastObserver; + + Object _lastValue; + bool _scheduled = false; + + /** + * Observes [path] on [object] for changes. This returns an object that can be + * used to get the changes and get/set the value at this path. + * See [PathObserver.values] and [PathObserver.value]. + */ + PathObserver(this.object, String path) + : path = path, _isValid = _isPathValid(path) { + + // TODO(jmesserly): if the path is empty, or the object is! Observable, we + // can optimize the PathObserver to be more lightweight. + + _values = new StreamController.broadcast(sync: true, + onListen: _observe, + onCancel: _unobserve); + + if (_isValid) { + var segments = []; + for (var segment in path.trim().split('.')) { + if (segment == '') continue; + var index = int.parse(segment, onError: (_) {}); + segments.add(index != null ? index : new Symbol(segment)); + } + + // Create the property observer linked list. + // Note that the structure of a path can't change after it is initially + // constructed, even though the objects along the path can change. + for (int i = segments.length - 1; i >= 0; i--) { + _observer = new _PropertyObserver(this, segments[i], _observer); + if (_lastObserver == null) _lastObserver = _observer; + } + } + } + + // TODO(jmesserly): we could try adding the first value to the stream, but + // that delivers the first record async. + /** + * Listens to the stream, and invokes the [callback] immediately with the + * current [value]. This is useful for bindings, which want to be up-to-date + * immediately. + */ + StreamSubscription bindSync(void callback(value)) { + var result = values.listen(callback); + callback(value); + return result; + } + + // TODO(jmesserly): should this be a change record with the old value? + // TODO(jmesserly): should this be a broadcast stream? We only need + // single-subscription in the bindings system, so single sub saves overhead. + /** + * Gets the stream of values that were observed at this path. + * This returns a single-subscription stream. + */ + Stream get values => _values.stream; + + /** Force synchronous delivery of [values]. */ + void _deliverValues() { + _scheduled = false; + + var newValue = value; + if (!identical(_lastValue, newValue)) { + _values.add(newValue); + _lastValue = newValue; + } + } + + void _observe() { + if (_observer != null) { + _lastValue = value; + _observer.observe(); + } + } + + void _unobserve() { + if (_observer != null) _observer.unobserve(); + } + + void _notifyChange() { + if (_scheduled) return; + _scheduled = true; + + // TODO(jmesserly): should we have a guarenteed order with respect to other + // paths? If so, we could implement this fairly easily by sorting instances + // of this class by birth order before delivery. + queueChangeRecords(_deliverValues); + } + + /** Gets the last reported value at this path. */ + get value { + if (!_isValid) return null; + if (_observer == null) return object; + _observer.ensureValue(object); + return _lastObserver.value; + } + + /** Sets the value at this path. */ + void set value(Object value) { + // TODO(jmesserly): throw if property cannot be set? + // MDV seems tolerant of these error. + if (_observer == null || !_isValid) return; + _observer.ensureValue(object); + var last = _lastObserver; + if (_setObjectProperty(last._object, last._property, value)) { + // Technically, this would get updated asynchronously via a change record. + // However, it is nice if calling the getter will yield the same value + // that was just set. So we use this opportunity to update our cache. + last.value = value; + } + } +} + +// TODO(jmesserly): these should go away in favor of mirrors! +_getObjectProperty(object, property) { + if (object is List && property is int) { + if (property >= 0 && property < object.length) { + return object[property]; + } else { + return null; + } + } + + // TODO(jmesserly): what about length? + if (object is Map) return object[property]; + + if (object is Observable) return object.getValueWorkaround(property); + + return null; +} + +bool _setObjectProperty(object, property, value) { + if (object is List && property is int) { + object[property] = value; + } else if (object is Map) { + object[property] = value; + } else if (object is Observable) { + (object as Observable).setValueWorkaround(property, value); + } else { + return false; + } + return true; +} + + +class _PropertyObserver { + final PathObserver _path; + final _property; + final _PropertyObserver _next; + + // TODO(jmesserly): would be nice not to store both of these. + Object _object; + Object _value; + StreamSubscription _sub; + + _PropertyObserver(this._path, this._property, this._next); + + get value => _value; + + void set value(Object newValue) { + _value = newValue; + if (_next != null) { + if (_sub != null) _next.unobserve(); + _next.ensureValue(_value); + if (_sub != null) _next.observe(); + } + } + + void ensureValue(object) { + // If we're observing, values should be up to date already. + if (_sub != null) return; + + _object = object; + value = _getObjectProperty(object, _property); + } + + void observe() { + if (_object is Observable) { + assert(_sub == null); + _sub = (_object as Observable).changes.listen(_onChange); + } + if (_next != null) _next.observe(); + } + + void unobserve() { + if (_sub == null) return; + + _sub.cancel(); + _sub = null; + if (_next != null) _next.unobserve(); + } + + void _onChange(List<ChangeRecord> changes) { + for (var change in changes) { + // TODO(jmesserly): what to do about "new Symbol" here? + // Ideally this would only preserve names if the user has opted in to + // them being preserved. + // TODO(jmesserly): should we drop observable maps with String keys? + // If so then we only need one check here. + if (change.changes(_property)) { + value = _getObjectProperty(_object, _property); + _path._notifyChange(); + return; + } + } + } +} + +// From: https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js + +const _pathIndentPart = r'[$a-z0-9_]+[$a-z0-9_\d]*'; +final _pathRegExp = new RegExp('^' + '(?:#?' + _pathIndentPart + ')?' + '(?:' + '(?:\\.' + _pathIndentPart + ')' + ')*' + r'$', caseSensitive: false); + +final _spacesRegExp = new RegExp(r'\s'); + +bool _isPathValid(String s) { + s = s.replaceAll(_spacesRegExp, ''); + + if (s == '') return true; + if (s[0] == '.') return false; + return _pathRegExp.hasMatch(s); +}
diff --git a/utils/pub/pub.Makefile b/utils/pub/pub.Makefile new file mode 100644 index 0000000..c1d628d --- /dev/null +++ b/utils/pub/pub.Makefile
@@ -0,0 +1,6 @@ +# This file is generated by gyp; do not edit. + +export builddir_name ?= dart/utils/pub/out +.PHONY: all +all: + $(MAKE) -C ../.. pub
diff --git a/utils/pub/pub.target.mk b/utils/pub/pub.target.mk new file mode 100644 index 0000000..6362e21 --- /dev/null +++ b/utils/pub/pub.target.mk
@@ -0,0 +1,38 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := pub +### Rules for action "generate_pub_snapshot": +quiet_cmd_dart_utils_pub_pub_gyp_pub_target_generate_pub_snapshot = ACTION dart_utils_pub_pub_gyp_pub_target_generate_pub_snapshot $@ +cmd_dart_utils_pub_pub_gyp_pub_target_generate_pub_snapshot = LD_LIBRARY_PATH=$(builddir)/lib.host:$(builddir)/lib.target:$$LD_LIBRARY_PATH; export LD_LIBRARY_PATH; cd utils/pub; mkdir -p $(obj)/gen; "$(builddir)/dart" "--package-root=$(builddir)/packages/" "--generate-script-snapshot=$(obj)/gen/pub.dart.snapshot" ../../sdk/lib/_internal/pub/bin/pub.dart + +$(obj)/gen/pub.dart.snapshot: obj := $(abs_obj) +$(obj)/gen/pub.dart.snapshot: builddir := $(abs_builddir) +$(obj)/gen/pub.dart.snapshot: TOOLSET := $(TOOLSET) +$(obj)/gen/pub.dart.snapshot: $(builddir)/dart sdk/lib/_internal/pub/bin/pub.dart sdk/lib/_internal/pub/lib/src/http.dart sdk/lib/_internal/pub/lib/src/utils.dart sdk/lib/_internal/pub/lib/src/command_deploy.dart sdk/lib/_internal/pub/lib/src/git_source.dart sdk/lib/_internal/pub/lib/src/command_install.dart sdk/lib/_internal/pub/lib/src/exit_codes.dart sdk/lib/_internal/pub/lib/src/command.dart sdk/lib/_internal/pub/lib/src/source_registry.dart sdk/lib/_internal/pub/lib/src/pubspec.dart sdk/lib/_internal/pub/lib/src/command_help.dart sdk/lib/_internal/pub/lib/src/oauth2.dart sdk/lib/_internal/pub/lib/src/command_uploader.dart sdk/lib/_internal/pub/lib/src/error_group.dart sdk/lib/_internal/pub/lib/src/directory_tree.dart sdk/lib/_internal/pub/lib/src/sdk.dart sdk/lib/_internal/pub/lib/src/dart.dart sdk/lib/_internal/pub/lib/src/hosted_source.dart sdk/lib/_internal/pub/lib/src/version.dart sdk/lib/_internal/pub/lib/src/git.dart sdk/lib/_internal/pub/lib/src/io.dart sdk/lib/_internal/pub/lib/src/system_cache.dart sdk/lib/_internal/pub/lib/src/safe_http_server.dart sdk/lib/_internal/pub/lib/src/command_update.dart sdk/lib/_internal/pub/lib/src/command_version.dart sdk/lib/_internal/pub/lib/src/validator.dart sdk/lib/_internal/pub/lib/src/command_cache.dart sdk/lib/_internal/pub/lib/src/source.dart sdk/lib/_internal/pub/lib/src/command_lish.dart sdk/lib/_internal/pub/lib/src/package.dart sdk/lib/_internal/pub/lib/src/log.dart sdk/lib/_internal/pub/lib/src/entrypoint.dart sdk/lib/_internal/pub/lib/src/lock_file.dart sdk/lib/_internal/pub/lib/src/path_source.dart sdk/lib/_internal/pub/lib/src/validator/name.dart sdk/lib/_internal/pub/lib/src/validator/size.dart sdk/lib/_internal/pub/lib/src/validator/pubspec_field.dart sdk/lib/_internal/pub/lib/src/validator/compiled_dartdoc.dart sdk/lib/_internal/pub/lib/src/validator/directory.dart sdk/lib/_internal/pub/lib/src/validator/utf8_readme.dart sdk/lib/_internal/pub/lib/src/validator/dependency.dart sdk/lib/_internal/pub/lib/src/validator/license.dart sdk/lib/_internal/pub/lib/src/validator/lib.dart sdk/lib/_internal/pub/lib/src/solver/version_solver.dart sdk/lib/_internal/pub/lib/src/solver/backtracking_solver.dart sdk/lib/_internal/pub/test/command_line_config.dart sdk/lib/_internal/pub/test/version_test.dart sdk/lib/_internal/pub/test/io_test.dart sdk/lib/_internal/pub/test/pub_cache_test.dart sdk/lib/_internal/pub/test/test_pub.dart sdk/lib/_internal/pub/test/lock_file_test.dart sdk/lib/_internal/pub/test/real_version_test.dart sdk/lib/_internal/pub/test/utils_test.dart sdk/lib/_internal/pub/test/directory_tree_test.dart sdk/lib/_internal/pub/test/unknown_source_test.dart sdk/lib/_internal/pub/test/dev_dependency_test.dart sdk/lib/_internal/pub/test/package_files_test.dart sdk/lib/_internal/pub/test/pubspec_test.dart sdk/lib/_internal/pub/test/pub_test.dart sdk/lib/_internal/pub/test/pub_install_and_update_test.dart sdk/lib/_internal/pub/test/version_solver_test.dart sdk/lib/_internal/pub/test/descriptor.dart sdk/lib/_internal/pub/test/error_group_test.dart sdk/lib/_internal/pub/test/pub_uploader_test.dart sdk/lib/_internal/pub/test/lish/cloud_storage_upload_doesnt_redirect_test.dart sdk/lib/_internal/pub/test/lish/cloud_storage_upload_provides_an_error_test.dart sdk/lib/_internal/pub/test/lish/upload_form_provides_an_error_test.dart sdk/lib/_internal/pub/test/lish/utils.dart sdk/lib/_internal/pub/test/lish/package_creation_provides_a_malformed_error_test.dart sdk/lib/_internal/pub/test/lish/package_validation_has_a_warning_and_is_canceled_test.dart sdk/lib/_internal/pub/test/lish/force_cannot_be_combined_with_dry_run_test.dart sdk/lib/_internal/pub/test/lish/upload_form_fields_is_not_a_map_test.dart sdk/lib/_internal/pub/test/lish/package_creation_provides_invalid_json_test.dart sdk/lib/_internal/pub/test/lish/package_creation_provides_a_malformed_success_test.dart sdk/lib/_internal/pub/test/lish/package_creation_provides_an_error_test.dart sdk/lib/_internal/pub/test/lish/upload_form_is_missing_url_test.dart sdk/lib/_internal/pub/test/lish/force_does_not_publish_if_there_are_errors_test.dart sdk/lib/_internal/pub/test/lish/upload_form_is_missing_fields_test.dart sdk/lib/_internal/pub/test/lish/package_validation_has_an_error_test.dart sdk/lib/_internal/pub/test/lish/preview_package_validation_has_a_warning_test.dart sdk/lib/_internal/pub/test/lish/package_validation_has_a_warning_and_continues_test.dart sdk/lib/_internal/pub/test/lish/force_publishes_if_tests_are_no_warnings_or_errors_test.dart sdk/lib/_internal/pub/test/lish/preview_package_validation_has_no_warnings_test.dart sdk/lib/_internal/pub/test/lish/archives_and_uploads_a_package_test.dart sdk/lib/_internal/pub/test/lish/upload_form_url_is_not_a_string_test.dart sdk/lib/_internal/pub/test/lish/force_publishes_if_there_are_warnings_test.dart sdk/lib/_internal/pub/test/lish/upload_form_fields_has_a_non_string_value_test.dart sdk/lib/_internal/pub/test/lish/upload_form_provides_invalid_json_test.dart sdk/lib/_internal/pub/test/update/git/do_not_update_if_unneeded_test.dart sdk/lib/_internal/pub/test/update/git/update_to_incompatible_pubspec_test.dart sdk/lib/_internal/pub/test/update/git/update_to_nonexistent_pubspec_test.dart sdk/lib/_internal/pub/test/update/git/update_locked_test.dart sdk/lib/_internal/pub/test/update/git/update_one_locked_test.dart sdk/lib/_internal/pub/test/update/hosted/unlock_dependers_test.dart sdk/lib/_internal/pub/test/update/hosted/unlock_if_necessary_test.dart sdk/lib/_internal/pub/test/update/hosted/update_removed_constraints_test.dart sdk/lib/_internal/pub/test/validator/license_test.dart sdk/lib/_internal/pub/test/validator/utils.dart sdk/lib/_internal/pub/test/validator/pubspec_field_test.dart sdk/lib/_internal/pub/test/validator/dependency_test.dart sdk/lib/_internal/pub/test/validator/directory_test.dart sdk/lib/_internal/pub/test/validator/name_test.dart sdk/lib/_internal/pub/test/validator/lib_test.dart sdk/lib/_internal/pub/test/validator/size_test.dart sdk/lib/_internal/pub/test/validator/utf8_readme_test.dart sdk/lib/_internal/pub/test/validator/compiled_dartdoc_test.dart sdk/lib/_internal/pub/test/oauth2/utils.dart sdk/lib/_internal/pub/test/oauth2/with_server_rejected_credentials_authenticates_again_test.dart sdk/lib/_internal/pub/test/oauth2/with_a_server_rejected_refresh_token_authenticates_again_test.dart sdk/lib/_internal/pub/test/oauth2/with_no_credentials_authenticates_and_saves_credentials_test.dart sdk/lib/_internal/pub/test/oauth2/with_an_expired_credentials_refreshes_and_saves_test.dart sdk/lib/_internal/pub/test/oauth2/with_a_pre_existing_credentials_does_not_authenticate_test.dart sdk/lib/_internal/pub/test/oauth2/with_an_expired_credentials_without_a_refresh_token_authenticates_again_test.dart sdk/lib/_internal/pub/test/oauth2/with_a_malformed_credentials_authenticates_again_test.dart sdk/lib/_internal/pub/test/descriptor/tar.dart sdk/lib/_internal/pub/test/descriptor/git.dart sdk/lib/_internal/pub/test/hosted/fail_gracefully_on_missing_package_test.dart sdk/lib/_internal/pub/test/hosted/offline_test.dart sdk/lib/_internal/pub/test/hosted/fail_gracefully_on_url_resolve_test.dart sdk/lib/_internal/pub/test/hosted/remove_removed_transitive_dependency_test.dart sdk/lib/_internal/pub/test/hosted/remove_removed_dependency_test.dart sdk/lib/_internal/pub/test/install/relative_symlink_test.dart sdk/lib/_internal/pub/test/install/broken_symlink_test.dart sdk/lib/_internal/pub/test/install/switch_source_test.dart sdk/lib/_internal/pub/test/install/git/check_out_transitive_test.dart sdk/lib/_internal/pub/test/install/git/unlock_if_incompatible_test.dart sdk/lib/_internal/pub/test/install/git/require_pubspec_test.dart sdk/lib/_internal/pub/test/install/git/check_out_with_trailing_slash_test.dart sdk/lib/_internal/pub/test/install/git/dependency_name_match_pubspec_test.dart sdk/lib/_internal/pub/test/install/git/check_out_branch_test.dart sdk/lib/_internal/pub/test/install/git/stay_locked_if_compatible_test.dart sdk/lib/_internal/pub/test/install/git/check_out_and_update_test.dart sdk/lib/_internal/pub/test/install/git/check_out_twice_test.dart sdk/lib/_internal/pub/test/install/git/check_out_test.dart sdk/lib/_internal/pub/test/install/git/lock_version_test.dart sdk/lib/_internal/pub/test/install/git/require_pubspec_name_test.dart sdk/lib/_internal/pub/test/install/git/check_out_revision_test.dart sdk/lib/_internal/pub/test/install/git/different_repo_name_test.dart sdk/lib/_internal/pub/test/install/hosted/unlock_if_incompatible_test.dart sdk/lib/_internal/pub/test/install/hosted/do_not_update_on_removed_constraints_test.dart sdk/lib/_internal/pub/test/install/hosted/stay_locked_test.dart sdk/lib/_internal/pub/test/install/hosted/install_test.dart sdk/lib/_internal/pub/test/install/hosted/stay_locked_if_compatible_test.dart sdk/lib/_internal/pub/test/install/hosted/install_transitive_test.dart sdk/lib/_internal/pub/test/install/hosted/unlock_if_new_is_unsatisfied_test.dart sdk/lib/_internal/pub/test/install/hosted/stay_locked_if_new_is_satisfied_test.dart sdk/lib/_internal/pub/test/install/hosted/cached_pubspec_test.dart sdk/lib/_internal/pub/test/install/hosted/resolve_constraints_test.dart sdk/lib/_internal/pub/test/install/hosted/repair_cache_test.dart sdk/lib/_internal/pub/test/install/path/nonexistent_dir_test.dart sdk/lib/_internal/pub/test/install/path/absolute_path_test.dart sdk/lib/_internal/pub/test/install/path/relative_symlink_test.dart sdk/lib/_internal/pub/test/install/path/shared_dependency_test.dart sdk/lib/_internal/pub/test/install/path/shared_dependency_symlink_test.dart sdk/lib/_internal/pub/test/install/path/absolute_symlink_test.dart sdk/lib/_internal/pub/test/install/path/no_pubspec_test.dart sdk/lib/_internal/pub/test/install/path/relative_path_test.dart sdk/lib/_internal/pub/test/install/path/path_is_file_test.dart sdk/lib/_internal/pub/test/deploy/copies_non_dart_files_to_deploy_test.dart sdk/lib/_internal/pub/test/deploy/ignores_non_entrypoint_dart_files_test.dart sdk/lib/_internal/pub/test/deploy/compiles_dart_entrypoints_to_dart_and_js_test.dart sdk/lib/_internal/pub/test/deploy/reports_dart_parse_errors_test.dart sdk/lib/_internal/pub/test/deploy/copies_dart_js_next_to_entrypoints_test.dart sdk/lib/_internal/pub/test/deploy/with_no_web_directory_test.dart sdk/lib/_internal/libraries.dart sdk/lib/_internal/compiler/compiler.dart sdk/lib/_internal/compiler/implementation/dart_types.dart sdk/lib/_internal/compiler/implementation/string_validator.dart sdk/lib/_internal/compiler/implementation/world.dart sdk/lib/_internal/compiler/implementation/typechecker.dart sdk/lib/_internal/compiler/implementation/filenames.dart sdk/lib/_internal/compiler/implementation/dart2js.dart sdk/lib/_internal/compiler/implementation/patch_parser.dart sdk/lib/_internal/compiler/implementation/constants.dart sdk/lib/_internal/compiler/implementation/script.dart sdk/lib/_internal/compiler/implementation/library_loader.dart sdk/lib/_internal/compiler/implementation/enqueue.dart sdk/lib/_internal/compiler/implementation/compiler.dart sdk/lib/_internal/compiler/implementation/diagnostic_listener.dart sdk/lib/_internal/compiler/implementation/warnings.dart sdk/lib/_internal/compiler/implementation/source_file_provider.dart sdk/lib/_internal/compiler/implementation/tree_validator.dart sdk/lib/_internal/compiler/implementation/apiimpl.dart sdk/lib/_internal/compiler/implementation/native_handler.dart sdk/lib/_internal/compiler/implementation/constant_system_dart.dart sdk/lib/_internal/compiler/implementation/dart2jslib.dart sdk/lib/_internal/compiler/implementation/compile_time_constants.dart sdk/lib/_internal/compiler/implementation/closure.dart sdk/lib/_internal/compiler/implementation/code_buffer.dart sdk/lib/_internal/compiler/implementation/source_file.dart sdk/lib/_internal/compiler/implementation/deferred_load.dart sdk/lib/_internal/compiler/implementation/resolved_visitor.dart sdk/lib/_internal/compiler/implementation/colors.dart sdk/lib/_internal/compiler/implementation/source_map_builder.dart sdk/lib/_internal/compiler/implementation/constant_system.dart sdk/lib/_internal/compiler/implementation/util/characters.dart sdk/lib/_internal/compiler/implementation/util/util.dart sdk/lib/_internal/compiler/implementation/util/uri_extras.dart sdk/lib/_internal/compiler/implementation/util/link_implementation.dart sdk/lib/_internal/compiler/implementation/util/link.dart sdk/lib/_internal/compiler/implementation/util/util_implementation.dart sdk/lib/_internal/compiler/implementation/tools/mini_parser.dart sdk/lib/_internal/compiler/implementation/dart_backend/utils.dart sdk/lib/_internal/compiler/implementation/dart_backend/dart_backend.dart sdk/lib/_internal/compiler/implementation/dart_backend/emitter.dart sdk/lib/_internal/compiler/implementation/dart_backend/backend.dart sdk/lib/_internal/compiler/implementation/dart_backend/placeholder_collector.dart sdk/lib/_internal/compiler/implementation/dart_backend/renamer.dart sdk/lib/_internal/compiler/implementation/js_backend/minify_namer.dart sdk/lib/_internal/compiler/implementation/js_backend/namer.dart sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart sdk/lib/_internal/compiler/implementation/js_backend/runtime_types.dart sdk/lib/_internal/compiler/implementation/js_backend/constant_system_javascript.dart sdk/lib/_internal/compiler/implementation/js_backend/emitter_no_eval.dart sdk/lib/_internal/compiler/implementation/js_backend/js_backend.dart sdk/lib/_internal/compiler/implementation/js_backend/backend.dart sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart sdk/lib/_internal/compiler/implementation/js_backend/constant_emitter.dart sdk/lib/_internal/compiler/implementation/lib/string_helper.dart sdk/lib/_internal/compiler/implementation/lib/collection_patch.dart sdk/lib/_internal/compiler/implementation/lib/js_rti.dart sdk/lib/_internal/compiler/implementation/lib/core_patch.dart sdk/lib/_internal/compiler/implementation/lib/js_array.dart sdk/lib/_internal/compiler/implementation/lib/mirrors_patch.dart sdk/lib/_internal/compiler/implementation/lib/isolate_patch.dart sdk/lib/_internal/compiler/implementation/lib/async_patch.dart sdk/lib/_internal/compiler/implementation/lib/collection_dev_patch.dart sdk/lib/_internal/compiler/implementation/lib/js_helper.dart sdk/lib/_internal/compiler/implementation/lib/scalarlist_patch.dart sdk/lib/_internal/compiler/implementation/lib/native_helper.dart sdk/lib/_internal/compiler/implementation/lib/io_patch.dart sdk/lib/_internal/compiler/implementation/lib/js_number.dart sdk/lib/_internal/compiler/implementation/lib/regexp_helper.dart sdk/lib/_internal/compiler/implementation/lib/isolate_helper.dart sdk/lib/_internal/compiler/implementation/lib/js_mirrors.dart sdk/lib/_internal/compiler/implementation/lib/constant_map.dart sdk/lib/_internal/compiler/implementation/lib/typed_data_patch.dart sdk/lib/_internal/compiler/implementation/lib/math_patch.dart sdk/lib/_internal/compiler/implementation/lib/js_string.dart sdk/lib/_internal/compiler/implementation/lib/json_patch.dart sdk/lib/_internal/compiler/implementation/lib/foreign_helper.dart sdk/lib/_internal/compiler/implementation/lib/interceptors.dart sdk/lib/_internal/compiler/implementation/mirrors/mirrors.dart sdk/lib/_internal/compiler/implementation/mirrors/util.dart sdk/lib/_internal/compiler/implementation/mirrors/dart2js_mirror.dart sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.dart sdk/lib/_internal/compiler/implementation/resolution/scope.dart sdk/lib/_internal/compiler/implementation/resolution/resolution.dart sdk/lib/_internal/compiler/implementation/resolution/secret_tree_element.dart sdk/lib/_internal/compiler/implementation/resolution/members.dart sdk/lib/_internal/compiler/implementation/ssa/ssa.dart sdk/lib/_internal/compiler/implementation/ssa/bailout.dart sdk/lib/_internal/compiler/implementation/ssa/codegen.dart sdk/lib/_internal/compiler/implementation/ssa/types.dart sdk/lib/_internal/compiler/implementation/ssa/validate.dart sdk/lib/_internal/compiler/implementation/ssa/invoke_dynamic_specializers.dart sdk/lib/_internal/compiler/implementation/ssa/optimize.dart sdk/lib/_internal/compiler/implementation/ssa/types_propagation.dart sdk/lib/_internal/compiler/implementation/ssa/codegen_helpers.dart sdk/lib/_internal/compiler/implementation/ssa/nodes.dart sdk/lib/_internal/compiler/implementation/ssa/value_set.dart sdk/lib/_internal/compiler/implementation/ssa/builder.dart sdk/lib/_internal/compiler/implementation/ssa/interceptor_simplifier.dart sdk/lib/_internal/compiler/implementation/ssa/tracer.dart sdk/lib/_internal/compiler/implementation/ssa/variable_allocator.dart sdk/lib/_internal/compiler/implementation/ssa/value_range_analyzer.dart sdk/lib/_internal/compiler/implementation/js/precedence.dart sdk/lib/_internal/compiler/implementation/js/nodes.dart sdk/lib/_internal/compiler/implementation/js/builder.dart sdk/lib/_internal/compiler/implementation/js/printer.dart sdk/lib/_internal/compiler/implementation/js/js.dart sdk/lib/_internal/compiler/implementation/types/types.dart sdk/lib/_internal/compiler/implementation/types/union_type_mask.dart sdk/lib/_internal/compiler/implementation/types/type_mask.dart sdk/lib/_internal/compiler/implementation/types/concrete_types_inferrer.dart sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart sdk/lib/_internal/compiler/implementation/types/flat_type_mask.dart sdk/lib/_internal/compiler/implementation/elements/elements.dart sdk/lib/_internal/compiler/implementation/elements/modelx.dart sdk/lib/_internal/compiler/implementation/universe/side_effects.dart sdk/lib/_internal/compiler/implementation/universe/selector_map.dart sdk/lib/_internal/compiler/implementation/universe/universe.dart sdk/lib/_internal/compiler/implementation/universe/function_set.dart sdk/lib/_internal/compiler/implementation/scanner/byte_array_scanner.dart sdk/lib/_internal/compiler/implementation/scanner/byte_strings.dart sdk/lib/_internal/compiler/implementation/scanner/scannerlib.dart sdk/lib/_internal/compiler/implementation/scanner/scanner_implementation.dart sdk/lib/_internal/compiler/implementation/scanner/listener.dart sdk/lib/_internal/compiler/implementation/scanner/class_element_parser.dart sdk/lib/_internal/compiler/implementation/scanner/parser_task.dart sdk/lib/_internal/compiler/implementation/scanner/scanner.dart sdk/lib/_internal/compiler/implementation/scanner/keyword.dart sdk/lib/_internal/compiler/implementation/scanner/scanner_task.dart sdk/lib/_internal/compiler/implementation/scanner/partial_parser.dart sdk/lib/_internal/compiler/implementation/scanner/token.dart sdk/lib/_internal/compiler/implementation/scanner/parser.dart sdk/lib/_internal/compiler/implementation/scanner/array_based_scanner.dart sdk/lib/_internal/compiler/implementation/scanner/string_scanner.dart sdk/lib/_internal/compiler/implementation/tree/visitors.dart sdk/lib/_internal/compiler/implementation/tree/prettyprint.dart sdk/lib/_internal/compiler/implementation/tree/unparser.dart sdk/lib/_internal/compiler/implementation/tree/tree.dart sdk/lib/_internal/compiler/implementation/tree/nodes.dart sdk/lib/_internal/compiler/implementation/tree/dartstring.dart sdk/lib/_internal/compiler/samples/jsonify/jsonify.dart sdk/lib/_internal/compiler/samples/compile_loop/compile_loop.dart sdk/lib/_internal/compiler/samples/leap/leap_leg.dart sdk/lib/_internal/compiler/samples/leap/request_cache.dart sdk/lib/_internal/compiler/samples/leap/leap_script.dart sdk/lib/_internal/compiler/samples/leap/leap_server.dart sdk/lib/_internal/compiler/samples/leap/leap.dart sdk/lib/_internal/compiler/samples/darttags/darttags.dart pkg/intl/lib/number_symbols.dart pkg/intl/lib/intl.dart pkg/intl/lib/number_format.dart pkg/intl/lib/date_format.dart pkg/intl/lib/number_symbols_data.dart pkg/intl/lib/date_symbol_data_http_request.dart pkg/intl/lib/intl_browser.dart pkg/intl/lib/bidi_utils.dart pkg/intl/lib/date_symbol_data_local.dart pkg/intl/lib/date_time_patterns.dart pkg/intl/lib/message_lookup_by_library.dart pkg/intl/lib/date_symbol_data_file.dart pkg/intl/lib/extract_messages.dart pkg/intl/lib/generate_localized.dart pkg/intl/lib/bidi_formatter.dart pkg/intl/lib/date_symbols.dart pkg/intl/lib/intl_standalone.dart pkg/intl/lib/src/date_format_helpers.dart pkg/intl/lib/src/http_request_data_reader.dart pkg/intl/lib/src/file_data_reader.dart pkg/intl/lib/src/date_format_internal.dart pkg/intl/lib/src/intl_message.dart pkg/intl/lib/src/intl_helpers.dart pkg/intl/lib/src/date_format_field.dart pkg/intl/lib/src/lazy_locale_data.dart pkg/intl/lib/src/data/dates/localeList.dart pkg/intl/test/date_time_format_local_odd_test.dart pkg/intl/test/data_directory.dart pkg/intl/test/date_time_format_local_even_test.dart pkg/intl/test/intl_message_basic_example_test.dart pkg/intl/test/number_test_data.dart pkg/intl/test/bidi_utils_test.dart pkg/intl/test/date_time_format_test_data.dart pkg/intl/test/number_closure_test.dart pkg/intl/test/date_time_format_file_even_test.dart pkg/intl/test/number_format_test.dart pkg/intl/test/intl_test.dart pkg/intl/test/find_default_locale_standalone_test.dart pkg/intl/test/date_time_format_test_core.dart pkg/intl/test/date_time_format_http_request_test.dart pkg/intl/test/date_time_format_uninitialized_test.dart pkg/intl/test/date_time_format_test_stub.dart pkg/intl/test/find_default_locale_browser_test.dart pkg/intl/test/bidi_format_test.dart pkg/intl/test/date_time_format_file_odd_test.dart pkg/intl/test/message_extraction/generate_from_json.dart pkg/intl/test/message_extraction/sample_with_messages.dart pkg/intl/test/message_extraction/make_hardcoded_translation.dart pkg/intl/test/message_extraction/part_of_sample_with_messages.dart pkg/intl/test/message_extraction/extract_to_json.dart pkg/intl/test/message_extraction/message_extraction_test.dart pkg/intl/tool/generate_locale_data_files.dart pkg/intl/example/basic/basic_example_runner.dart pkg/intl/example/basic/basic_example.dart pkg/intl/example/basic/messages_de.dart pkg/intl/example/basic/messages_all.dart pkg/intl/example/basic/messages_th_th.dart pkg/serialization/lib/serialization.dart pkg/serialization/lib/src/format.dart pkg/serialization/lib/src/serialization_rule.dart pkg/serialization/lib/src/serialization_helpers.dart pkg/serialization/lib/src/mirrors_helpers.dart pkg/serialization/lib/src/basic_rule.dart pkg/serialization/lib/src/reader_writer.dart pkg/serialization/test/serialization_test.dart pkg/serialization/test/test_models.dart pkg/serialization/test/polyfill_identity_map_test.dart pkg/serialization/test/no_library_test.dart pkg/analyzer_experimental/bin/analyzer.dart pkg/analyzer_experimental/lib/options.dart pkg/analyzer_experimental/lib/analyzer.dart pkg/analyzer_experimental/lib/src/utils.dart pkg/analyzer_experimental/lib/src/error_formatter.dart pkg/analyzer_experimental/lib/src/analyzer_impl.dart pkg/analyzer_experimental/lib/src/error.dart pkg/analyzer_experimental/lib/src/generated/constant.dart pkg/analyzer_experimental/lib/src/generated/utilities_dart.dart pkg/analyzer_experimental/lib/src/generated/instrumentation.dart pkg/analyzer_experimental/lib/src/generated/source_io.dart pkg/analyzer_experimental/lib/src/generated/ast.dart pkg/analyzer_experimental/lib/src/generated/sdk.dart pkg/analyzer_experimental/lib/src/generated/element.dart pkg/analyzer_experimental/lib/src/generated/engine.dart pkg/analyzer_experimental/lib/src/generated/scanner.dart pkg/analyzer_experimental/lib/src/generated/java_core.dart pkg/analyzer_experimental/lib/src/generated/java_engine.dart pkg/analyzer_experimental/lib/src/generated/java_io.dart pkg/analyzer_experimental/lib/src/generated/parser.dart pkg/analyzer_experimental/lib/src/generated/java_junit.dart pkg/analyzer_experimental/lib/src/generated/sdk_io.dart pkg/analyzer_experimental/lib/src/generated/html.dart pkg/analyzer_experimental/lib/src/generated/java_engine_io.dart pkg/analyzer_experimental/lib/src/generated/error.dart pkg/analyzer_experimental/lib/src/generated/source.dart pkg/analyzer_experimental/lib/src/generated/resolver.dart pkg/analyzer_experimental/test/utils.dart pkg/analyzer_experimental/test/error_test.dart pkg/analyzer_experimental/test/options_test.dart pkg/analyzer_experimental/test/generated/resolver_test.dart pkg/analyzer_experimental/test/generated/scanner_test.dart pkg/analyzer_experimental/test/generated/element_test.dart pkg/analyzer_experimental/test/generated/ast_test.dart pkg/analyzer_experimental/test/generated/test_support.dart pkg/analyzer_experimental/test/generated/parser_test.dart pkg/analyzer_experimental/example/scanner_driver.dart pkg/analyzer_experimental/example/resolver_driver.dart pkg/analyzer_experimental/example/parser_driver.dart pkg/webdriver/lib/webdriver.dart pkg/webdriver/lib/src/base64decoder.dart pkg/webdriver/test/webdriver_test.dart pkg/fixnum/lib/fixnum.dart pkg/fixnum/lib/src/int64.dart pkg/fixnum/lib/src/intx.dart pkg/fixnum/lib/src/int32.dart pkg/fixnum/test/int_32_test.dart pkg/fixnum/test/int_64_vm_test.dart pkg/fixnum/test/int_64_test.dart pkg/pathos/lib/path.dart pkg/pathos/test/pathos_test.dart pkg/pathos/test/pathos_windows_test.dart pkg/pathos/test/pathos_posix_test.dart pkg/crypto/lib/crypto.dart pkg/crypto/lib/src/crypto_utils.dart pkg/crypto/lib/src/hmac.dart pkg/crypto/lib/src/sha1.dart pkg/crypto/lib/src/hash_utils.dart pkg/crypto/lib/src/md5.dart pkg/crypto/lib/src/sha256.dart pkg/meta/lib/meta.dart pkg/oauth2/lib/oauth2.dart pkg/oauth2/lib/src/utils.dart pkg/oauth2/lib/src/expiration_exception.dart pkg/oauth2/lib/src/credentials.dart pkg/oauth2/lib/src/authorization_exception.dart pkg/oauth2/lib/src/authorization_code_grant.dart pkg/oauth2/lib/src/handle_access_token_response.dart pkg/oauth2/lib/src/client.dart pkg/oauth2/test/utils.dart pkg/oauth2/test/handle_access_token_response_test.dart pkg/oauth2/test/authorization_code_grant_test.dart pkg/oauth2/test/client_test.dart pkg/oauth2/test/utils_test.dart pkg/oauth2/test/credentials_test.dart pkg/http/lib/http.dart pkg/http/lib/testing.dart pkg/http/lib/src/io_client.dart pkg/http/lib/src/utils.dart pkg/http/lib/src/base_request.dart pkg/http/lib/src/streamed_response.dart pkg/http/lib/src/byte_stream.dart pkg/http/lib/src/base_client.dart pkg/http/lib/src/streamed_request.dart pkg/http/lib/src/base_response.dart pkg/http/lib/src/mock_client.dart pkg/http/lib/src/response.dart pkg/http/lib/src/request.dart pkg/http/lib/src/client.dart pkg/http/lib/src/multipart_file.dart pkg/http/lib/src/multipart_request.dart pkg/http/test/mock_client_test.dart pkg/http/test/utils.dart pkg/http/test/streamed_request_test.dart pkg/http/test/multipart_test.dart pkg/http/test/client_test.dart pkg/http/test/http_test.dart pkg/http/test/request_test.dart pkg/http/test/response_test.dart pkg/http/test/safe_http_server.dart pkg/expect/lib/expect.dart pkg/mdv_observe/lib/mdv_observe.dart pkg/mdv_observe/lib/src/observable_list.dart pkg/mdv_observe/lib/src/observable_map.dart pkg/mdv_observe/lib/src/observable_box.dart pkg/mdv_observe/test/utils.dart pkg/mdv_observe/test/list_change_test.dart pkg/mdv_observe/test/observable_list_test.dart pkg/mdv_observe/test/observe_test.dart pkg/mdv_observe/test/observable_map_test.dart pkg/logging/lib/logging.dart pkg/logging/test/logging_test.dart pkg/stack_trace/lib/stack_trace.dart pkg/stack_trace/lib/src/utils.dart pkg/stack_trace/lib/src/lazy_trace.dart pkg/stack_trace/lib/src/trace.dart pkg/stack_trace/lib/src/frame.dart pkg/stack_trace/test/frame_test.dart pkg/stack_trace/test/trace_test.dart pkg/yaml/lib/yaml.dart pkg/yaml/lib/src/composer.dart pkg/yaml/lib/src/utils.dart pkg/yaml/lib/src/yaml_exception.dart pkg/yaml/lib/src/deep_equals.dart pkg/yaml/lib/src/visitor.dart pkg/yaml/lib/src/yaml_map.dart pkg/yaml/lib/src/parser.dart pkg/yaml/lib/src/model.dart pkg/yaml/lib/src/constructor.dart pkg/yaml/test/yaml_test.dart pkg/scheduled_test/lib/scheduled_test.dart pkg/scheduled_test/lib/scheduled_process.dart pkg/scheduled_test/lib/scheduled_server.dart pkg/scheduled_test/lib/descriptor.dart pkg/scheduled_test/lib/src/utils.dart pkg/scheduled_test/lib/src/schedule_error.dart pkg/scheduled_test/lib/src/task.dart pkg/scheduled_test/lib/src/substitute_future.dart pkg/scheduled_test/lib/src/scheduled_future_matchers.dart pkg/scheduled_test/lib/src/value_future.dart pkg/scheduled_test/lib/src/mock_clock.dart pkg/scheduled_test/lib/src/future_group.dart pkg/scheduled_test/lib/src/schedule.dart pkg/scheduled_test/lib/src/scheduled_server/handler.dart pkg/scheduled_test/lib/src/scheduled_server/safe_http_server.dart pkg/scheduled_test/lib/src/descriptor/directory_descriptor.dart pkg/scheduled_test/lib/src/descriptor/async_descriptor.dart pkg/scheduled_test/lib/src/descriptor/nothing_descriptor.dart pkg/scheduled_test/lib/src/descriptor/descriptor.dart pkg/scheduled_test/lib/src/descriptor/file_descriptor.dart pkg/scheduled_test/lib/src/descriptor/pattern_descriptor.dart pkg/scheduled_test/test/utils.dart pkg/scheduled_test/test/substitute_future_test.dart pkg/scheduled_test/test/scheduled_server_test.dart pkg/scheduled_test/test/scheduled_future_matchers_test.dart pkg/scheduled_test/test/metatest.dart pkg/scheduled_test/test/value_future_test.dart pkg/scheduled_test/test/scheduled_process_test.dart pkg/scheduled_test/test/descriptor/utils.dart pkg/scheduled_test/test/descriptor/file_test.dart pkg/scheduled_test/test/descriptor/nothing_test.dart pkg/scheduled_test/test/descriptor/pattern_test.dart pkg/scheduled_test/test/descriptor/directory_test.dart pkg/scheduled_test/test/descriptor/async_test.dart pkg/scheduled_test/test/scheduled_test/current_schedule_state_test.dart pkg/scheduled_test/test/scheduled_test/current_schedule_current_task_test.dart pkg/scheduled_test/test/scheduled_test/nested_task_test.dart pkg/scheduled_test/test/scheduled_test/on_exception_test.dart pkg/scheduled_test/test/scheduled_test/task_return_value_test.dart pkg/scheduled_test/test/scheduled_test/abort_test.dart pkg/scheduled_test/test/scheduled_test/timeout_test.dart pkg/scheduled_test/test/scheduled_test/out_of_band_task_test.dart pkg/scheduled_test/test/scheduled_test/wrap_async_test.dart pkg/scheduled_test/test/scheduled_test/wrap_future_test.dart pkg/scheduled_test/test/scheduled_test/signal_error_test.dart pkg/scheduled_test/test/scheduled_test/set_up_test.dart pkg/scheduled_test/test/scheduled_test/on_complete_test.dart pkg/scheduled_test/test/scheduled_test/simple_test.dart pkg/scheduled_test/test/scheduled_test/current_schedule_errors_test.dart pkg/unittest/lib/unittest.dart pkg/unittest/lib/html_individual_config.dart pkg/unittest/lib/vm_config.dart pkg/unittest/lib/html_enhanced_config.dart pkg/unittest/lib/matcher.dart pkg/unittest/lib/interactive_html_config.dart pkg/unittest/lib/mock.dart pkg/unittest/lib/html_config.dart pkg/unittest/lib/compact_vm_config.dart pkg/unittest/lib/src/expect.dart pkg/unittest/lib/src/basematcher.dart pkg/unittest/lib/src/utils.dart pkg/unittest/lib/src/string_matchers.dart pkg/unittest/lib/src/pretty_print.dart pkg/unittest/lib/src/core_matchers.dart pkg/unittest/lib/src/interfaces.dart pkg/unittest/lib/src/description.dart pkg/unittest/lib/src/future_matchers.dart pkg/unittest/lib/src/numeric_matchers.dart pkg/unittest/lib/src/iterable_matchers.dart pkg/unittest/lib/src/map_matchers.dart pkg/unittest/lib/src/config.dart pkg/unittest/lib/src/test_case.dart pkg/unittest/lib/src/operator_matchers.dart pkg/unittest/test/mock_regexp_negative_test.dart pkg/unittest/test/matchers_minified_test.dart pkg/unittest/test/matchers_test.dart pkg/unittest/test/unittest_test.dart pkg/unittest/test/matchers_unminified_test.dart pkg/unittest/test/test_common.dart pkg/unittest/test/test_utils.dart pkg/unittest/test/pretty_print_test.dart pkg/unittest/test/pretty_print_minified_test.dart pkg/unittest/test/instance_test.dart pkg/unittest/test/pretty_print_unminified_test.dart pkg/unittest/test/mock_test.dart pkg/unittest/test/mock_stepwise_negative_test.dart pkg/args/lib/args.dart pkg/args/lib/src/parser.dart pkg/args/lib/src/usage.dart pkg/args/test/args_test.dart pkg/args/test/usage_test.dart pkg/args/test/command_test.dart pkg/args/test/parse_test.dart pkg/args/example/test_runner.dart pkg/source_maps/lib/span.dart pkg/source_maps/lib/source_maps.dart pkg/source_maps/lib/parser.dart pkg/source_maps/lib/builder.dart pkg/source_maps/lib/printer.dart pkg/source_maps/lib/src/utils.dart pkg/source_maps/lib/src/vlq.dart pkg/source_maps/test/vlq_test.dart pkg/source_maps/test/run.dart pkg/source_maps/test/builder_test.dart pkg/source_maps/test/utils_test.dart pkg/source_maps/test/common.dart pkg/source_maps/test/parser_test.dart pkg/source_maps/test/printer_test.dart pkg/source_maps/test/span_test.dart pkg/source_maps/test/end2end_test.dart FORCE_DO_CMD + $(call do_cmd,dart_utils_pub_pub_gyp_pub_target_generate_pub_snapshot) + +all_deps += $(obj)/gen/pub.dart.snapshot +action_dart_utils_pub_pub_gyp_pub_target_generate_pub_snapshot_outputs := $(obj)/gen/pub.dart.snapshot + + +### Rules for final target. +# Build our special outputs first. +$(obj).target/utils/pub/pub.stamp: | $(action_dart_utils_pub_pub_gyp_pub_target_generate_pub_snapshot_outputs) + +# Preserve order dependency of special output on deps. +$(action_dart_utils_pub_pub_gyp_pub_target_generate_pub_snapshot_outputs): | $(builddir)/dart $(obj).target/pkg/pkg_packages.stamp + +$(obj).target/utils/pub/pub.stamp: TOOLSET := $(TOOLSET) +$(obj).target/utils/pub/pub.stamp: $(builddir)/dart $(obj).target/pkg/pkg_packages.stamp FORCE_DO_CMD + $(call do_cmd,touch) + +all_deps += $(obj).target/utils/pub/pub.stamp +# Add target alias +.PHONY: pub +pub: $(obj).target/utils/pub/pub.stamp + +# Add target alias to "all" target. +.PHONY: all +all: pub +
diff --git a/utils/pub/solver/greedy_solver.dart b/utils/pub/solver/greedy_solver.dart new file mode 100644 index 0000000..e664ea2 --- /dev/null +++ b/utils/pub/solver/greedy_solver.dart
@@ -0,0 +1,556 @@ +// 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. + +/// Attempts to resolve a set of version constraints for a package dependency +/// graph and select an appropriate set of best specific versions for all +/// dependent packages. It works iteratively and tries to reach a stable +/// solution where the constraints of all dependencies are met. If it fails to +/// reach a solution after a certain number of iterations, it assumes the +/// dependency graph is unstable and reports and error. +/// +/// There are two fundamental operations in the process of iterating over the +/// graph: +/// +/// 1. Changing the selected concrete version of some package. (This includes +/// adding and removing a package too, which is considering changing the +/// version to or from "none".) In other words, a node has changed. +/// 2. Changing the version constraint that one package places on another. In +/// other words, and edge has changed. +/// +/// Both of these events have a corresponding (potentional) async operation and +/// roughly cycle back and forth between each other. When we change the version +/// of package changes, we asynchronously load the pubspec for the new version. +/// When that's done, we compare the dependencies of the new version versus the +/// old one. For everything that differs, we change those constraints between +/// this package and that dependency. +/// +/// When a constraint on a package changes, we re-calculate the overall +/// constraint on that package. I.e. with a shared dependency, we intersect all +/// of the constraints that its depending packages place on it. If that overall +/// constraint changes (say from "<3.0.0" to "<2.5.0"), then the currently +/// picked version for that package may fall outside of the new constraint. If +/// that happens, we find the new best version that meets the updated constraint +/// and then the change the package to use that version. That cycles back up to +/// the beginning again. +library version_solver1; + +import 'dart:async'; +import 'dart:collection' show Queue; +import 'dart:math' as math; + +import '../lock_file.dart'; +import '../log.dart' as log; +import '../package.dart'; +import '../source.dart'; +import '../source_registry.dart'; +import '../version.dart'; +import 'version_solver.dart'; + +class GreedyVersionSolver extends VersionSolver { + final _packages = <String, DependencyNode>{}; + final _work = new Queue<WorkItem>(); + int _numIterations = 0; + + GreedyVersionSolver(SourceRegistry sources, Package root, LockFile lockFile, + List<String> useLatest) + : super(sources, root, lockFile, useLatest); + + /// The non-backtracking solver always only tries one solution. + int get attemptedSolutions => 1; + + void forceLatestVersion(String package) { + // TODO(nweiz): How do we want to detect and handle unknown dependencies + // here? + getDependency(package).useLatestVersion = true; + } + + Future<List<PackageId>> runSolver() { + // Kick off the work by adding the root package at its concrete version to + // the dependency graph. + enqueue(new AddConstraint('(entrypoint)', new PackageRef.root(root))); + + Future processNextWorkItem(_) { + while (true) { + // Stop if we are done. + if (_work.isEmpty) return new Future.value(buildResults()); + + // If we appear to be stuck in a loop, then we probably have an unstable + // graph, bail. We guess this based on a rough heuristic that it should + // only take a certain number of steps to solve a graph with a given + // number of connections. + // TODO(rnystrom): These numbers here are magic and arbitrary. Tune + // when we have a better picture of real-world package topologies. + _numIterations++; + if (_numIterations > math.max(50, _packages.length * 5)) { + throw new CouldNotSolveException(); + } + + // Run the first work item. + var future = _work.removeFirst().process(this); + + // If we have an async operation to perform, chain the loop to resume + // when it's done. Otherwise, just loop synchronously. + if (future != null) { + return future.then(processNextWorkItem); + } + } + } + + return processNextWorkItem(null); + } + + void enqueue(WorkItem work) { + _work.add(work); + } + + DependencyNode getDependency(String package) { + // There can be unused dependencies in the graph, so just create an empty + // one if needed. + _packages.putIfAbsent(package, () => new DependencyNode(package)); + return _packages[package]; + } + + /// Sets the best selected version of [package] to [version]. + void setVersion(String package, Version version) { + _packages[package].version = version; + } + + /// Returns the most recent version of [dependency] that satisfies all of its + /// version constraints. + Future<Version> getBestVersion(DependencyNode dependency) { + return cache.getVersions(dependency.name, + dependency.source, dependency.description).then((versions) { + var best = null; + for (var ref in versions) { + if (dependency.useLatestVersion || + dependency.constraint.allows(ref.version)) { + if (best == null || ref.version > best) best = ref.version; + } + } + + // TODO(rnystrom): Better exception. + if (best == null) { + if (tryUnlockDepender(dependency)) return null; + throw new NoVersionException(dependency.name, dependency.constraint, + dependency.toList()); + } else if (!dependency.constraint.allows(best)) { + if (tryUnlockDepender(dependency)) return null; + throw new CouldNotUpdateException( + dependency.name, dependency.constraint, best); + } + + return best; + }); + } + + /// Looks for a package that depends (transitively) on [dependency] and has + /// its version locked in the lockfile. If one is found, enqueues an + /// [UnlockPackage] work item for it and returns true. Otherwise, returns + /// false. + /// + /// This does a breadth-first search; immediate dependers will be unlocked + /// first, followed by transitive dependers. + bool tryUnlockDepender(DependencyNode dependency, [Set<String> seen]) { + if (seen == null) seen = new Set(); + // Avoid an infinite loop if there are circular dependencies. + if (seen.contains(dependency.name)) return false; + seen.add(dependency.name); + + for (var dependerName in dependency.dependers) { + var depender = getDependency(dependerName); + var locked = lockFile.packages[dependerName]; + if (locked != null && depender.version == locked.version && + depender.source.name == locked.source.name) { + enqueue(new UnlockPackage(depender)); + return true; + } + } + + return dependency.dependers.map(getDependency).any((subdependency) => + tryUnlockDepender(subdependency, seen)); + } + + List<PackageId> buildResults() { + return _packages.values + .where((dep) => dep.isDependedOn) + .map(_dependencyToPackageId) + .toList(); + } + + PackageId _dependencyToPackageId(DependencyNode dep) { + var description = dep.description; + + // If the lockfile contains a fully-resolved description for the package, + // use that. This allows e.g. Git to ensure that the same commit is used. + var lockedPackage = lockFile.packages[dep.name]; + if (lockedPackage != null && lockedPackage.version == dep.version && + lockedPackage.source.name == dep.source.name && + dep.source.descriptionsEqual( + description, lockedPackage.description)) { + description = lockedPackage.description; + } + + return new PackageId(dep.name, dep.source, dep.version, description); + } +} + +/// The constraint solver works by iteratively processing a queue of work items. +/// Each item is a single atomic change to the dependency graph. Handling them +/// in a queue lets us handle asynchrony (resolving versions requires +/// information from servers) as well as avoid deeply nested recursion. +abstract class WorkItem { + /// Processes this work item. Returns a future that completes when the work is + /// done. If `null` is returned, that means the work has completed + /// synchronously and the next item can be started immediately. + Future process(GreedyVersionSolver solver); +} + +/// The best selected version for a package has changed to [version]. If the +/// previous version of the package is `null`, that means the package is being +/// added to the graph. If [version] is `null`, it is being removed. +class ChangeVersion implements WorkItem { + /// The name of the package whose version is being changed. + final String package; + + /// The source of the package whose version is changing. + final Source source; + + /// The description identifying the package whose version is changing. + final description; + + /// The new selected version. + final Version version; + + ChangeVersion(this.package, this.source, this.description, this.version); + + Future process(GreedyVersionSolver solver) { + log.fine("Changing $package to version $version."); + + var dependency = solver.getDependency(package); + var oldVersion = dependency.version; + solver.setVersion(package, version); + + // The dependencies between the old and new version may be different. Walk + // them both and update any constraints that differ between the two. + return Future.wait([ + getDependencyRefs(solver, oldVersion), + getDependencyRefs(solver, version)]).then((list) { + var oldDependencyRefs = list[0]; + var newDependencyRefs = list[1]; + + for (var oldRef in oldDependencyRefs.values) { + if (newDependencyRefs.containsKey(oldRef.name)) { + // The dependency is in both versions of this package, but its + // constraint may have changed. + var newRef = newDependencyRefs.remove(oldRef.name); + solver.enqueue(new AddConstraint(package, newRef)); + } else { + // The dependency is not in the new version of the package, so just + // remove its constraint. + solver.enqueue(new RemoveConstraint(package, oldRef.name)); + } + } + + // Everything that's left is a depdendency that's only in the new + // version of the package. + for (var newRef in newDependencyRefs.values) { + solver.enqueue(new AddConstraint(package, newRef)); + } + }); + } + + /// Get the dependencies at [version] of the package being changed. + Future<Map<String, PackageRef>> getDependencyRefs(VersionSolver solver, + Version version) { + // If there is no version, it means no package, so no dependencies. + if (version == null) { + return new Future<Map<String, PackageRef>>.value(<String, PackageRef>{}); + } + + var id = new PackageId(package, source, version, description); + return solver.cache.getPubspec(id).then((pubspec) { + var dependencies = <String, PackageRef>{}; + for (var dependency in pubspec.dependencies) { + dependencies[dependency.name] = dependency; + } + + // Include dev dependencies only from the root package. + if (id.isRoot) { + for (var dependency in pubspec.devDependencies) { + dependencies[dependency.name] = dependency; + } + } + + return dependencies; + }); + } +} + +/// A constraint that a depending package places on a dependent package has +/// changed. +/// +/// This is an abstract class that contains logic for updating the dependency +/// graph once a dependency has changed. Changing the dependency is the +/// responsibility of subclasses. +abstract class ChangeConstraint implements WorkItem { + Future process(GreedyVersionSolver solver); + + void undo(GreedyVersionSolver solver); + + Future _processChange(GreedyVersionSolver solver, + DependencyNode oldDependency, + DependencyNode newDependency) { + var name = newDependency.name; + var source = oldDependency.source != null ? + oldDependency.source : newDependency.source; + var description = oldDependency.description != null ? + oldDependency.description : newDependency.description; + var oldConstraint = oldDependency.constraint; + var newConstraint = newDependency.constraint; + + // If the package is over-constrained, i.e. the packages depending have + // disjoint constraints, then try unlocking a depender that's locked by the + // lockfile. If there are no remaining locked dependencies, throw an error. + if (newConstraint != null && newConstraint.isEmpty) { + if (solver.tryUnlockDepender(newDependency)) { + undo(solver); + return null; + } + + throw new DisjointConstraintException(name, newDependency.toList()); + } + + // If this constraint change didn't cause the overall constraint on the + // package to change, then we don't need to do any further work. + if (oldConstraint == newConstraint) return null; + + // If the dependency has been cut free from the graph, just remove it. + if (!newDependency.isDependedOn) { + solver.enqueue(new ChangeVersion(name, source, description, null)); + return null; + } + + // If the dependency is on the root package, then we don't need to do + // anything since it's already at the best version. + if (name == solver.root.name) { + solver.enqueue(new ChangeVersion( + name, source, description, solver.root.version)); + return null; + } + + // If the dependency is on a package in the lockfile, use the lockfile's + // version for that package if it's valid given the other constraints. + var lockedPackage = solver.lockFile.packages[name]; + if (lockedPackage != null && newDependency.source == lockedPackage.source) { + var lockedVersion = lockedPackage.version; + if (newConstraint.allows(lockedVersion)) { + solver.enqueue( + new ChangeVersion(name, source, description, lockedVersion)); + return null; + } + } + + // The constraint has changed, so see what the best version of the package + // that meets the new constraint is. + return solver.getBestVersion(newDependency).then((best) { + if (best == null) { + undo(solver); + } else if (newDependency.version != best) { + solver.enqueue(new ChangeVersion(name, source, description, best)); + } + }); + } +} + +/// The constraint given by [ref] is being placed by [depender]. +class AddConstraint extends ChangeConstraint { + /// The package that has the dependency. + final String depender; + + /// The package being depended on and the constraints being placed on it. The + /// source, version, and description in this ref are all considered + /// constraints on the dependent package. + final PackageRef ref; + + AddConstraint(this.depender, this.ref); + + Future process(GreedyVersionSolver solver) { + log.fine("Adding $depender's constraint $ref."); + + var dependency = solver.getDependency(ref.name); + var oldDependency = dependency.clone(); + dependency.placeConstraint(depender, ref); + return _processChange(solver, oldDependency, dependency); + } + + void undo(GreedyVersionSolver solver) { + solver.getDependency(ref.name).removeConstraint(depender); + } +} + +/// [depender] is no longer placing a constraint on [dependent]. +class RemoveConstraint extends ChangeConstraint { + /// The package that was placing a constraint on [dependent]. + String depender; + + /// The package that was being depended on. + String dependent; + + /// The constraint that was removed. + PackageRef _removed; + + RemoveConstraint(this.depender, this.dependent); + + Future process(GreedyVersionSolver solver) { + log.fine("Removing $depender's constraint ($_removed) on $dependent."); + + var dependency = solver.getDependency(dependent); + var oldDependency = dependency.clone(); + _removed = dependency.removeConstraint(depender); + return _processChange(solver, oldDependency, dependency); + } + + void undo(GreedyVersionSolver solver) { + solver.getDependency(dependent).placeConstraint(depender, _removed); + } +} + +/// [package]'s version is no longer constrained by the lockfile. +class UnlockPackage implements WorkItem { + /// The package being unlocked. + DependencyNode package; + + UnlockPackage(this.package); + + Future process(GreedyVersionSolver solver) { + log.fine("Unlocking ${package.name}."); + + solver.lockFile.packages.remove(package.name); + return solver.getBestVersion(package).then((best) { + if (best == null) return null; + solver.enqueue(new ChangeVersion( + package.name, package.source, package.description, best)); + }); + } +} + +/// Describes one [Package] in the [DependencyGraph] and keeps track of which +/// packages depend on it and what constraints they place on it. +class DependencyNode { + /// The name of the this dependency's package. + final String name; + + /// The [PackageRefs] that represent constraints that depending packages have + /// placed on this one. + final Map<String, PackageRef> _refs; + + /// The currently-selected best version for this dependency. + Version version; + + /// Whether this dependency should always select the latest version. + bool useLatestVersion = false; + + /// Gets whether or not any other packages are currently depending on this + /// one. If `false`, then it means this package is not part of the dependency + /// graph and should be omitted. + bool get isDependedOn => !_refs.isEmpty; + + /// The names of all the packages that depend on this dependency. + Iterable<String> get dependers => _refs.keys; + + /// Gets the overall constraint that all packages are placing on this one. + /// If no packages have a constraint on this one (which can happen when this + /// package is in the process of being added to the graph), returns `null`. + VersionConstraint get constraint { + if (_refs.isEmpty) return null; + return new VersionConstraint.intersection( + _refs.values.map((ref) => ref.constraint)); + } + + /// The source of this dependency's package. + Source get source { + var canonical = _canonicalRef(); + if (canonical == null) return null; + return canonical.source; + } + + /// The description of this dependency's package. + get description { + var canonical = _canonicalRef(); + if (canonical == null) return null; + return canonical.description; + } + + /// Return the PackageRef that has the canonical source and description for + /// this package. If any dependency is on the root package, that will be used; + /// otherwise, it will be the source and description that all dependencies + /// agree upon. + PackageRef _canonicalRef() { + if (_refs.isEmpty) return null; + var refs = _refs.values; + for (var ref in refs) { + if (ref.isRoot) return ref; + } + return refs.first; + } + + DependencyNode(this.name) + : _refs = <String, PackageRef>{}; + + DependencyNode._clone(DependencyNode other) + : name = other.name, + version = other.version, + _refs = new Map<String, PackageRef>.from(other._refs); + + /// Creates a copy of this dependency. + DependencyNode clone() => new DependencyNode._clone(this); + + /// Places [ref] as a constraint from [package] onto this. + void placeConstraint(String package, PackageRef ref) { + var requiredDepender = _requiredDepender(); + if (requiredDepender != null) { + var required = _refs[requiredDepender]; + if (required.source.name != ref.source.name) { + throw new SourceMismatchException(name, [ + new Dependency(requiredDepender, required), + new Dependency(package, ref)]); + } else if (!required.descriptionEquals(ref)) { + throw new DescriptionMismatchException(name, [ + new Dependency(requiredDepender, required), + new Dependency(package, ref)]); + } + } + + _refs[package] = ref; + } + + /// Returns the name of a package whose constraint source and description + /// all other constraints must match. Returns null if there are no + /// requirements on new constraints. + String _requiredDepender() { + if (_refs.isEmpty) return null; + + var dependers = _refs.keys.toList(); + if (dependers.length == 1) { + var depender = dependers[0]; + if (_refs[depender].isRoot) return null; + return depender; + } + + return dependers[1]; + } + + /// Removes the constraint from [package] onto this. + PackageRef removeConstraint(String package) => _refs.remove(package); + + /// Converts this to a list of [Dependency] objects like the error types + /// expect. + List<Dependency> toList() { + var result = <Dependency>[]; + _refs.forEach((name, ref) { + result.add(new Dependency(name, ref)); + }); + return result; + } +}