mirror of
https://github.com/microsoft/vscode.git
synced 2026-08-15 18:37:00 +01:00
* feat: update to Electron v42
* chore: drop support for arm 32-bit server
* chore: update types/node to v24.x
* chore: temporarily lock the target version for build/
* chore: update v8-source-location.patch
* chore: fix clippy
* chore: cleanup armhf server ci config
* fix: broken lock file
* fix: c++ version requirement for sysroot builds
* fix: msvc compilation of native modules
* fix: handle rejections for fire-and-forget loadurl
* fix: windows build
* ci: fix teardown of daemon process on windows
```
2026-05-15T20:55:09.7717127Z Assertion failed: !(handle->flags & UV_HANDLE_CLOSING), file src\win\async.c, line 76
```
* chore: update deb and rpm dependencies
* chore: update version info
* spec: improve reliablity of offscreencanvas tests
* spec: retry EPERM failures on teardown
* chore: update x86_64 rpm deps
* ci: exclude server binskim for armhf
* temp: bump distro
* test: ignore deprecation warnings treated as errors
* chore: update lockfile
* fix: externalize electron from auth extension bundles
Error: Cannot find module 'c:\Users\cloudtest\AppData\Local\Temp\vscode-sanityQvCaze\vscode-server-win32-x64-web\extensions\github-authentication\dist\install.js'
at Module._resolveFilename (node:internal/modules/cjs/loader:1476:15)
at wrapResolveFilename (node:internal/modules/cjs/loader:1049:27)
at defaultResolveImplForCJSLoading (node:internal/modules/cjs/loader:1073:10)
at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1094:12)
at Module._load (node:internal/modules/cjs/loader:1262:25)
at wrapModuleLoad (node:internal/modules/cjs/loader:255:19)
at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5)
at node:internal/main/run_main_module:33:47 {
code: 'MODULE_NOT_FOUND',
requireStack: []
}
Node.js v24.15.0
* test: make wsl sanity tests reliable
* chore: bump electron@42.1.0
* temp: bump distro
* chore: bump electron@42.2.0
* chore: bump distro
* chore: update debian dependencies
* Revert "test: make wsl sanity tests reliable"
This reverts commit b3f2b63e83.
* test: do not fail for deprecation warnings
* chore: patch node24 server binary for wsl1
* chore: address review feedback
* chore: revert global navigation error handler in browserview
* chore: bump distro
489 lines
10 KiB
TypeScript
489 lines
10 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
interface Item<K, V> {
|
|
previous: Item<K, V> | undefined;
|
|
next: Item<K, V> | undefined;
|
|
key: K;
|
|
value: V;
|
|
}
|
|
|
|
const enum Touch {
|
|
None = 0,
|
|
AsOld = 1,
|
|
AsNew = 2
|
|
}
|
|
|
|
class LinkedMap<K, V> implements Map<K, V> {
|
|
|
|
readonly [Symbol.toStringTag] = 'LinkedMap';
|
|
|
|
private _map: Map<K, Item<K, V>>;
|
|
private _head: Item<K, V> | undefined;
|
|
private _tail: Item<K, V> | undefined;
|
|
private _size: number;
|
|
|
|
private _state: number;
|
|
|
|
constructor() {
|
|
this._map = new Map<K, Item<K, V>>();
|
|
this._head = undefined;
|
|
this._tail = undefined;
|
|
this._size = 0;
|
|
this._state = 0;
|
|
}
|
|
|
|
clear(): void {
|
|
this._map.clear();
|
|
this._head = undefined;
|
|
this._tail = undefined;
|
|
this._size = 0;
|
|
this._state++;
|
|
}
|
|
|
|
isEmpty(): boolean {
|
|
return !this._head && !this._tail;
|
|
}
|
|
|
|
get size(): number {
|
|
return this._size;
|
|
}
|
|
|
|
get first(): V | undefined {
|
|
return this._head?.value;
|
|
}
|
|
|
|
get last(): V | undefined {
|
|
return this._tail?.value;
|
|
}
|
|
|
|
has(key: K): boolean {
|
|
return this._map.has(key);
|
|
}
|
|
|
|
get(key: K, touch: Touch = Touch.None): V | undefined {
|
|
const item = this._map.get(key);
|
|
if (!item) {
|
|
return undefined;
|
|
}
|
|
if (touch !== Touch.None) {
|
|
this.touch(item, touch);
|
|
}
|
|
return item.value;
|
|
}
|
|
|
|
set(key: K, value: V, touch: Touch = Touch.None): this {
|
|
let item = this._map.get(key);
|
|
if (item) {
|
|
item.value = value;
|
|
if (touch !== Touch.None) {
|
|
this.touch(item, touch);
|
|
}
|
|
} else {
|
|
item = { key, value, next: undefined, previous: undefined };
|
|
switch (touch) {
|
|
case Touch.None:
|
|
this.addItemLast(item);
|
|
break;
|
|
case Touch.AsOld:
|
|
this.addItemFirst(item);
|
|
break;
|
|
case Touch.AsNew:
|
|
this.addItemLast(item);
|
|
break;
|
|
default:
|
|
this.addItemLast(item);
|
|
break;
|
|
}
|
|
this._map.set(key, item);
|
|
this._size++;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
delete(key: K): boolean {
|
|
return !!this.remove(key);
|
|
}
|
|
|
|
remove(key: K): V | undefined {
|
|
const item = this._map.get(key);
|
|
if (!item) {
|
|
return undefined;
|
|
}
|
|
this._map.delete(key);
|
|
this.removeItem(item);
|
|
this._size--;
|
|
return item.value;
|
|
}
|
|
|
|
shift(): V | undefined {
|
|
if (!this._head && !this._tail) {
|
|
return undefined;
|
|
}
|
|
if (!this._head || !this._tail) {
|
|
throw new Error('Invalid list');
|
|
}
|
|
const item = this._head;
|
|
this._map.delete(item.key);
|
|
this.removeItem(item);
|
|
this._size--;
|
|
return item.value;
|
|
}
|
|
|
|
forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: unknown): void {
|
|
const state = this._state;
|
|
let current = this._head;
|
|
while (current) {
|
|
if (thisArg) {
|
|
callbackfn.bind(thisArg)(current.value, current.key, this);
|
|
} else {
|
|
callbackfn(current.value, current.key, this);
|
|
}
|
|
if (this._state !== state) {
|
|
throw new Error(`LinkedMap got modified during iteration.`);
|
|
}
|
|
current = current.next;
|
|
}
|
|
}
|
|
|
|
keys(): MapIterator<K> {
|
|
const map = this;
|
|
const state = this._state;
|
|
let current = this._head;
|
|
const iterator: MapIterator<K> = {
|
|
[Symbol.iterator]() {
|
|
return iterator;
|
|
},
|
|
[Symbol.dispose]() { /* no-op */ },
|
|
next(): IteratorResult<K> {
|
|
if (map._state !== state) {
|
|
throw new Error(`LinkedMap got modified during iteration.`);
|
|
}
|
|
if (current) {
|
|
const result = { value: current.key, done: false };
|
|
current = current.next;
|
|
return result;
|
|
} else {
|
|
return { value: undefined, done: true };
|
|
}
|
|
}
|
|
};
|
|
return iterator;
|
|
}
|
|
|
|
values(): MapIterator<V> {
|
|
const map = this;
|
|
const state = this._state;
|
|
let current = this._head;
|
|
const iterator: MapIterator<V> = {
|
|
[Symbol.iterator]() {
|
|
return iterator;
|
|
},
|
|
[Symbol.dispose]() { /* no-op */ },
|
|
next(): IteratorResult<V> {
|
|
if (map._state !== state) {
|
|
throw new Error(`LinkedMap got modified during iteration.`);
|
|
}
|
|
if (current) {
|
|
const result = { value: current.value, done: false };
|
|
current = current.next;
|
|
return result;
|
|
} else {
|
|
return { value: undefined, done: true };
|
|
}
|
|
}
|
|
};
|
|
return iterator;
|
|
}
|
|
|
|
entries(): MapIterator<[K, V]> {
|
|
const map = this;
|
|
const state = this._state;
|
|
let current = this._head;
|
|
const iterator: MapIterator<[K, V]> = {
|
|
[Symbol.iterator]() {
|
|
return iterator;
|
|
},
|
|
[Symbol.dispose]() { /* no-op */ },
|
|
next(): IteratorResult<[K, V]> {
|
|
if (map._state !== state) {
|
|
throw new Error(`LinkedMap got modified during iteration.`);
|
|
}
|
|
if (current) {
|
|
const result: IteratorResult<[K, V]> = { value: [current.key, current.value], done: false };
|
|
current = current.next;
|
|
return result;
|
|
} else {
|
|
return { value: undefined, done: true };
|
|
}
|
|
}
|
|
};
|
|
return iterator;
|
|
}
|
|
|
|
[Symbol.iterator](): MapIterator<[K, V]> {
|
|
return this.entries();
|
|
}
|
|
|
|
protected trimOld(newSize: number) {
|
|
if (newSize >= this.size) {
|
|
return;
|
|
}
|
|
if (newSize === 0) {
|
|
this.clear();
|
|
return;
|
|
}
|
|
let current = this._head;
|
|
let currentSize = this.size;
|
|
while (current && currentSize > newSize) {
|
|
this._map.delete(current.key);
|
|
current = current.next;
|
|
currentSize--;
|
|
}
|
|
this._head = current;
|
|
this._size = currentSize;
|
|
if (current) {
|
|
current.previous = undefined;
|
|
}
|
|
this._state++;
|
|
}
|
|
|
|
protected trimNew(newSize: number) {
|
|
if (newSize >= this.size) {
|
|
return;
|
|
}
|
|
if (newSize === 0) {
|
|
this.clear();
|
|
return;
|
|
}
|
|
let current = this._tail;
|
|
let currentSize = this.size;
|
|
while (current && currentSize > newSize) {
|
|
this._map.delete(current.key);
|
|
current = current.previous;
|
|
currentSize--;
|
|
}
|
|
this._tail = current;
|
|
this._size = currentSize;
|
|
if (current) {
|
|
current.next = undefined;
|
|
}
|
|
this._state++;
|
|
}
|
|
|
|
private addItemFirst(item: Item<K, V>): void {
|
|
// First time Insert
|
|
if (!this._head && !this._tail) {
|
|
this._tail = item;
|
|
} else if (!this._head) {
|
|
throw new Error('Invalid list');
|
|
} else {
|
|
item.next = this._head;
|
|
this._head.previous = item;
|
|
}
|
|
this._head = item;
|
|
this._state++;
|
|
}
|
|
|
|
private addItemLast(item: Item<K, V>): void {
|
|
// First time Insert
|
|
if (!this._head && !this._tail) {
|
|
this._head = item;
|
|
} else if (!this._tail) {
|
|
throw new Error('Invalid list');
|
|
} else {
|
|
item.previous = this._tail;
|
|
this._tail.next = item;
|
|
}
|
|
this._tail = item;
|
|
this._state++;
|
|
}
|
|
|
|
private removeItem(item: Item<K, V>): void {
|
|
if (item === this._head && item === this._tail) {
|
|
this._head = undefined;
|
|
this._tail = undefined;
|
|
}
|
|
else if (item === this._head) {
|
|
// This can only happen if size === 1 which is handled
|
|
// by the case above.
|
|
if (!item.next) {
|
|
throw new Error('Invalid list');
|
|
}
|
|
item.next.previous = undefined;
|
|
this._head = item.next;
|
|
}
|
|
else if (item === this._tail) {
|
|
// This can only happen if size === 1 which is handled
|
|
// by the case above.
|
|
if (!item.previous) {
|
|
throw new Error('Invalid list');
|
|
}
|
|
item.previous.next = undefined;
|
|
this._tail = item.previous;
|
|
}
|
|
else {
|
|
const next = item.next;
|
|
const previous = item.previous;
|
|
if (!next || !previous) {
|
|
throw new Error('Invalid list');
|
|
}
|
|
next.previous = previous;
|
|
previous.next = next;
|
|
}
|
|
item.next = undefined;
|
|
item.previous = undefined;
|
|
this._state++;
|
|
}
|
|
|
|
private touch(item: Item<K, V>, touch: Touch): void {
|
|
if (!this._head || !this._tail) {
|
|
throw new Error('Invalid list');
|
|
}
|
|
if ((touch !== Touch.AsOld && touch !== Touch.AsNew)) {
|
|
return;
|
|
}
|
|
|
|
if (touch === Touch.AsOld) {
|
|
if (item === this._head) {
|
|
return;
|
|
}
|
|
|
|
const next = item.next;
|
|
const previous = item.previous;
|
|
|
|
// Unlink the item
|
|
if (item === this._tail) {
|
|
// previous must be defined since item was not head but is tail
|
|
// So there are more than on item in the map
|
|
previous!.next = undefined;
|
|
this._tail = previous;
|
|
}
|
|
else {
|
|
// Both next and previous are not undefined since item was neither head nor tail.
|
|
next!.previous = previous;
|
|
previous!.next = next;
|
|
}
|
|
|
|
// Insert the node at head
|
|
item.previous = undefined;
|
|
item.next = this._head;
|
|
this._head.previous = item;
|
|
this._head = item;
|
|
this._state++;
|
|
} else if (touch === Touch.AsNew) {
|
|
if (item === this._tail) {
|
|
return;
|
|
}
|
|
|
|
const next = item.next;
|
|
const previous = item.previous;
|
|
|
|
// Unlink the item.
|
|
if (item === this._head) {
|
|
// next must be defined since item was not tail but is head
|
|
// So there are more than on item in the map
|
|
next!.previous = undefined;
|
|
this._head = next;
|
|
} else {
|
|
// Both next and previous are not undefined since item was neither head nor tail.
|
|
next!.previous = previous;
|
|
previous!.next = next;
|
|
}
|
|
item.next = undefined;
|
|
item.previous = this._tail;
|
|
this._tail.next = item;
|
|
this._tail = item;
|
|
this._state++;
|
|
}
|
|
}
|
|
|
|
toJSON(): [K, V][] {
|
|
const data: [K, V][] = [];
|
|
|
|
this.forEach((value, key) => {
|
|
data.push([key, value]);
|
|
});
|
|
|
|
return data;
|
|
}
|
|
|
|
fromJSON(data: [K, V][]): void {
|
|
this.clear();
|
|
|
|
for (const [key, value] of data) {
|
|
this.set(key, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
abstract class Cache<K, V> extends LinkedMap<K, V> {
|
|
|
|
protected _limit: number;
|
|
protected _ratio: number;
|
|
|
|
constructor(limit: number, ratio: number = 1) {
|
|
super();
|
|
this._limit = limit;
|
|
this._ratio = Math.min(Math.max(0, ratio), 1);
|
|
}
|
|
|
|
get limit(): number {
|
|
return this._limit;
|
|
}
|
|
|
|
set limit(limit: number) {
|
|
this._limit = limit;
|
|
this.checkTrim();
|
|
}
|
|
|
|
get ratio(): number {
|
|
return this._ratio;
|
|
}
|
|
|
|
set ratio(ratio: number) {
|
|
this._ratio = Math.min(Math.max(0, ratio), 1);
|
|
this.checkTrim();
|
|
}
|
|
|
|
override get(key: K, touch: Touch = Touch.AsNew): V | undefined {
|
|
return super.get(key, touch);
|
|
}
|
|
|
|
peek(key: K): V | undefined {
|
|
return super.get(key, Touch.None);
|
|
}
|
|
|
|
override set(key: K, value: V): this {
|
|
super.set(key, value, Touch.AsNew);
|
|
return this;
|
|
}
|
|
|
|
protected checkTrim() {
|
|
if (this.size > this._limit) {
|
|
this.trim(Math.round(this._limit * this._ratio));
|
|
}
|
|
}
|
|
|
|
protected abstract trim(newSize: number): void;
|
|
}
|
|
|
|
export class LRUCache<K, V> extends Cache<K, V> {
|
|
|
|
constructor(limit: number, ratio: number = 1) {
|
|
super(limit, ratio);
|
|
}
|
|
|
|
protected override trim(newSize: number) {
|
|
this.trimOld(newSize);
|
|
}
|
|
|
|
override set(key: K, value: V): this {
|
|
super.set(key, value);
|
|
this.checkTrim();
|
|
return this;
|
|
}
|
|
}
|