dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.25k stars 1.58k forks source link

DartAnalyzer exception in WebStorm #25372

Closed zoechi closed 8 years ago

zoechi commented 8 years ago

[3522266] ERROR - yzer.DartAnalysisServerService - Dart analysis server, SDK version 1.14.0-edge.c6f0d75d81a4a45817be1516449e1d3fe04be223, server version 1.14.0, error: Exception from assist contributor: DefaultAssistContributor Cannot compute RESOLVED_UNIT for /home/myuser/dart/bwu_ng_iron_list/lib/bwu_ng_iron_list.dart in /home/myuser/dart/bwu_ng_iron_list/lib/bwu_ng_iron_list.dart

0 AnalysisContextImpl.computeResult (package:analyzer/src/context/context.dart:651)

1 AnalysisContextImpl.resolveCompilationUnit2 (package:analyzer/src/context/context.dart:1166)

2 DartAssistContributor.computeAssists. (package:analysis_server/plugin/edit/assist/assist_dart.dart:64)

3 Future.Future.microtask. (dart:async/future.dart:144)

4 _rootRun (dart:async/zone.dart:903)

5 _CustomZone.run (dart:async/zone.dart:802)

6 _CustomZone.runGuarded (dart:async/zone.dart:708)

7 _CustomZone.bindCallback. (dart:async/zone.dart:733)

8 _rootRun (dart:async/zone.dart:907)

9 _CustomZone.run (dart:async/zone.dart:802)

10 _CustomZone.runGuarded (dart:async/zone.dart:708)

11 _CustomZone.bindCallback. (dart:async/zone.dart:733)

12 _microtaskLoop (dart:async/schedule_microtask.dart:41)

13 _startMicrotaskLoop (dart:async/schedule_microtask.dart:50)

14 _runPendingImmediateCallback (dart:isolate-patch/isolate_patch.dart:96)

15 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:149)

Caused by Unexpected exception while performing ResolveUnitTask for source /home/myuser/dart/bwu_ng_iron_list/lib/bwu_ng_iron_list.dart

0 AnalysisTask._safelyPerform (package:analyzer/task/model.dart:320)

1 AnalysisTask.perform (package:analyzer/task/model.dart:210)

2 AnalysisDriver.performWorkItem (package:analyzer/src/task/driver.dart:271)

3 AnalysisDriver.computeResult (package:analyzer/src/task/driver.dart:109)

4 AnalysisContextImpl.computeResult (package:analyzer/src/context/context.dart:647)

5 AnalysisContextImpl.resolveCompilationUnit2 (package:analyzer/src/context/context.dart:1166)

6 DartAssistContributor.computeAssists. (package:analysis_server/plugin/edit/assist/assist_dart.dart:64)

7 Future.Future.microtask. (dart:async/future.dart:144)

8 _rootRun (dart:async/zone.dart:903)

9 _CustomZone.run (dart:async/zone.dart:802)

10 _CustomZone.runGuarded (dart:async/zone.dart:708)

11 _CustomZone.bindCallback. (dart:async/zone.dart:733)

12 _rootRun (dart:async/zone.dart:907)

13 _CustomZone.run (dart:async/zone.dart:802)

14 _CustomZone.runGuarded (dart:async/zone.dart:708)

15 _CustomZone.bindCallback. (dart:async/zone.dart:733)

16 _microtaskLoop (dart:async/schedule_microtask.dart:41)

17 _startMicrotaskLoop (dart:async/schedule_microtask.dart:50)

18 _runPendingImmediateCallback (dart:isolate-patch/isolate_patch.dart:96)

19 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:149)

Caused by IllegalArgumentException: Expected 3 parameters, not 2 null

0 ArgumentList.correspondingStaticParameters= (package:analyzer/src/generated/ast.dart:453)

1 StaticTypeAnalyzer._inferMethodInvocationGeneric (package:analyzer/src/generated/static_type_analyzer.dart:1874)

2 StaticTypeAnalyzer._inferMethodInvocation (package:analyzer/src/generated/static_type_analyzer.dart:1816)

3 StaticTypeAnalyzer.visitMethodInvocation (package:analyzer/src/generated/static_type_analyzer.dart:745)

4 MethodInvocation.accept (package:analyzer/src/generated/ast.dart:8214)

5 ResolverVisitor.visitMethodInvocation (package:analyzer/src/generated/resolver.dart:9165)

6 MethodInvocation.accept (package:analyzer/src/generated/ast.dart:8214)

7 AstNode._safelyVisitChild (package:analyzer/src/generated/ast.dart:1110)

8 VariableDeclaration.visitChildren (package:analyzer/src/generated/ast.dart:11913)

9 ResolverVisitor.visitNode (package:analyzer/src/generated/resolver.dart:9177)

10 UnifyingAstVisitor.visitVariableDeclaration (package:analyzer/dart/ast/visitor.dart:1844)

11 ScopedVisitor.visitVariableDeclaration (package:analyzer/src/generated/resolver.dart:10628)

12 ResolverVisitor.visitVariableDeclaration (package:analyzer/src/generated/resolver.dart:9293)

13 VariableDeclaration.accept (package:analyzer/src/generated/ast.dart:11907)

14 NodeList.accept (package:analyzer/src/generated/ast.dart:8612)

15 VariableDeclarationList.visitChildren (package:analyzer/src/generated/ast.dart:12022)

16 ResolverVisitor.visitNode (package:analyzer/src/generated/resolver.dart:9177)

17 UnifyingAstVisitor.visitVariableDeclarationList (package:analyzer/dart/ast/visitor.dart:1848)

18 ResolverVisitor.visitVariableDeclarationList (package:analyzer/src/generated/resolver.dart:9318)

19 VariableDeclarationList.accept (package:analyzer/src/generated/ast.dart:12016)

20 AstNode._safelyVisitChild (package:analyzer/src/generated/ast.dart:1110)

21 VariableDeclarationStatement.visitChildren (package:analyzer/src/generated/ast.dart:12079)

22 ResolverVisitor.visitNode (package:analyzer/src/generated/resolver.dart:9177)

23 UnifyingAstVisitor.visitVariableDeclarationStatement (package:analyzer/dart/ast/visitor.dart:1852)

