Skip to content

Commit eb4f469

Browse files
committed
Make LinkedMap iterator state aware
1 parent abc9127 commit eb4f469

4 files changed

Lines changed: 148 additions & 67 deletions

File tree

src/vs/base/common/map.ts

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -587,18 +587,22 @@ export class LinkedMap<K, V> {
587587
private _tail: Item<K, V> | undefined;
588588
private _size: number;
589589

590+
private _state: number;
591+
590592
constructor() {
591593
this._map = new Map<K, Item<K, V>>();
592594
this._head = undefined;
593595
this._tail = undefined;
594596
this._size = 0;
597+
this._state = 0;
595598
}
596599

597600
clear(): void {
598601
this._map.clear();
599602
this._head = undefined;
600603
this._tail = undefined;
601604
this._size = 0;
605+
this._state++;
602606
}
603607

604608
isEmpty(): boolean {
@@ -701,34 +705,18 @@ export class LinkedMap<K, V> {
701705
}
702706
}
703707

704-
values(): V[] {
705-
const result: V[] = [];
706-
let current = this._head;
707-
while (current) {
708-
result.push(current.value);
709-
current = current.next;
710-
}
711-
return result;
712-
}
713-
714-
keys(): K[] {
715-
const result: K[] = [];
716-
let current = this._head;
717-
while (current) {
718-
result.push(current.key);
719-
current = current.next;
720-
}
721-
return result;
722-
}
723-
724-
/* VS Code / Monaco editor runs on es5 which has no Symbol.iterator
725708
keys(): IterableIterator<K> {
726-
const current = this._head;
709+
const map = this;
710+
const state = this._state;
711+
let current = this._head;
727712
const iterator: IterableIterator<K> = {
728713
[Symbol.iterator]() {
729714
return iterator;
730715
},
731-
next():IteratorResult<K> {
716+
next(): IteratorResult<K> {
717+
if (map._state !== state) {
718+
throw new Error(`Map got modified during iteration.`);
719+
}
732720
if (current) {
733721
const result = { value: current.key, done: false };
734722
current = current.next;
@@ -742,12 +730,17 @@ export class LinkedMap<K, V> {
742730
}
743731

744732
values(): IterableIterator<V> {
745-
const current = this._head;
733+
const map = this;
734+
const state = this._state;
735+
let current = this._head;
746736
const iterator: IterableIterator<V> = {
747737
[Symbol.iterator]() {
748738
return iterator;
749739
},
750-
next():IteratorResult<V> {
740+
next(): IteratorResult<V> {
741+
if (map._state !== state) {
742+
throw new Error(`Map got modified during iteration.`);
743+
}
751744
if (current) {
752745
const result = { value: current.value, done: false };
753746
current = current.next;
@@ -759,7 +752,34 @@ export class LinkedMap<K, V> {
759752
};
760753
return iterator;
761754
}
762-
*/
755+
756+
entries(): IterableIterator<[K, V]> {
757+
const map = this;
758+
const state = this._state;
759+
let current = this._head;
760+
const iterator: IterableIterator<[K, V]> = {
761+
[Symbol.iterator]() {
762+
return iterator;
763+
},
764+
next(): IteratorResult<[K, V]> {
765+
if (map._state !== state) {
766+
throw new Error(`Map got modified during iteration.`);
767+
}
768+
if (current) {
769+
const result: IteratorResult<[K, V]> = { value: [current.key, current.value], done: false };
770+
current = current.next;
771+
return result;
772+
} else {
773+
return { value: undefined, done: true };
774+
}
775+
}
776+
};
777+
return iterator;
778+
}
779+
780+
[Symbol.iterator](): IterableIterator<[K, V]> {
781+
return this.entries();
782+
}
763783

764784
protected trimOld(newSize: number) {
765785
if (newSize >= this.size) {
@@ -781,6 +801,7 @@ export class LinkedMap<K, V> {
781801
if (current) {
782802
current.previous = undefined;
783803
}
804+
this._state++;
784805
}
785806

786807
private addItemFirst(item: Item<K, V>): void {
@@ -794,6 +815,7 @@ export class LinkedMap<K, V> {
794815
this._head.previous = item;
795816
}
796817
this._head = item;
818+
this._state++;
797819
}
798820

799821
private addItemLast(item: Item<K, V>): void {
@@ -807,6 +829,7 @@ export class LinkedMap<K, V> {
807829
this._tail.next = item;
808830
}
809831
this._tail = item;
832+
this._state++;
810833
}
811834

812835
private removeItem(item: Item<K, V>): void {
@@ -843,6 +866,7 @@ export class LinkedMap<K, V> {
843866
}
844867
item.next = undefined;
845868
item.previous = undefined;
869+
this._state++;
846870
}
847871

848872
private touch(item: Item<K, V>, touch: Touch): void {
@@ -879,6 +903,7 @@ export class LinkedMap<K, V> {
879903
item.next = this._head;
880904
this._head.previous = item;
881905
this._head = item;
906+
this._state++;
882907
} else if (touch === Touch.AsNew) {
883908
if (item === this._tail) {
884909
return;
@@ -902,6 +927,7 @@ export class LinkedMap<K, V> {
902927
item.previous = this._tail;
903928
this._tail.next = item;
904929
this._tail = item;
930+
this._state++;
905931
}
906932
}
907933

@@ -953,8 +979,8 @@ export class LRUCache<K, V> extends LinkedMap<K, V> {
953979
this.checkTrim();
954980
}
955981

956-
get(key: K): V | undefined {
957-
return super.get(key, Touch.AsNew);
982+
get(key: K, touch: Touch = Touch.AsNew): V | undefined {
983+
return super.get(key, touch);
958984
}
959985

960986
peek(key: K): V | undefined {

0 commit comments

Comments
 (0)