refactor: replace V8 hidden values with WeakMap / WeakSet (#26659)

This commit is contained in:
Milan Burda 2020-11-24 22:11:39 +01:00 коммит произвёл GitHub
Родитель 0be6c92aa9
Коммит c8d77cae4a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 29 добавлений и 19 удалений

Просмотреть файл

@ -1,11 +1,11 @@
import { WebContents } from 'electron/main'; import { WebContents } from 'electron/main';
const v8Util = process._linkedBinding('electron_common_v8_util');
const getOwnerKey = (webContents: WebContents, contextId: string) => { const getOwnerKey = (webContents: WebContents, contextId: string) => {
return `${webContents.id}-${contextId}`; return `${webContents.id}-${contextId}`;
}; };
const electronIds = new WeakMap<Object, number>();
class ObjectsRegistry { class ObjectsRegistry {
private nextId: number = 0 private nextId: number = 0
@ -81,14 +81,14 @@ class ObjectsRegistry {
// Private: Saves the object into storage and assigns an ID for it. // Private: Saves the object into storage and assigns an ID for it.
saveToStorage (object: any) { saveToStorage (object: any) {
let id: number = v8Util.getHiddenValue(object, 'electronId'); let id = electronIds.get(object);
if (!id) { if (!id) {
id = ++this.nextId; id = ++this.nextId;
this.storage[id] = { this.storage[id] = {
count: 0, count: 0,
object: object object: object
}; };
v8Util.setHiddenValue(object, 'electronId', id); electronIds.set(object, id);
} }
return id; return id;
} }
@ -101,7 +101,7 @@ class ObjectsRegistry {
} }
pointer.count -= 1; pointer.count -= 1;
if (pointer.count === 0) { if (pointer.count === 0) {
v8Util.deleteHiddenValue(pointer.object, 'electronId'); electronIds.delete(pointer.object);
delete this.storage[id]; delete this.storage[id];
} }
} }

Просмотреть файл

@ -56,6 +56,8 @@ function setCachedRendererFunction (id: RendererFunctionId, wc: electron.WebCont
return value; return value;
} }
const locationInfo = new WeakMap<Object, string>();
// Return the description of object's members: // Return the description of object's members:
const getObjectMembers = function (object: any): ObjectMember[] { const getObjectMembers = function (object: any): ObjectMember[] {
let names = Object.getOwnPropertyNames(object); let names = Object.getOwnPropertyNames(object);
@ -186,7 +188,7 @@ const throwRPCError = function (message: string) {
}; };
const removeRemoteListenersAndLogWarning = (sender: any, callIntoRenderer: (...args: any[]) => void) => { const removeRemoteListenersAndLogWarning = (sender: any, callIntoRenderer: (...args: any[]) => void) => {
const location = v8Util.getHiddenValue(callIntoRenderer, 'location'); const location = locationInfo.get(callIntoRenderer);
let message = 'Attempting to call a function in a renderer window that has been closed or released.' + let message = 'Attempting to call a function in a renderer window that has been closed or released.' +
`\nFunction provided here: ${location}`; `\nFunction provided here: ${location}`;
@ -269,7 +271,7 @@ const unwrapArgs = function (sender: electron.WebContents, frameId: number, cont
removeRemoteListenersAndLogWarning(this, callIntoRenderer); removeRemoteListenersAndLogWarning(this, callIntoRenderer);
} }
}; };
v8Util.setHiddenValue(callIntoRenderer, 'location', meta.location); locationInfo.set(callIntoRenderer, meta.location);
Object.defineProperty(callIntoRenderer, 'length', { value: meta.length }); Object.defineProperty(callIntoRenderer, 'length', { value: meta.length });
setCachedRendererFunction(objectId, sender, frameId, callIntoRenderer); setCachedRendererFunction(objectId, sender, frameId, callIntoRenderer);

Просмотреть файл