24 VariableDeclarationStatement.accept (package:analyzer/src/generated/ast.dart:12075)

25 NodeList.accept (package:analyzer/src/generated/ast.dart:8612)

26 Block.visitChildren (package:analyzer/src/generated/ast.dart:1595)

27 ResolverVisitor.visitNode (package:analyzer/src/generated/resolver.dart:9177)

28 UnifyingAstVisitor.visitBlock (package:analyzer/dart/ast/visitor.dart:1545)

29 ScopedVisitor.visitBlock (package:analyzer/src/generated/resolver.dart:10148)

30 Block.accept (package:analyzer/src/generated/ast.dart:1591)

31 AstNode._safelyVisitChild (package:analyzer/src/generated/ast.dart:1110)

32 BlockFunctionBody.visitChildren (package:analyzer/src/generated/ast.dart:1674)

33 ResolverVisitor.visitNode (package:analyzer/src/generated/resolver.dart:9177)

34 UnifyingAstVisitor.visitBlockFunctionBody (package:analyzer/dart/ast/visitor.dart:1548)

35 ScopedVisitor.visitBlockFunctionBody (package:analyzer/src/generated/resolver.dart:10160)

36 ResolverVisitor.visitBlockFunctionBody (package:analyzer/src/generated/resolver.dart:8445)

37 BlockFunctionBody.accept (package:analyzer/src/generated/ast.dart:1670)

38 AstNode._safelyVisitChild (package:analyzer/src/generated/ast.dart:1110)

39 MethodDeclaration.visitChildren (package:analyzer/src/generated/ast.dart:8046)

40 ResolverVisitor.visitNode (package:analyzer/src/generated/resolver.dart:9177)

41 UnifyingAstVisitor.visitMethodDeclaration (package:analyzer/dart/ast/visitor.dart:1726)

42 ScopedVisitor.visitMethodDeclarationInScope (package:analyzer/src/generated/resolver.dart:10554)

43 ResolverVisitor.visitMethodDeclarationInScope (package:analyzer/src/generated/resolver.dart:9138)

44 ScopedVisitor.visitMethodDeclaration (package:analyzer/src/generated/resolver.dart:10546)

45 ResolverVisitor.visitMethodDeclaration (package:analyzer/src/generated/resolver.dart:9129)

46 MethodDeclaration.accept (package:analyzer/src/generated/ast.dart:8037)

47 NodeList.accept (package:analyzer/src/generated/ast.dart:8612)

48 ScopedVisitor.visitClassMembersInScope (package:analyzer/src/generated/resolver.dart:10229)

49 ScopedVisitor.visitClassDeclaration (package:analyzer/src/generated/resolver.dart:10206)

50 ResolverVisitor.visitClassDeclaration (package:analyzer/src/generated/resolver.dart:8487)

51 ClassDeclaration.accept (package:analyzer/src/generated/ast.dart:2277)

52 ResolverVisitor.visitCompilationUnit (package:analyzer/src/generated/resolver.dart:8573)

53 CompilationUnit.accept (package:analyzer/src/generated/ast.dart:2896)

54 ResolveUnitTask.internalPerform (package:analyzer/src/task/dart.dart:4837)

55 AnalysisTask._safelyPerform (package:analyzer/task/model.dart:310)

56 AnalysisTask.perform (package:analyzer/task/model.dart:210)

57 AnalysisDriver.performWorkItem (package:analyzer/src/task/driver.dart:271)

58 AnalysisDriver.computeResult (package:analyzer/src/task/driver.dart:109)

59 AnalysisContextImpl.computeResult (package:analyzer/src/context/context.dart:647)

60 AnalysisContextImpl.resolveCompilationUnit2 (package:analyzer/src/context/context.dart:1166)

61 DartAssistContributor.computeAssists. (package:analysis_server/plugin/edit/assist/assist_dart.dart:64)

62 Future.Future.microtask. (dart:async/future.dart:144)

63 _rootRun (dart:async/zone.dart:903)

64 _CustomZone.run (dart:async/zone.dart:802)

65 _CustomZone.runGuarded (dart:async/zone.dart:708)

66 _CustomZone.bindCallback. (dart:async/zone.dart:733)

67 _rootRun (dart:async/zone.dart:907)

68 _CustomZone.run (dart:async/zone.dart:802)

69 _CustomZone.runGuarded (dart:async/zone.dart:708)

70 _CustomZone.bindCallback. (dart:async/zone.dart:733)

71 _microtaskLoop (dart:async/schedule_microtask.dart:41)

72 _startMicrotaskLoop (dart:async/schedule_microtask.dart:50)

73 _runPendingImmediateCallback (dart:isolate-patch/isolate_patch.dart:96)

74 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:149)

java.lang.Throwable at com.intellij.openapi.diagnostic.Logger.error(Logger.java:115) at com.jetbrains.lang.dart.analyzer.DartAnalysisServerService$1.serverError(DartAnalysisServerService.java:204) at com.google.dart.server.internal.BroadcastAnalysisServerListener.serverError(BroadcastAnalysisServerListener.java:180) at com.google.dart.server.internal.remote.processor.NotificationServerErrorProcessor.process(NotificationServerErrorProcessor.java:37) at com.google.dart.server.internal.remote.RemoteAnalysisServerImpl.processNotification(RemoteAnalysisServerImpl.java:502) at com.google.dart.server.internal.remote.RemoteAnalysisServerImpl.processResponse(RemoteAnalysisServerImpl.java:517) at com.google.dart.server.internal.remote.RemoteAnalysisServerImpl.access$600(RemoteAnalysisServerImpl.java:49) at com.google.dart.server.internal.remote.RemoteAnalysisServerImpl$ServerResponseReaderThread.run(RemoteAnalysisServerImpl.java:868) [3522267] ERROR - yzer.DartAnalysisServerService - WebStorm 11.0.3 Build #WS-143.1559.5 [3522267] ERROR - yzer.DartAnalysisServerService - JDK: 1.8.0_11 [3522267] ERROR - yzer.DartAnalysisServerService - VM: Java HotSpot(TM) 64-Bit Server VM [3522267] ERROR - yzer.DartAnalysisServerService - Vendor: Oracle Corporation [3522267] ERROR - yzer.DartAnalysisServerService - OS: Linux [3522267] ERROR - yzer.DartAnalysisServerService - Last Action: SaveAll

