1. Szenario In der vorherigen JavaScript-Sandbox-Erkundung wurde die Sandbox-Schnittstelle deklariert und einige einfache Codes zum Ausführen von JS-Skripten von Drittanbietern bereitgestellt, die vollständige Die Kapselungsbibliothek von Exportschnittstelle LowLevelJavascriptVm<VmHandle> { global: VmHandle; undefiniert: VmHandle; Typ von (Handle: VmHandle): Zeichenfolge; getNumber(handle: VmHandle): Nummer; getString(handle: VmHandle): Zeichenfolge; neueNummer(Wert: Nummer): VmHandle; newString(Wert: Zeichenfolge): VmHandle; neuesObjekt(Prototyp?: VmHandle): VmHandle; neueFunktion( Name: Zeichenfolge, Wert: VmFunctionImplementation<VmHandle> ): VmHandle; getProp(handle: VmHandle, Schlüssel: Zeichenfolge | VmHandle): VmHandle; setProp(Handle: VmHandle, Schlüssel: Zeichenfolge | VmHandle, Wert: VmHandle): void; definierenProp( Griff: VmHandle, Schlüssel: Zeichenfolge | VmHandle, Deskriptor: VmPropertyDescriptor<VmHandle> ): Leere; Aufruffunktion( Funktion: VmHandle, thisVal: VmHandle, ...Argumente: VmHandle[] ): VmCallResult<VmHandle>; evalCode(Code: Zeichenfolge): VmCallResult<VmHandle>; } Das Folgende ist ein offizielles Codebeispiel importiere { getQuickJS } von "quickjs-emscripten"; asynchrone Funktion main() { const QuickJS = warte auf getQuickJS(); const vm = QuickJS.createVm(); const Welt = vm.newString("Welt"); vm.setProp(vm.global, "NAME", Welt); welt.entsorgen(); const result = vm.evalCode(`"Hallo " + NAME + "!"`); if (Ergebnis.Fehler) { console.log("Ausführung fehlgeschlagen:", vm.dump(result.error)); Ergebnis.Fehler.Entsorgen(); } anders { console.log("Erfolg:", vm.dump(Ergebnis.Wert)); Ergebnis.Wert.Entsorgen(); } vm.dispose(); } hauptsächlich(); Wie Sie sehen, müssen Sie nach dem Erstellen der Variablen in der VM daran denken, 2. Vereinfachen Sie die zugrunde liegende APIEs gibt zwei Hauptzwecke:
2.1 Dispose automatisch aufrufen Die Grundidee besteht darin, alle Werte, die
importiere { QuickJSHandle, QuickJSVm } von "quickjs-emscripten"; const QuickJSVmScopeSymbol = Symbol("QuickJSVmScope"); /** * Fügen Sie QuickJSVm einen lokalen Bereich hinzu. Alle Methodenaufrufe im lokalen Bereich müssen den Speicher nicht mehr manuell freigeben. * @param vm * @param-Handle */ Exportfunktion mit Scope<F extends (vm: QuickJSVm) => any>( vm: QuickJSVm, Griff: F ): { Wert: ReturnType<F>; entsorgen(): ungültig; } { lass entsorgt: (() => void)[] = []; Funktion Wrap(Handle: QuickJSHandle) { entsorgt.push(() => handle.alive && handle.dispose()); Rückholgriff; } //Mehrschichtigen Proxy vermeiden const isProxy = !!Reflect.get(vm, QuickJSVmScopeSymbol); Funktion entsorgen() { wenn (istProxy) { Reflect.get(vm, QuickJSVmScopeSymbol)(); zurückkehren; } entsorgt.forEach((entsorgen) => entsorgen()); //Speicher der Closure-Variable manuell freigeben disposes.length = 0; } konstanter Wert = Griff( istProxy ? vm : neuer Proxy(vm, { erhalten( Ziel: QuickJSVm, p: Schlüssel von QuickJSVm | Typ von QuickJSVmScopeSymbol ): beliebig { wenn (p === QuickJSVmScopeSymbol) { zurückgeben, entsorgen; } //Sperren Sie den this-Wert aller Methoden auf das QuickJSVm-Objekt statt auf das Proxy-Objekt const res = Reflect.get(target, p, target); Wenn ( p.startsWith("neu") || ["getProp", "unwrapResult"].includes(p) ) { Rückgabewert (...args: beliebig[]): QuickJSHandle => { Rückgabewert für Wrap(Reflect.apply(res, Ziel, Argumente)); }; } wenn (["evalCode", "callFunction"].includes(p)) { return (...args: beliebig[]) => { const res = (Ziel[p] als beliebiges)(...args); entsorgt.push(() => { const handle = res.Fehler ?? res.Wert; handle.alive und handle.entsorgen(); }); Rückgabewert; }; } wenn (Typ von res === "Funktion") { return (...args: beliebig[]) => { gibt Reflect.apply(res, Ziel, Argumente) zurück; }; } Rückgabewert; }, }) ); return { Wert, entsorgen }; } verwenden mitScope(vm, (vm) => { const _hello = vm.newFunction("hallo", () => {}); const _object = vm.newObject(); vm.setProp(_object, "hallo", _hallo); vm.setProp(_object, "name", vm.newString("liuli")); erwarten(vm.dump(vm.getProp(_object, "hallo"))).nicht.toBeNull(); vm.setProp(vm.global, "VM_GLOBAL", _object); }).entsorgen(); Es unterstützt sogar verschachtelte Aufrufe und muss mit Umfang(vm, (vm) => mitScope(vm, (vm) => { Konsole.log(vm.dump(vm.unwrapResult(vm.evalCode("1+1")))); }) ).entsorgen(); 2.2 Bieten Sie eine bessere Möglichkeit zum Erstellen von VM-Werten Die Hauptidee besteht darin, den Typ der erstellten importiere { QuickJSHandle, QuickJSVm } von "quickjs-emscripten"; importiere { withScope } von "./withScope"; Typ MarshalValue = {Wert: QuickJSHandle; entsorgen: () => void }; /** * Vereinfachen Sie die Erstellung komplexer Objekte mit QuickJSVm * @param vm */ Exportfunktion Marshal (vm: QuickJSVm) { Funktion Marshal (Wert: (...Argumente: beliebig[]) => beliebig, Name: Zeichenfolge): MarshalValue; Funktion Marshal (Wert: beliebig): MarshalValue; Funktion Marshal (Wert: beliebig, Name?: Zeichenfolge): MarshalValue { returniere mit Umfang(vm, (vm) => { Funktion _f(Wert: beliebig, Name?: Zeichenfolge): QuickJSHandle { wenn (Typ des Wertes === "Zeichenfolge") { gibt vm.newString(Wert) zurück; } wenn (Typ des Wertes === "Zahl") { return vm.neueNummer(Wert); } wenn (Typ des Wertes === "Boolesch") { Geben Sie vm.unwrapResult(vm.evalCode(`${value}`)); zurück. } if (Wert === undefiniert) { gibt vm.undefined zurück; } wenn (Wert === null) { gibt vm.null zurück; } wenn (Typ des Wertes === "bigint") { return vm.unwrapResult(vm.evalCode(`BigInt(${value})`)); } wenn (Typ des Wertes === "Funktion") { returniere vm.newFunction(Name!, Wert); } wenn (Typ des Wertes === "Objekt") { wenn (Array.isArray(Wert)) { const _array = vm.newArray(); Wert.fürJeden((v) => { wenn (Typ von v === "Funktion") { throw new Error("Arrays dürfen keine Funktionen enthalten, da keine Namen angegeben werden können"); } vm.callFunction(vm.getProp(_array, "push"), _array, _f(v)); }); gibt _array zurück; } if (Wert Instanz von Map) { const _map = vm.unwrapResult(vm.evalCode("neue Map()")); Wert.fürJeden((v, k) => { vm.unwrapResult( vm.callFunction(vm.getProp(_map, "set"), _map, _f(k), _f(v, k)) ); }); gib _map zurück; } const _object = vm.newObject(); Objekt.Einträge(Wert).fürJeden(([k, v]) => { vm.setProp(_object, k, _f(v, k)); }); gibt _Objekt zurück; } throw new Error("nicht unterstützter Typ"); } return _f(Wert, Name); }); } Rückkehrmarschall; } verwenden const mockHello = jest.fn(); const jetzt = neues Datum(); const { Wert, entsorgen } = marshal(vm)({ Name: "liuli", Alter: 1, Geschlecht: falsch, Hobby: [1, 2, 3], Konto: Benutzername: "li", }, hallo: mockHallo, Karte: neue Map().set(1, "a"), Datum: jetzt, }); vm.setProp(vm.global, "vm_global", Wert); entsorgen(); Funktion evalCode(Code: Zeichenfolge) { returniere vm.unwrapResult(vm.evalCode(code)).consume(vm.dump.bind(vm)); } erwarten(evalCode("vm_global.name")).toBe("liuli"); erwarten(evalCode("vm_global.age")).toBe(1); erwarten(evalCode("vm_global.sex")).soll(false); erwarten(evalCode("vm_global.hobby")).toEqual([1, 2, 3]); erwarten(neues Datum(evalCode("vm_global.date"))).toEqual(jetzt); erwarten(evalCode("vm_global.account.username")).toEqual("li"); evalCode("vm_global.hello()"); erwarte(mockHello.mock.calls.length).toBe(1); erwarten(evalCode("vm_global.map.size")).toBe(1); erwarten(evalCode("vm_global.map.get(1)")).toBe("a"); Die derzeit unterstützten Typen werden mit dem strukturierten Klonalgorithmus von JavaScript verglichen, der an vielen Stellen verwendet wird (
3. Implementieren Sie gängige APIs wie console/setTimeout/setInterval Da 3.1 Implementierung der Konsole
importiere { QuickJSVm } von "quickjs-emscripten"; importiere { marshal } von "../util/marshal"; Schnittstelle IVmConsole exportieren { log(...args: beliebig[]): ungültig; info(...args: beliebig[]): ungültig; warnen(...args: any[]): void; Fehler(...Argumente: beliebig[]): ungültig; } /** * Definieren Sie die Konsolen-API in der VM * @param vm * @param logger */ Exportfunktion defineConsole(vm: QuickJSVm, logger: IVmConsole) { const fields = ["log", "info", "warn", "error"] als const; const dump = vm.dump.bind(vm); const { Wert, Entsorgung } = Marshal(vm)( Felder.reduzieren((res, k) => { res[k] = (...args: beliebig[]) => { logger[k](...args.map(dump)); }; Rückgabewert; }, {} als Datensatz<Zeichenfolge, Funktion>) ); vm.setProp(vm.global, "Konsole", Wert); entsorgen(); } Exportklasse BasicVmConsole implementiert IVmConsole { Fehler(...Argumente: beliebig[]): void { Konsole.Fehler(...Argumente); } info(...args: beliebig[]): void { Konsole.info(...args); } log(...args: beliebig[]): void { Konsole.log(...args); } warnen(...args: any[]): void { Konsole.warnen(...args); } } verwenden Konsole definieren(vm, neue BasicVmConsole()); 3.2 Implementierung von setTimeoutGrundgedanke: Implementierung von setTimeout und clearTimeout basierend auf quickjs Globale
clearTimeout: Ruft das echte
importiere { QuickJSVm } von "quickjs-emscripten"; importiere { withScope } von "../util/withScope"; importiere { VmSetInterval } aus "./defineSetInterval"; importiere { deleteKey } aus "../util/deleteKey"; importiere { CallbackIdGenerator } von "@webos/ipc-main"; /** * Fügen Sie die Methode setTimeout ein. * Sie müssen nach der Injektion {@link defineEventLoop} aufrufen, um die Ereignisschleife der VM auszuführen. * @param vm */ Exportfunktion defineSetTimeout(vm: QuickJSVm): VmSetInterval { const callbackMap = neue Map<Zeichenfolge, beliebig>(); Funktion löschen (ID: Zeichenfolge) { mitScope(vm, (vm) => { Schlüssel löschen( vm, vm.unwrapResult(vm.evalCode(`VM_GLOBAL.setTimeoutCallback`)), Ausweis ); }).entsorgen(); Löschen Sie das Intervall. RückrufMap.delete(id); } mitScope(vm, (vm) => { const vmGlobal = vm.getProp(vm.global, "VM_GLOBAL"); wenn (vm.typeof(vmGlobal) === "undefiniert") { throw new Error("VM_GLOBAL existiert nicht, Sie müssen zuerst defineVmGlobal ausführen"); } vm.setProp(vmGlobal, "setTimeoutCallback", vm.newObject()); vm.setProp( vm.global, "Zeitlimit festlegen", vm.newFunction("setTimeout", (Rückruf, ms) => { const id = CallbackIdGenerator.generate(); //Dies ist bereits asynchron, daher müssen Sie es mit withScope(vm, (vm) => { umschließen. const Rückrufe = vm.unwrapResult( vm.evalCode("VM_GLOBAL.setTimeoutCallback") ); vm.setProp(Rückrufe, ID, Rückruf); //Dies ist immer noch asynchron, daher müssen Sie es mit const timeout = setTimeout( umschließen. () => mitScope(vm, (vm) => { const Rückrufe = vm.unwrapResult( vm.evalCode(`VM_GLOBAL.setTimeoutCallback`) ); const callback = vm.getProp(callbacks, id); vm.callFunction(Rückruf, vm.null); RückrufMap.delete(id); }).entsorgen(), vm.dump(ms) ); RückrufMap.set(id, Zeitüberschreitung); }).entsorgen(); gibt vm.newString(id) zurück; }) ); vm.setProp( vm.global, "Zeitüberschreitung löschen", vm.newFunction("clearTimeout", (id) => löschen(vm.dump(id))) ); }).entsorgen(); zurückkehren { Rückrufkarte, klar() { [...callbackMap.keys()].fürEach(löschen); }, }; } verwenden const vmSetTimeout = definierenSetTimeout(vm); mitScope(vm, (vm) => { vm.evalCode(` const begin = Date.now() setzeIntervall(() => { console.log(Datum.jetzt() - beginnen) }, 100) `); }).entsorgen(); vmSetTimeout.clear(); 3.3 Implementierung von setInterval Im Grunde ist es ähnlich wie die Implementierung des importiere { QuickJSVm } von "quickjs-emscripten"; importiere { withScope } von "../util/withScope"; importiere { deleteKey } aus "../util/deleteKey"; importiere { CallbackIdGenerator } von "@webos/ipc-main"; Exportschnittstelle VmSetInterval { RückrufMap: Map<Zeichenfolge, beliebig>; löschen(): ungültig; } /** * Fügen Sie die Methode setInterval ein. * Sie müssen nach der Injektion {@link defineEventLoop} aufrufen, um die Ereignisschleife der VM auszuführen. * @param vm */ Exportfunktion defineSetInterval(vm: QuickJSVm): VmSetInterval { const callbackMap = neue Map<Zeichenfolge, beliebig>(); Funktion löschen (ID: Zeichenfolge) { mitScope(vm, (vm) => { Schlüssel löschen( vm, vm.unwrapResult(vm.evalCode(`VM_GLOBAL.setTimeoutCallback`)), Ausweis ); }).entsorgen(); Löschen Sie das Intervall. RückrufMap.delete(id); } mitScope(vm, (vm) => { const vmGlobal = vm.getProp(vm.global, "VM_GLOBAL"); wenn (vm.typeof(vmGlobal) === "undefiniert") { throw new Error("VM_GLOBAL existiert nicht, Sie müssen zuerst defineVmGlobal ausführen"); } vm.setProp(vmGlobal, "setIntervalCallback", vm.newObject()); vm.setProp( vm.global, "Intervall festlegen", vm.newFunction("setInterval", (Rückruf, ms) => { const id = CallbackIdGenerator.generate(); //Dies ist bereits asynchron, daher müssen Sie es mit withScope(vm, (vm) => { umschließen. const Rückrufe = vm.unwrapResult( vm.evalCode("VM_GLOBAL.setIntervalCallback") ); vm.setProp(Rückrufe, ID, Rückruf); const Intervall = setzeInterval(() => { mitScope(vm, (vm) => { vm.callFunktion( vm.unwrapResult( vm.evalCode(`VM_GLOBAL.setIntervalCallback['${id}']`) ), vm.null ); }).entsorgen(); }, vm.dump(ms)); RückrufMap.set(id, Intervall); }).entsorgen(); gibt vm.newString(id) zurück; }) ); vm.setProp( vm.global, "Löschintervall", vm.newFunction("clearInterval", (id) => löschen(vm.dump(id))) ); }).entsorgen(); zurückkehren { Rückrufkarte, klar() { [...callbackMap.keys()].fürEach(löschen); }, }; } 3.4 Implementierung der Ereignisschleife Eine Sache ist jedoch, dass const { log } = defineMockConsole(vm); mitScope(vm, (vm) => { vm.evalCode(`Promise.resolve().then(()=>console.log(1))`); }).entsorgen(); erwarte(log.mock.calls.length).toBe(0); vm.executePendingJobs(); erwarte(log.mock.calls.length).toBe(1); Wir können also eine Funktion verwenden, die automatisch importiere { QuickJSVm } von "quickjs-emscripten"; Schnittstelle VmEventLoop exportieren { löschen(): ungültig; } /** * Definieren Sie den Ereignisschleifenmechanismus in der VM, versuchen Sie, eine Schleife zu erstellen und die wartenden asynchronen Operationen auszuführen * @param vm */ Exportfunktion defineEventLoop(vm: QuickJSVm) { const Intervall = setzeInterval(() => { vm.executePendingJobs(); }, 100); zurückkehren { klar() { clearInterval(Intervall); }, }; } Rufen Sie jetzt einfach const { log } = defineMockConsole(vm); const eventLoop = defineEventLoop(vm); versuchen { mitScope(vm, (vm) => { vm.evalCode(`Promise.resolve().then(()=>console.log(1))`); }).entsorgen(); erwarte(log.mock.calls.length).toBe(0); warte warte(100); erwarte(log.mock.calls.length).toBe(1); Endlich eventLoop.löschen(); } 4. Realisieren Sie die Kommunikation zwischen Sandbox und System Was unserer Sandbox noch fehlt, ist ein Kommunikationsmechanismus, also implementieren wir einen Der Kern besteht darin, Kommunikation zwischen Sandbox und System: importiere { QuickJSHandle, QuickJSVm } von "quickjs-emscripten"; importiere { marshal } von "../util/marshal"; importiere { withScope } von "../util/withScope"; importiere { IEventEmitter } von "@webos/ipc-main"; Exporttyp VmMessageChannel = IEventEmitter & { listenerMap: Map<string, ((msg: any) => void)[]>; }; /** * Nachrichtenkommunikation definieren * @param vm */ Exportfunktion defineMessageChannel(vm: QuickJSVm): VmMessageChannel { const res = mitScope(vm, (vm) => { const vmGlobal = vm.getProp(vm.global, "VM_GLOBAL"); wenn (vm.typeof(vmGlobal) === "undefiniert") { throw new Error("VM_GLOBAL existiert nicht, Sie müssen zuerst defineVmGlobal ausführen"); } const listenerMap = neue Map<string, ((msg: string) => void)[]>(); const messagePort = marshal(vm)({ //Region VM-Prozess Callback-Funktionsdefinition listenerMap: new Map(), //emitMain(channel: QuickJSHandle, msg: QuickJSHandle) für VM-Prozess { const key = vm.dump(channel); Konstantwert = vm.dump(msg); wenn (!listenerMap.has(Schlüssel)) { console.log("Der Hauptprozess hört nicht auf die API: ", Schlüssel, Wert); zurückkehren; } listenerMap.get(Schlüssel)!.forEach((fn) => { versuchen { fn(Wert); } fangen (e) { console.error("Beim Ausführen der Rückruffunktion ist ein Fehler aufgetreten: ", e); } }); }, //Ende der Region }); vm.setProp(vmGlobal, "MessagePort", messagePort.Wert); //Funktion emitVM(channel: string, msg: string) für Hauptprozess { mitScope(vm, (vm) => { const _map = vm.unwrapResult( vm.evalCode("VM_GLOBAL.MessagePort.listenerMap") ); const _get = vm.getProp(_map, "get"); const _array = vm.unwrapResult( vm.callFunction(_get, _map, vm.newString(Kanal)) ); wenn (!vm.dump(_array)) { zurückkehren; } für ( sei i = 0, Länge = vm.dump(vm.getProp(_array, "Länge")); i < Länge; ich++ ) { vm.callFunktion( vm.getProp(_array, vm.newNumber(i)), vm.null, Marshal (VM) (Nachricht).Wert ); } }).entsorgen(); } zurückkehren { ausgeben: emitVM, offByChannel(Kanal: Zeichenfolge): void { listenerMap.delete(Kanal); }, ein(Kanal: Zeichenfolge, Handle: (Daten: beliebig) => void): void { wenn (!listenerMap.has(channel)) { listenerMap.set(Kanal, []); } listenerMap.get(Kanal)!.push(Handle); }, ListenerMap, } als VmMessageChannel; }); res.entsorgen(); gibt Res.Wert zurück; }
verwenden definiereVmGlobal(vm); const messageChannel = defineMessageChannel(vm); const mockFn = jest.fn(); messageChannel.on("hallo", mockFn); mitScope(vm, (vm) => { vm.evalCode(` Klasse QuickJSEventEmitter { emittieren(Kanal, Daten) { VM_GLOBAL.MessagePort.emitMain(Kanal, Daten); } auf(Kanal, Handle) { wenn (!VM_GLOBAL.MessagePort.listenerMap.has(channel)) { VM_GLOBAL.MessagePort.listenerMap.set(channel, []); } VM_GLOBAL.MessagePort.listenerMap.get(Kanal).push(Handle); } offByChannel(Kanal) { VM_GLOBAL.MessagePort.listenerMap.delete(Kanal); } } const em = neuer QuickJSEventEmitter() em.emit('hallo', 'liuli') `); }).entsorgen(); erwarten(mockFn.mock.calls[0][0]).toBe("liuli"); messageChannel.listenerMap.clear(); 5. Implementieren Sie IJavaScriptShadowbox Abschließend fügen wir die oben implementierten Funktionen zusammen, um importiere { IJavaScriptShadowbox } aus "./IJavaScriptShadowbox"; importiere { getQuickJS, QuickJS, QuickJSVm } von "quickjs-emscripten"; importieren { Grundlegende VM-Konsole, Konsole definieren, definiereEventLoop, definiereMessageChannel, definiereSetInterval, definierenSetTimeout, definierenVmGlobal, VmEventLoop, VmMessageChannel, VmSetInterval, mit Umfang, } von "@webos/quickjs-emscripten-utils"; Exportklasse QuickJSShadowbox implementiert IJavaScriptShadowbox { privater vmMessageChannel: VmMessageChannel; private vmEventLoop: VmEventLoop; privates vmSetInterval: VmSetInterval; privates vmSetTimeout: VmSetInterval; privater Konstruktor (schreibgeschützte VM: QuickJSVm) { Konsole definieren(vm, neue BasicVmConsole()); definiereVmGlobal(vm); this.vmSetTimeout = definiereSetTimeout(vm); this.vmSetInterval = definiereSetInterval(vm); this.vmEventLoop = definiereEventLoop(vm); this.vmMessageChannel = definiereMessageChannel(vm); } zerstören(): void { this.vmMessageChannel.listenerMap.clear(); this.vmEventLoop.clear(); this.vmSetInterval.clear(); Dies.vmSetTimeout.clear(); dies.vm.dispose(); } eval(Code: Zeichenfolge): void { mitScope(this.vm, (vm) => { vm.unwrapResult(vm.evalCode(code)); }).entsorgen(); } emittieren(Kanal: Zeichenfolge, Daten?: beliebig): void { this.vmMessageChannel.emit(Kanal, Daten); } ein(Kanal: Zeichenfolge, Handle: (Daten: beliebig) => void): void { this.vmMessageChannel.on(Kanal, Handle); } offByChannel(Kanal: Zeichenfolge) { this.vmMessageChannel.offByChannel(Kanal); } privates statisches QuickJS: QuickJS; statisch asynchron erstellen() { wenn (!QuickJSShadowbox.quickJS) { QuickJSShadowbox.quickJS = warte auf getQuickJS(); } gibt eine neue QuickJSShadowbox zurück (QuickJSShadowbox.quickJS.createVm()); } statisch zerstören() { QuickJSShadowbox.quickJS = null wie beliebig; } } Verwendung auf Systemebene const shadowbox = warte auf QuickJSShadowbox.create(); const mockConsole = defineMockConsole(shadowbox.vm); Schattenbox.eval(Code); shadowbox.emit(AppChannelEnum.Open); erwarten(mockConsole.log.mock.calls[0][0]).toBe("öffnen"); shadowbox.emit(WindowChannelEnum.AllClose); expect(mockConsole.log.mock.calls[1][0]).toBe("alle schließen"); Schattenbox.zerstören(); Verwendung im Sandbox-Bereich const eventEmitter = neuer QuickJSEventEmitter(); eventEmitter.on(AppChannelEnum.Open, async () => { console.log("öffnen"); }); eventEmitter.on(WindowChannelEnum.AllClose, async () => { console.log("alle schließen"); }); 6. Aktuelle Einschränkungen der QuickJS-SandboxIm Folgenden sind einige der Einschränkungen der aktuellen Implementierung aufgeführt, die in Zukunft auch verbessert werden können Die Konsole unterstützt nur allgemeine Protokoll-/Info-/Warn-/Fehlermethoden. Dies ist das Ende dieses Artikels über die Details der QuickJS-Kapselung von JavaScript-Sandboxen. Weitere relevante QuickJS-Kapselungsinhalte von JavaScript-Sandboxen finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder durchsuchen Sie die verwandten Artikel weiter unten. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird! Das könnte Sie auch interessieren:
|
<<: So zeigen Sie den Datenbankinstallationspfad in MySQL an
>>: Python schreibt die Ausgabe in den CSV-Vorgang
Gespeicherte Datenbankprozeduren DROP-VERFAHREN, ...
1. Filter Beispiel: <!DOCTYPE html> <htm...
Erläuterung der HTML-Tags 1. HTML-Tags Tag: !DOCT...
Hintergrund Verwenden Sie Idea mit Docker, um den...
Inhaltsverzeichnis Erstellen Sie ein Docker-Image...
Inhaltsverzeichnis 1. MySQL Master-Slave-Replikat...
Was Sie aus Büchern lernen, ist immer oberflächli...
Jeden Tag ein jQuery-Plugin - Schritt-Fortschritt...
Inhaltsverzeichnis Machen Sie das Scrollen flüssi...
Das Befehlsformat für die MySQL-Anmeldung ist: my...
Inhaltsverzeichnis Überblick Leistung.jetzt Konso...
Docker erfreut sich immer größerer Beliebtheit. E...
1. Deinstallieren Sie zuerst npm sudo npm deinsta...
In diesem Artikel wird das Shell-Skript von mysql...
Heute werde ich über einen CSS-Spezialeffekt spre...