From 3f54f240bd1cd86026e06f306461cbed7641271c Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Tue, 14 Jul 2020 18:38:54 -0700 Subject: [PATCH] perf: pass primitives directly through the context bridge, avoids copying (#24531) --- .eslintrc.json | 3 ++- .../api/electron_api_context_bridge.cc | 11 +++++++++++ spec-main/api-context-bridge-spec.ts | 18 ++++++++++++++++++ tsconfig.json | 2 +- 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 73a57b97eeba..0db6a039d9cf 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -31,7 +31,8 @@ "BUILDFLAG": "readonly", "ENABLE_DESKTOP_CAPTURER": "readonly", "ENABLE_REMOTE_MODULE": "readonly", - "ENABLE_VIEWS_API": "readonly" + "ENABLE_VIEWS_API": "readonly", + "BigInt": "readonly" }, "overrides": [ { diff --git a/shell/renderer/api/electron_api_context_bridge.cc b/shell/renderer/api/electron_api_context_bridge.cc index 59816c745ac7..e2436171d69b 100644 --- a/shell/renderer/api/electron_api_context_bridge.cc +++ b/shell/renderer/api/electron_api_context_bridge.cc @@ -146,6 +146,17 @@ v8::MaybeLocal PassValueToOtherContext( "deeper than 1000 are not supported."))); return v8::MaybeLocal(); } + + // Certain primitives always use the current contexts prototype and we can + // pass these through directly which is significantly more performant than + // copying them. This list of primitives is based on the classification of + // "primitive value" as defined in the ECMA262 spec + // https://tc39.es/ecma262/#sec-primitive-value + if (value->IsString() || value->IsNumber() || value->IsNullOrUndefined() || + value->IsBoolean() || value->IsSymbol() || value->IsBigInt()) { + return v8::MaybeLocal(value); + } + // Check Cache auto cached_value = object_cache->GetCachedProxiedObject(value); if (!cached_value.IsEmpty()) { diff --git a/spec-main/api-context-bridge-spec.ts b/spec-main/api-context-bridge-spec.ts index d5fdd0816ce4..ad91c4e5597a 100644 --- a/spec-main/api-context-bridge-spec.ts +++ b/spec-main/api-context-bridge-spec.ts @@ -313,6 +313,20 @@ describe('contextBridge', () => { expect(result).to.deep.equal(['null', 'undefined']); }); + it('should proxy symbols such that symbol equality works', async () => { + await makeBindingWindow(() => { + const mySymbol = Symbol('unique'); + contextBridge.exposeInMainWorld('example', { + getSymbol: () => mySymbol, + isSymbol: (s: Symbol) => s === mySymbol + }); + }); + const result = await callWithBindings((root: any) => { + return root.example.isSymbol(root.example.getSymbol()); + }); + expect(result).to.equal(true, 'symbols should be equal across contexts'); + }); + it('should proxy typed arrays and regexps through the serializer', async () => { await makeBindingWindow(() => { contextBridge.exposeInMainWorld('example', { @@ -479,6 +493,8 @@ describe('contextBridge', () => { string: 'string', boolean: true, arr: [123, 'string', true, ['foo']], + symbol: Symbol('foo'), + bigInt: 10n, getObject: () => ({ thing: 123 }), getNumber: () => 123, getString: () => 'string', @@ -511,6 +527,8 @@ describe('contextBridge', () => { [example.arr[2], Boolean], [example.arr[3], Array], [example.arr[3][0], String], + [example.symbol, Symbol], + [example.bigInt, BigInt], [example.getNumber, Function], [example.getNumber(), Number], [example.getObject(), Object], diff --git a/tsconfig.json b/tsconfig.json index 0abc258da456..b886d28038c4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "module": "commonjs", - "target": "es2017", + "target": "es2020", "lib": [ "es2019", "dom",