Dart VM version: 1.14.0-edge.a7ebeb00fbb09b774836239b31d2c7defcc12c9c (Wed Jan 6 17:25:11 2016) on "linux_x64"

  • .analysis_options.
analyzer:
  strong-mode: true
  exclude:
    - '**/*array*'
    - '**/array*'
  language:
    enableSuperMixins: true
linter:
  rules:
    - always_declare_return_types
    - always_specify_types
#    - avoid_as
    - camel_case_types
    - constant_identifier_names
    - empty_constructor_bodies
    - implementation_imports
    - library_names
    - library_prefixes
    - non_constant_identifier_names
    - one_member_abstracts
    - package_api_docs
    - package_prefixed_library_names
    - prefer_is_not_empty
    - slash_for_doc_comments
    - super_goes_last
    - type_annotate_public_apis
    - type_init_formals
#    - unnecessary_brace_in_string_interp
    - unnecessary_getters_setters
    - package_names
/// @license
/// Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
/// This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
/// The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
/// The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
/// Code distributed by Google as part of the polymer project is also
/// subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt

library bwu_ng_iron_list;

import 'dart:math' show max, min;
import 'dart:html' show CssStyleDeclaration, Element, window;
import 'dart:async' show Future;
import 'package:angular2/angular2.dart';

//<link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html">

/// `iron-list` displays a virtual, 'infinite' list. The template inside
/// the iron-list element represents the DOM to create for each list item.
/// The `items` property specifies an array of list item data.
///
/// For performance reasons, not every item in the list is rendered at once;
/// instead a small subset of actual template elements *(enough to fill the viewport)*
/// are rendered and reused as the user scrolls. As such, it is important that all
/// state of the list template be bound to the model driving it, since the view may
/// be reused with a new model at any time. Particularly, any state that may change
/// as the result of a user interaction with the list item must be bound to the model
/// to avoid view state inconsistency.
///
/// __Important:__ `iron-list` must either be explicitly sized, or delegate scrolling to an
/// explicitly sized parent. By "explicitly sized", we mean it either has an explicit
/// CSS `height` property set via a class or inline style, or else is sized by other
/// layout means (e.g. the `flex` or `fit` classes).
///
/// ### Template model
///
/// List item templates should bind to template models of the following structure:
///
///     {
///       index: 0,     // data index for this item
///       item: {       // user data corresponding to items[index]
///         /* user item data
///       }
///     }
///
/// Alternatively, you can change the property name used as data index by changing the
/// `indexAs` property. The `as` property defines the name of the variable to add to the binding
/// scope for the array.
///
/// For example, given the following `data` array:
///
/// ##### data.json
///
/// ```js
/// [
///   {"name": "Bob"},
///   {"name": "Tim"},
///   {"name": "Mike"}
/// ]
/// ```
///
/// The following code would render the list (note the name and checked properties are
/// bound from the model object provided to the template scope):
///
/// ```html
/// <template is="dom-bind">
///   <iron-ajax url="data.json" last-response="{{data}}" auto></iron-ajax>
///   <iron-list items="[[data]]" as="item">
///     <template>
///       <div>
///         Name: <span>[[item.name]]</span>
///       </div>
///     </template>
///   </iron-list>
/// </template>
/// ```
///
/// ### Styling
///
/// Use the `--iron-list-items-container` mixin to style the container of items, e.g.
///
/// ```css
/// iron-list {
///  --iron-list-items-container: {
///     margin: auto;
///   };
/// }
/// ```
///
/// ### Resizing
///
/// `iron-list` lays out the items when it receives a notification via the `iron-resize` event.
/// This event is fired by any element that implements `IronResizableBehavior`.
///
/// By default, elements such as `iron-pages`, `paper-tabs` or `paper-dialog` will trigger
/// this event automatically. If you hide the list manually (e.g. you use `display: none`)
/// you might want to implement `IronResizableBehavior` or fire this event manually right
/// after the list became visible again. e.g.
///
/// ```js
/// document.querySelector('iron-list').fire('iron-resize');
/// ```
///
/// ### When should `<iron-list>` be used?
///
/// `iron-list` should be used when a page has significantly more DOM nodes than the ones
/// visible on the screen. e.g. the page has 500 nodes, but only 20 are visible at the time.
/// This is why we refer to it as a `virtual` list. In this case, a `dom-repeat` will still
/// create 500 nodes which could slow down the web app, but `iron-list` will only create 20.
///
/// However, having an `iron-list` does not mean that you can load all the data at once.
/// Say, you have a million records in the database, you want to split the data into pages
/// so you can bring a page at the time. The page could contain 500 items, and iron-list
/// will only render 20.
///
/// @group Iron Element
/// @element iron-list
/// @demo demo/index.html Simple list
/// @demo demo/selection.html Selection of items
/// @demo demo/collapse.html Collapsable items

@Component(selector: 'bwu-iron-list')
@View(templateUrl: 'bwu_ng_iron_list.html'
//    directives: const [FORM_DIRECTIVES]
    )
class BwuIronList implements AfterViewInit {
  static final List<Match> IOS =
      new RegExp(r'iP(?:hone|ad;(?: U;)? CPU) OS (\d+)')
          .allMatches(window.navigator.userAgent);
  static final bool IOS_TOUCH_SCROLLING = IOS.isNotEmpty &&
      IOS[1].start >=
          8; // TODO(zoechi) check if this works properly (especiall '.start'
  static final int DEFAULT_PHYSICAL_COUNT = 3;
  static final int MAX_PHYSICAL_COUNT = 500;

  /// An array containing items determining how many instances of the template
  /// to stamp and that that each template instance should bind to.
  @Input() List items;

  /// The name of the variable to add to the binding scope for the array
  /// element associated with a given template instance.
  @Input() String as = 'item';

  /// The name of the variable to add to the binding scope with the index
  /// for the row.
  @Input() String indexAs = 'index';