@ -23,6 +23,9 @@ const finalizationRegistry = new (window as any).FinalizationRegistry((id: numbe
} }
}); });
const electronIds = new WeakMap<Object, number>();
const isReturnValue = new WeakSet<Object>();
function getCachedRemoteObject (id: number) { function getCachedRemoteObject (id: number) {
const ref = remoteObjectCache.get(id); const ref = remoteObjectCache.get(id);
if (ref !== undefined) { if (ref !== undefined) {
@ -90,10 +93,10 @@ function wrapArgs (args: any[], visited = new Set()): any {
value.then(onFulfilled, onRejected); value.then(onFulfilled, onRejected);
}) })
}; };
} else if (v8Util.getHiddenValue(value, 'electronId')) { } else if (electronIds.has(value)) {
return { return {
type: 'remote-object', type: 'remote-object',
id: v8Util.getHiddenValue(value, 'electronId') id: electronIds.get(value)
}; };
} }
@ -111,7 +114,7 @@ function wrapArgs (args: any[], visited = new Set()): any {
} }
visited.delete(value); visited.delete(value);
return meta; return meta;
} else if (typeof value === 'function' && v8Util.getHiddenValue(value, 'returnValue')) { } else if (typeof value === 'function' && isReturnValue.has(value)) {
return { return {
type: 'function-with-return-value', type: 'function-with-return-value',
value: valueToMeta(value()) value: valueToMeta(value())
@ -120,7 +123,7 @@ function wrapArgs (args: any[], visited = new Set()): any {
return { return {
type: 'function', type: 'function',
id: callbacksRegistry.add(value), id: callbacksRegistry.add(value),
location: v8Util.getHiddenValue(value, 'location'), location: callbacksRegistry.getLocation(value),
length: value.length length: value.length
}; };
} else { } else {
@ -287,7 +290,7 @@ function metaToValue (meta: MetaType): any {
} }
// Track delegate obj's lifetime & tell browser to clean up when object is GCed. // Track delegate obj's lifetime & tell browser to clean up when object is GCed.
v8Util.setHiddenValue(ret, 'electronId', meta.id); electronIds.set(ret, meta.id);
setCachedRemoteObject(meta.id, ret); setCachedRemoteObject(meta.id, ret);
return ret; return ret;
} }
@ -373,7 +376,7 @@ Object.defineProperty(exports, 'process', {
// Create a function that will return the specified value when called in browser. // Create a function that will return the specified value when called in browser.
export function createFunctionWithReturnValue<T> (returnValue: T): () => T { export function createFunctionWithReturnValue<T> (returnValue: T): () => T {
const func = () => returnValue; const func = () => returnValue;
v8Util.setHiddenValue(func, 'returnValue', true); isReturnValue.add(func);
return func; return func;
} }

Просмотреть файл

@ -1,4 +1,5 @@
const v8Util = process._linkedBinding('electron_common_v8_util'); const callbackIds = new WeakMap<Function, number>();
const locationInfo = new WeakMap<Function, string>();
export class CallbacksRegistry { export class CallbacksRegistry {
private nextId: number = 0 private nextId: number = 0
@ -6,7 +7,7 @@ export class CallbacksRegistry {
add (callback: Function) { add (callback: Function) {
// The callback is already added. // The callback is already added.
let id = v8Util.getHiddenValue<number>(callback, 'callbackId'); let id = callbackIds.get(callback);
if (id != null) return id; if (id != null) return id;
id = this.nextId += 1; id = this.nextId += 1;
@ -17,7 +18,7 @@ export class CallbacksRegistry {
const stackString = (new Error()).stack; const stackString = (new Error()).stack;
if (!stackString) return; if (!stackString) return;
let filenameAndLine; let filenameAndLine: string;
let match; let match;
while ((match = regexp.exec(stackString)) !== null) { while ((match = regexp.exec(stackString)) !== null) {
@ -32,8 +33,8 @@ export class CallbacksRegistry {
} }
this.callbacks.set(id, callback); this.callbacks.set(id, callback);
v8Util.setHiddenValue(callback, 'callbackId', id); callbackIds.set(callback, id);
v8Util.setHiddenValue(callback, 'location', filenameAndLine); locationInfo.set(callback, filenameAndLine!);
return id; return id;
} }
@ -41,6 +42,10 @@ export class CallbacksRegistry {
return this.callbacks.get(id) || function () {}; return this.callbacks.get(id) || function () {};
} }
getLocation (callback: Function) {
return locationInfo.get(callback);
}
apply (id: number, ...args: any[]) { apply (id: number, ...args: any[]) {
return this.get(id).apply(global, ...args); return this.get(id).apply(global, ...args);
} }
@ -48,7 +53,7 @@ export class CallbacksRegistry {
remove (id: number) { remove (id: number) {
const callback = this.callbacks.get(id); const callback = this.callbacks.get(id);
if (callback) { if (callback) {
v8Util.deleteHiddenValue(callback, 'callbackId'); callbackIds.delete(callback);
this.callbacks.delete(id); this.callbacks.delete(id);
} }
} }