  /// The name of the variable to add to the binding scope to indicate
  /// if the row is selected.
  @Input() String selectedAs = 'selected';

  /// When true, tapping a row will select the item, placing its data model
  /// in the set of selected items retrievable via the selection property.
  ///
  /// Note that tapping focusable elements within the list item will not
  /// result in selection, since they are presumed to have their * own action.
  @Input() bool selectionEnabled = false;

  /// When `multiSelection` is false, this is the currently selected item, or `null`
  /// if no item is selected.
  @Input() dynamic selectedItem;
  @Output() EventEmitter selectedItemChange = new EventEmitter();

  /// When `multiSelection` is true, this is an array that contains the selected items.
  @Input() dynamic selectedItems;
  @Output() EventEmitter selectedItemsChange = new EventEmitter();

  /// When `true`, multiple items may be selected at once (in this case,
  /// `selected` is an array of currently selected items).  When `false`,
  /// only one item may be selected at a time.
  @Input() bool multiSelection = false;

// TODO(zoechi)
//    observers: [
//      '_itemsChanged(items.*)',
//      '_selectionEnabledChanged(selectionEnabled)',
//      '_multiSelectionChanged(multiSelection)'
//    ],
//
//    behaviors: [
//      Polymer.Templatizer,
//      Polymer.IronResizableBehavior
//    ],

  @HostBinding('iron-resize') void _resizeHandler2(e) => _resizeHandler();

  /// The ratio of hidden tiles that should remain in the scroll direction.
  /// Recommended value ~0.5, so it will distribute tiles evely in both directions.
  double _ratio = 0.5;

  /// The element that controls the scroll
  /// @type {?Element}
  Element _scroller = null;

  /// The padding-top value of the `scroller` element
  int _scrollerPaddingTop = 0;

  /// This value is the same as `scrollTop`.
  int _scrollPosition = 0;

  /// The number of tiles in the DOM.
  int _physicalCount = 0;

  /// The k-th tile that is at the top of the scrolling list.
  int _physicalStart = 0;

  /// The k-th tile that is at the bottom of the scrolling list.
  int _physicalEnd = 0;

  /// The sum of the heights of all the tiles in the DOM.
  int _physicalSize = 0;

  /// The average `offsetHeight` of the tiles observed till now.

  int _physicalAverage = 0;

  /// The number of tiles which `offsetHeight` > 0 observed until now.

  int _physicalAverageCount = 0;

  /// The Y position of the item rendered in the `_physicalStart`
  /// tile relative to the scrolling list.

  int _physicalTop = 0;

  /// The number of items in the list.

  int _virtualCount = 0;

  /// The n-th item rendered in the `_physicalStart` tile.

  int _virtualStartVal = 0;

  /// A map between an item key and its physical item index

  Map<dynamic, int> _physicalIndexForKey = null;

  /// The estimated scroll height based on `_physicalAverage`

  int _estScrollHeight = 0;

  /// The scroll height of the dom node

  int _scrollHeight = 0;

  /// The height of the list. This is referred as the viewport in the context of list.

  int _viewportSize = 0;

  /// An array of DOM nodes that are currently in the tree
  /// @type {?Array<!TemplatizerNode>}

  List _physicalItems = null;

  /// An array of heights for each item in `_physicalItems`
  /// @type {?Array<number>}

  List _physicalSizes = null;

  /// A cached value for the first visible index.
  /// See `firstVisibleIndex`
  /// @type {?number}

  List _firstVisibleIndexVal = null;

  /// A cached value for the last visible index.
  /// See `lastVisibleIndex`
  /// @type {?number}

  List _lastVisibleIndexVal = null;

  /// A Polymer collection for the items.
  /// @type {?Polymer.Collection}

  List _collection = null;

  /// True if the current item list was rendered for the first time
  /// after attached.

  bool _itemsRendered = false;

  /// The page that is currently rendered.

  int _lastPage = null;

  /// The max number of pages to render. One page is equivalent to the height of the list.

  int _maxPages = 3;

  /// The bottom of the physical content.

  int get _physicalBottom => _physicalTop + _physicalSize;

  /// The bottom of the scroll.

  int get _scrollBottom => _scrollPosition + _viewportSize;

  /// The n-th item rendered in the last physical item.

  int get _virtualEnd => _virtualStartVal + _physicalCount - 1;

  /// The lowest n-th value for an item such that it can be rendered in `_physicalStart`.

  int _minVirtualStart = 0;

  /// The largest n-th value for an item such that it can be rendered in `_physicalStart`.

  int get _maxVirtualStart => max(0, _virtualCount - _physicalCount);

  /// The height of the physical content that isn't on the screen.

  int get _hiddenContentSize => _physicalSize - _viewportSize;

  /// The maximum scroll top value.

  int get _maxScrollTop => _estScrollHeight - _viewportSize;

  /// Sets the n-th item rendered in `_physicalStart`

  void set _virtualStart(int val) {
    // clamp the value so that _minVirtualStart <= val <= _maxVirtualStart
    _virtualStartVal = min(_maxVirtualStart, max(_minVirtualStart, val));
    _physicalStart = _virtualStartVal % _physicalCount;
    _physicalEnd = (_physicalStart + _physicalCount - 1) % _physicalCount;
  }

  /// Gets the n-th item rendered in `_physicalStart`

  int get _virtualStart => _virtualStartVal;

  /// An optimal physical size such that we will have enough physical items
  /// to fill up the viewport and recycle when the user scrolls.

  /// This default value assumes that we will at least have the equivalent
  /// to a viewport of physical items above and below the user's viewport.

  int get _optPhysicalSize => _viewportSize * _maxPages;

  /// True if the current list is visible.

  bool get _isVisible =>
      _scroller != null &&
      (_scroller.offsetWidth > 0 || _scroller.offsetHeight > 0);

  /// Gets the index of the first visible item in the viewport.
  ///
  /// @type {number}

  int get firstVisibleIndex {
    int physicalOffset;

    if (_firstVisibleIndexVal == null) {
      physicalOffset = _physicalTop;

      _firstVisibleIndexVal = _iterateItems((int pidx, int vidx) {
            physicalOffset += _physicalSizes[pidx];

            if (physicalOffset > _scrollPosition) {
              return vidx;
            }
          }) ??
          0;
    }

    return _firstVisibleIndexVal;
  }

  /// Gets the index of the last visible item in the viewport.
  ///
  /// @type {number}

  int get lastVisibleIndex {
    int physicalOffset;

    if (_lastVisibleIndexVal == null) {
      physicalOffset = _physicalTop;

      _iterateItems((pidx, vidx) {
        physicalOffset += _physicalSizes[pidx];

        if (physicalOffset <= _scrollBottom) {
          _lastVisibleIndexVal = vidx;
        }
      });
    }

    return _lastVisibleIndexVal;
  }

  void ngAfterViewInit() {
    if (IOS_TOUCH_SCROLLING) {
      _scrollListener = () {
        window.requestAnimationFrame(_scrollHandler);
      };
    } else {
      _scrollListener = _scrollHandler;
    }
  }

  /// When the element has been attached to the DOM tree.

  /// TODO(zoechi) replace attached with proper Angular lifecycle method
  attached() {
    // delegate to the parent's scroller
    // e.g. paper-scroll-header-panel
    var el = Polymer.dom(this);

    var parentNode = /*@type {?{scroller: ?Element}} */ (el.parentNode);
    if (parentNode && parentNode.scroller) {
      _scroller = parentNode.scroller;
    } else {
      _scroller = this;
      classes.add('has-scroller');
    }

    if (IOS_TOUCH_SCROLLING) {
      _scroller.style.setProperty('webkitOverflowScrolling', 'touch');
    }

    _scroller.addEventListener('scroll', _scrollListener);

    updateViewportBoundaries();
    _render();
  }

  /// When the element has been removed from the DOM tree.
  /// TODO(zoechi) replace attached with proper Angular lifecycle method

  detached() {
    _itemsRendered = false;
    if (_scroller) {
      _scroller.removeEventListener('scroll', _scrollListener);
    }
  }

  /// Invoke this method if you dynamically update the viewport's
  /// size or CSS padding.
  ///
  /// @method updateViewportBoundaries

  void updateViewportBoundaries() {
    CssStyleDeclaration scrollerStyle = _scroller.getComputedStyle();
    _scrollerPaddingTop = parseInt(scrollerStyle['padding-top'], 10);
    _viewportSize = _scroller.offsetHeight;
  }

  /// Update the models, the position of the
  /// items in the viewport and recycle tiles as needed.

  _refresh() {
    // clamp the `scrollTop` value
    // IE 10|11 scrollTop may go above `_maxScrollTop`
    // iOS `scrollTop` may go below 0 and above `_maxScrollTop`
    int scrollTop = max(0, min(_maxScrollTop, _scroller.scrollTop));
    int tileHeight, tileTop, kth, scrollBottom, physicalBottom;
    List<int> recycledTileSet;
    double ratio = _ratio;
    int delta = scrollTop - _scrollPosition;
    int recycledTiles = 0;
    int hiddenContentSize = _hiddenContentSize;
    double currentRatio = ratio;
    List<int> movingUp = [];

    // track the last `scrollTop`
    _scrollPosition = scrollTop;

    // clear cached visible index
    _firstVisibleIndexVal = null;
    _lastVisibleIndexVal = null;

    scrollBottom = _scrollBottom;
    physicalBottom = _physicalBottom;

    // random access
    if (delta.abs() > _physicalSize) {
      _physicalTop += delta;
      recycledTiles = (delta / _physicalAverage).round();
    }
    // scroll up
    else if (delta < 0) {
      int topSpace = scrollTop - _physicalTop;
      int virtualStart = _virtualStart;

      recycledTileSet = [];

      kth = _physicalEnd;
      currentRatio = topSpace / hiddenContentSize;

      // move tiles from bottom to top
      while (
          // approximate `currentRatio` to `ratio`
          currentRatio < ratio &&
              // recycle less physical items than the total
              recycledTiles < _physicalCount &&
              // ensure that these recycled tiles are needed
              virtualStart - recycledTiles > 0 &&
              // ensure that the tile is not visible
              physicalBottom - _physicalSizes[kth] > scrollBottom) {
        tileHeight = _physicalSizes[kth];
        currentRatio += tileHeight / hiddenContentSize;
        physicalBottom -= tileHeight;
        recycledTileSet.add(kth);
        recycledTiles++;
        kth = (kth == 0) ? _physicalCount - 1 : kth - 1;
      }

      movingUp = recycledTileSet;
      recycledTiles = -recycledTiles;
    }
    // scroll down
    else if (delta > 0) {
      int bottomSpace = physicalBottom - scrollBottom;
      int virtualEnd = _virtualEnd;
      int lastVirtualItemIndex = _virtualCount - 1;

      recycledTileSet = [];

      kth = _physicalStart;
      currentRatio = bottomSpace / hiddenContentSize;

      // move tiles from top to bottom
      while (
          // approximate `currentRatio` to `ratio`
          currentRatio < ratio &&
              // recycle less physical items than the total
              recycledTiles < _physicalCount &&
              // ensure that these recycled tiles are needed
              virtualEnd + recycledTiles < lastVirtualItemIndex &&
              // ensure that the tile is not visible
              _physicalTop + _physicalSizes[kth] < scrollTop) {
        tileHeight = _physicalSizes[kth];
        currentRatio += tileHeight / hiddenContentSize;

        _physicalTop += tileHeight;
        recycledTileSet.add(kth);
        recycledTiles++;
        kth = (kth + 1) % _physicalCount;
      }
    }

    if (recycledTiles == 0) {
      // If the list ever reach this case, the physical average is not significant enough
      // to create all the items needed to cover the entire viewport.
      // e.g. A few items have a height that differs from the average by serveral order of magnitude.
      if (physicalBottom < scrollBottom || _physicalTop > scrollTop) {
        new Future.delayed(const Duration(milliseconds: 1), _increasePool);
      }
    } else {
      _virtualStart = _virtualStart + recycledTiles;
      _update(recycledTileSet, movingUp);
    }
  }

  /// Update the list of items, starting from the `_virtualStartVal` item.
  /// @param {!Array<number>=} itemSet
  /// @param {!Array<number>=} movingUp

  void _update(List<int> itemSet, List<int> movingUp) {
    // update models
    _assignModels(itemSet);

    // measure heights
    _updateMetrics(itemSet);

    // adjust offset after measuring
    if (movingUp != null && movingUp.isNotEmpty) {
      while (movingUp.length > 0) {
        _physicalTop -= _physicalSizes[movingUp.removeLast()];
      }
    }
    // update the position of the items
    _positionItems();

    // set the scroller size
    _updateScrollerSize();

    // increase the pool of physical items
    _increasePoolIfNeeded();
  }

  /// Creates a pool of DOM elements and attaches them to the local dom.

  _createPool(int size) {
    var physicalItems = new List(size);

    _ensureTemplatized();

    for (var i = 0; i < size; i++) {
      var inst = stamp(null);
      // First element child is item; Safari doesn't support children[0]
      // on a doc fragment
      physicalItems[i] = inst.root.querySelector('*');
      Polymer.dom(this).appendChild(inst.root);
    }

    return physicalItems;
  }

  /// Increases the pool of physical items only if needed.
  /// This function will allocate additional physical items
  /// if the physical size is shorter than `_optPhysicalSize`

  void _increasePoolIfNeeded() {
    if (_viewportSize != 0 && _physicalSize < _optPhysicalSize) {
      // 0 <= `currentPage` <= `_maxPages`
      var currentPage = (_physicalSize / _viewportSize).floor();

      if (currentPage == 0) {
        // fill the first page
        new Future.delayed(
            new Duration(milliseconds: (_physicalCount * 0.5).round()),
            _increasePool);
      } else if (_lastPage != currentPage) {
        // once a page is filled up, paint it and defer the next increase
        window.requestAnimationFrame(() => _increasePool(1));
      } else {
        // fill the rest of the pages
        async(_increasePool.bind(this, 1));
      }
      _lastPage = currentPage;
      return true;
    }
    return false;
  }

  /// Increases the pool size.

  _increasePool(missingItems) {
    // limit the size
    var nextPhysicalCount =
        min(_physicalCount + missingItems, _virtualCount, MAX_PHYSICAL_COUNT);
    var prevPhysicalCount = _physicalCount;
    var delta = nextPhysicalCount - prevPhysicalCount;

    if (delta > 0) {
      [].push.apply(_physicalItems, _createPool(delta));
      [].push.apply(_physicalSizes, new List(delta));

      _physicalCount = prevPhysicalCount + delta;
      // tail call
      return _update();
    }
  }

  /// Render a new list of items. This method does exactly the same as `update`,
  /// but it also ensures that only one `update` cycle is created.

  _render() {
    var requiresUpdate = _virtualCount > 0 || _physicalCount > 0;

    if (isAttached && !_itemsRendered && _isVisible && requiresUpdate) {
      _lastPage = 0;
      _update();
      _itemsRendered = true;
    }
  }

  /// Templetizes the user template.

  _ensureTemplatized() {
    if (!ctor) {
      // Template instance props that should be excluded from forwarding
      var props = {};

      props.__key__ = true;
      props[as] = true;
      props[indexAs] = true;
      props[selectedAs] = true;

      _instanceProps = props;
      _userTemplate = Polymer.dom(this).querySelector('template');

      if (_userTemplate) {
        templatize(_userTemplate);
      } else {
        console
            .warn('iron-list requires a template to be provided in light-dom');
      }
    }
  }

  /// Implements extension point from Templatizer mixin.

  _getStampedChildren() {
    return _physicalItems;
  }

  /// Implements extension point from Templatizer
  /// Called as a side effect of a template instance path change, responsible
  /// for notifying items.<key-for-instance>.<path> change up to host.

  _forwardInstancePath(inst, path, value) {
    if (path.indexOf(as + '.') == 0) {
      notifyPath(
          'items.' + inst.__key__ + '.' + path.slice(as.length + 1), value);
    }
  }

  /// Implements extension point from Templatizer mixin
  /// Called as side-effect of a host property change, responsible for
  /// notifying parent path change on each row.

  _forwardParentProp(prop, value) {
    if (_physicalItems) {
      _physicalItems.forEach((item) {
        item._templateInstance[prop] = value;
      }, this);
    }
  }

  /// Implements extension point from Templatizer
  /// Called as side-effect of a host path change, responsible for
  /// notifying parent.<path> path change on each row.

  _forwardParentPath(path, value) {
    if (_physicalItems) {
      _physicalItems.forEach((item) {
        item._templateInstance.notifyPath(path, value, true);
      }, this);
    }
  }

  /// Called as a side effect of a host items.<key>.<path> path change,
  /// responsible for notifying item.<path> changes to row for key.

  _forwardItemPath(path, value) {
    if (_physicalIndexForKey) {
      var dot = path.indexOf('.');
      var key = path.substring(0, dot < 0 ? path.length : dot);
      var idx = _physicalIndexForKey[key];
      var row = _physicalItems[idx];
      if (row) {
        var inst = row._templateInstance;
        if (dot >= 0) {
          path = as + '.' + path.substring(dot + 1);
          inst.notifyPath(path, value, true);
        } else {
          inst[as] = value;
        }
      }
    }
  }

  /// Called when the items have changed. That is, ressignments
  /// to `items`, splices or updates to a single item.

  _itemsChanged(change) {
    if (change.path == 'items') {
      // render the new set
      _itemsRendered = false;

      // update the whole set
      _virtualStartVal = 0;
      _physicalTop = 0;
      _virtualCount = items ? items.length : 0;
      _collection = items ? Polymer.Collection.get(items) : null;
      _physicalIndexForKey = <dynamic, int>{};

      // scroll to the top
      _resetScrollPosition(0);

      // create the initial physical items
      if (!_physicalItems) {
        _physicalCount =
            Math.max(1, Math.min(DEFAULT_PHYSICAL_COUNT, _virtualCount));
        _physicalItems = _createPool(_physicalCount);
        _physicalSizes = new List(_physicalCount);
      }

      debounce('refresh', _render);
    } else if (change.path == 'items.splices') {
      // render the new set
      _itemsRendered = false;

      _adjustVirtualIndex(change.value.indexSplices);
      _virtualCount = items ? items.length : 0;

      debounce('refresh', _render);
    } else {
      // update a single item
      _forwardItemPath(change.path.split('.').slice(1).join('.'), change.value);
    }
  }

  /// @param {!Array<!PolymerSplice>} splices

  _adjustVirtualIndex(splices) {
    var i, splice, idx;

    for (i = 0; i < splices.length; i++) {
      splice = splices[i];

      // deselect removed items
      splice.removed.forEach($.selector.deselect, $.selector);

      idx = splice.index;
      // We only need to care about changes happening above the current position
      if (idx >= _virtualStartVal) {
        break;
      }

      _virtualStart = _virtualStart +
          Math.max(splice.addedCount - splice.removed.length,
              idx - _virtualStartVal);
    }
  }

  _scrollHandler() {
    _refresh();
  }

  /// Executes a provided function per every physical index in `itemSet`
  /// `itemSet` default value is equivalent to the entire set of physical indexes.
  ///
  /// @param {!function(number, number)} fn
  /// @param {!Array<number>=} itemSet

  _iterateItems(Function fn, List<int> itemSet) {
    var pidx, vidx, rtn, i;

    if (arguments.length == 2 && itemSet) {
      for (i = 0; i < itemSet.length; i++) {
        pidx = itemSet[i];
        if (pidx >= _physicalStart) {
          vidx = _virtualStartVal + (pidx - _physicalStart);
        } else {
          vidx = _virtualStartVal + (_physicalCount - _physicalStart) + pidx;
        }
        if ((rtn = fn.call(this, pidx, vidx)) != null) {
          return rtn;
        }
      }
    } else {
      pidx = _physicalStart;
      vidx = _virtualStartVal;

      for (; pidx < _physicalCount; pidx++, vidx++) {
        if ((rtn = fn.call(this, pidx, vidx)) != null) {
          return rtn;
        }
      }

      pidx = 0;

      for (; pidx < _physicalStart; pidx++, vidx++) {
        if ((rtn = fn.call(this, pidx, vidx)) != null) {
          return rtn;
        }
      }
    }
  }

  /// Assigns the data models to a given set of items.
  /// @param {!Array<number>=} itemSet

  _assignModels(itemSet) {
    _iterateItems((pidx, vidx) {
      var el = _physicalItems[pidx];
      var inst = el._templateInstance;
      var item = items && items[vidx];

      if (item) {
        inst[as] = item;
        inst.__key__ = _collection.getKey(item);
        inst[selectedAs] =
            /* @type {!ArraySelectorElement} */ ($.selector).isSelected(item);
        inst[indexAs] = vidx;
        el.removeAttribute('hidden');
        _physicalIndexForKey[inst.__key__] = pidx;
      } else {
        inst.__key__ = null;
        el.setAttribute('hidden', '');
      }
    }, itemSet);
  }

  /// Updates the height for a given set of items.
  ///
  /// @param {!Array<number>=} itemSet

  _updateMetrics(itemSet) {
    var newPhysicalSize = 0;
    var oldPhysicalSize = 0;
    var prevAvgCount = _physicalAverageCount;
    var prevPhysicalAvg = _physicalAverage;
    // Make sure we distributed all the physical items
    // so we can measure them
    Polymer.dom.flush();

    _iterateItems((pidx, vidx) {
      oldPhysicalSize += _physicalSizes[pidx] || 0;
      _physicalSizes[pidx] = _physicalItems[pidx].offsetHeight;
      newPhysicalSize += _physicalSizes[pidx];
      _physicalAverageCount += _physicalSizes[pidx] ? 1 : 0;
    }, itemSet);

    _physicalSize = _physicalSize + newPhysicalSize - oldPhysicalSize;
    _viewportSize = _scroller.offsetHeight;

    // update the average if we measured something
    if (_physicalAverageCount != prevAvgCount) {
      _physicalAverage = Math.round(
          ((prevPhysicalAvg * prevAvgCount) + newPhysicalSize) /
              _physicalAverageCount);
    }
  }

  /// Updates the position of the physical items.

  _positionItems() {
    _adjustScrollPosition();

    var y = _physicalTop;

    _iterateItems((pidx) {
      transform('translate3d(0, ' + y + 'px, 0)', _physicalItems[pidx]);
      y += _physicalSizes[pidx];
    });
  }

  /// Adjusts the scroll position when it was overestimated.

  _adjustScrollPosition() {
    var deltaHeight = _virtualStartVal == 0
        ? _physicalTop
        : Math.min(_scrollPosition + _physicalTop, 0);

    if (deltaHeight) {
      _physicalTop = _physicalTop - deltaHeight;

      // juking scroll position during interial scrolling on iOS is no bueno
      if (!IOS_TOUCH_SCROLLING) {
        _resetScrollPosition(_scroller.scrollTop - deltaHeight);
      }
    }
  }

  /// Sets the position of the scroll.

  _resetScrollPosition(pos) {
    if (_scroller) {
      _scroller.scrollTop = pos;
      _scrollPosition = _scroller.scrollTop;
    }
  }

  /// Sets the scroll height, that's the height of the content,
  ///
  /// @param {boolean=} forceUpdate If true, updates the height no matter what.

  _updateScrollerSize([bool forceUpdate = false]) {
    _estScrollHeight = (_physicalBottom +
        Math.max(_virtualCount - _physicalCount - _virtualStartVal, 0) *
            _physicalAverage);

    forceUpdate = forceUpdate || _scrollHeight == 0;
    forceUpdate =
        forceUpdate || _scrollPosition >= _estScrollHeight - _physicalSize;

    // amortize height adjustment, so it won't trigger repaints very often
    if (forceUpdate ||
        Math.abs(_estScrollHeight - _scrollHeight) >= _optPhysicalSize) {
      $.items.style.height = _estScrollHeight + 'px';
      _scrollHeight = _estScrollHeight;
    }
  }

  /// Scroll to a specific item in the virtual list regardless
  /// of the physical items in the DOM tree.
  ///
  /// @method scrollToIndex
  /// @param {number} idx The index of the item

  scrollToIndex(idx) {
    if (idx is! num) {
      return;
    }

    var firstVisible = firstVisibleIndex;

    idx = Math.min(Math.max(idx, 0), _virtualCount - 1);

    // start at the previous virtual item
    // so we have a item above the first visible item
    _virtualStart = idx - 1;

    // assign new models
    _assignModels();

    // measure the new sizes
    _updateMetrics();

    // estimate new physical offset
    _physicalTop = _virtualStart * _physicalAverage;

    var currentTopItem = _physicalStart;
    var currentVirtualItem = _virtualStart;
    var targetOffsetTop = 0;
    var hiddenContentSize = _hiddenContentSize;

    // scroll to the item as much as we can
    while (currentVirtualItem < idx && targetOffsetTop < hiddenContentSize) {
      targetOffsetTop = targetOffsetTop + _physicalSizes[currentTopItem];
      currentTopItem = (currentTopItem + 1) % _physicalCount;
      currentVirtualItem++;
    }

    // update the scroller size
    _updateScrollerSize(true);

    // update the position of the items
    _positionItems();

    // set the new scroll position
    _resetScrollPosition(_physicalTop + targetOffsetTop + 1);

    // increase the pool of physical items if needed
    _increasePoolIfNeeded();

    // clear cached visible index
    _firstVisibleIndexVal = null;
    _lastVisibleIndexVal = null;
  }

  /// Reset the physical average and the average count.

  _resetAverage() {
    _physicalAverage = 0;
    _physicalAverageCount = 0;
  }

  /// A handler for the `iron-resize` event triggered by `IronResizableBehavior`
  /// when the element is resized.

  _resizeHandler() {
    debounce('resize', () {
      _render();
      if (_itemsRendered && _physicalItems && _isVisible) {
        _resetAverage();
        updateViewportBoundaries();
        scrollToIndex(firstVisibleIndex);
      }
    });
  }

  _getModelFromItem(item) {
    var key = _collection.getKey(item);
    var pidx = _physicalIndexForKey[key];

    if (pidx != null) {
      return _physicalItems[pidx]._templateInstance;
    }
    return null;
  }

  /// Gets a valid item instance from its index or the object value.
  ///
  /// @param {(Object|number)} item The item object or its index

  _getNormalizedItem(item) {
    if (item is num) {
      item = items[item];
      if (!item) {
        throw new RangeError('<item> not found');
      }
    } else if (_collection.getKey(item) == undefined) {
      throw new TypeError('<item> should be a valid item');
    }
    return item;
  }

  /// Select the list item at the given index.
  ///
  /// @method selectItem
  /// @param {(Object|number)} item The item object or its index

  selectItem(item) {
    item = _getNormalizedItem(item);
    var model = _getModelFromItem(item);

    if (!multiSelection && selectedItem) {
      deselectItem(selectedItem);
    }
    if (model) {
      model[selectedAs] = true;
    }
    $.selector.select(item);
  }

  /// Deselects the given item list if it is already selected.
  ///

  /// @method deselect
  /// @param {(Object|number)} item The item object or its index

  deselectItem(item) {
    item = _getNormalizedItem(item);
    var model = _getModelFromItem(item);

    if (model) {
      model[selectedAs] = false;
    }
    $.selector.deselect(item);
  }

  /// Select or deselect a given item depending on whether the item
  /// has already been selected.
  ///
  /// @method toggleSelectionForItem
  /// @param {(Object|number)} item The item object or its index

  toggleSelectionForItem(item) {
    item = _getNormalizedItem(item);
    if (/*@type {!ArraySelectorElement} */ ($.selector).isSelected(item)) {
      deselectItem(item);
    } else {
      selectItem(item);
    }
  }

  /// Clears the current selection state of the list.
  ///
  /// @method clearSelection

  clearSelection() {
    unselect(item) {
      var model = _getModelFromItem(item);
      if (model) {
        model[selectedAs] = false;
      }
    }

    if (Array.isArray(selectedItems)) {
      selectedItems.forEach(unselect, this);
    } else if (selectedItem) {
      unselect.call(this, selectedItem);
    }

    /*@type {!ArraySelectorElement} */ ($.selector).clearSelection();
  }

  /// Add an event listener to `tap` if `selectionEnabled` is true,
  /// it will remove the listener otherwise.

  _selectionEnabledChanged(selectionEnabled) {
    if (selectionEnabled) {
      listen(this, 'tap', '_selectionHandler');
      listen(this, 'keypress', '_selectionHandler');
    } else {
      unlisten(this, 'tap', '_selectionHandler');
      unlisten(this, 'keypress', '_selectionHandler');
    }
  }

  /// Select an item from an event object.

  _selectionHandler(e) {
    if (e.type != 'keypress' || e.keyCode == 13) {
      var model = modelForElement(e.target);
      if (model) {
        toggleSelectionForItem(model[as]);
      }
    }
  }

  _multiSelectionChanged(multiSelection) {
    clearSelection();
    $.selector.multi = multiSelection;
  }

  /// Updates the size of an item.
  ///
  /// @method updateSizeForItem
  /// @param {(Object|number)} item The item object or its index

  updateSizeForItem(item) {
    item = _getNormalizedItem(item);
    var key = _collection.getKey(item);
    var pidx = _physicalIndexForKey[key];

    if (pidx != undefined) {
      _updateMetrics([pidx]);
      _positionItems();
    }
  }
}
zoechi commented 8 years ago

This worked wile I tried to port the JS code to Dart yesterday. I updated Dart from

Dart VM version: 1.14.0-edge.41b0e45f73083e5558d171204a0d90f017fe6763 (Wed Jan 6 05:05:40 2016) on "linux_x64"

to

Dart VM version: 1.14.0-edge.a7ebeb00fbb09b774836239b31d2c7defcc12c9c (Wed Jan 6 17:25:11 2016) on "linux_x64"

zoechi commented 8 years ago

Reverting to

Dart VM version: 1.14.0-edge.41b0e45f73083e5558d171204a0d90f017fe6763 (Wed Jan 6 05:05:40 2016) on "linux_x64"

fixes the issue

zoechi commented 8 years ago

Can't reproduce.