From cdf191336aa616a206b977ba3beeb1233cf41c32 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 28 Jun 2020 12:38:34 -0400 Subject: [PATCH] Clone function (#624) * Add clone function * Replace utilIsolate with clone * Replace JsonSchema.isolate with clone function * Include core.js for tests which use json-schema.js * Update visisted set --- .eslintrc.json | 1 + ext/bg/js/backend.js | 5 ++- ext/bg/js/json-schema.js | 20 +++-------- ext/bg/js/util.js | 20 ++--------- ext/mixed/js/core.js | 67 +++++++++++++++++++++++++++++++++++++ test/dictionary-validate.js | 5 ++- test/schema-validate.js | 5 ++- test/test-schema.js | 5 ++- 8 files changed, 88 insertions(+), 40 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index b8b7bee8..b7ed0164 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -99,6 +99,7 @@ "getSetDifference": "readonly", "escapeRegExp": "readonly", "deferPromise": "readonly", + "clone": "readonly", "EventDispatcher": "readonly", "EventListenerCollection": "readonly", "EXTENSION_IS_BROWSER_EDGE": "readonly" diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 344706d1..59a3de45 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -38,7 +38,6 @@ * profileConditionsDescriptorPromise * requestJson * requestText - * utilIsolate */ class Backend { @@ -875,7 +874,7 @@ class Backend { for (const target of targets) { try { const result = this._modifySetting(target); - results.push({result: utilIsolate(result)}); + results.push({result: clone(result)}); } catch (e) { results.push({error: errorToJson(e)}); } @@ -889,7 +888,7 @@ class Backend { for (const target of targets) { try { const result = this._getSetting(target); - results.push({result: utilIsolate(result)}); + results.push({result: clone(result)}); } catch (e) { results.push({error: errorToJson(e)}); } diff --git a/ext/bg/js/json-schema.js b/ext/bg/js/json-schema.js index f62402f9..2e009a7a 100644 --- a/ext/bg/js/json-schema.js +++ b/ext/bg/js/json-schema.js @@ -90,7 +90,7 @@ class JsonSchemaProxyHandler { throw new Error(`Property ${property} not supported`); } - value = JsonSchema.isolate(value); + value = JsonSchema.clone(value); JsonSchemaProxyHandler.validate(value, propertySchema, new JsonSchemaTraversalInfo(value, propertySchema)); @@ -515,7 +515,7 @@ class JsonSchemaProxyHandler { const schemaDefault = schema.default; if (typeof schemaDefault !== 'undefined') { - value = JsonSchema.isolate(schemaDefault); + value = JsonSchema.clone(schemaDefault); type = JsonSchemaProxyHandler.getValueType(value); assignDefault = !JsonSchemaProxyHandler.isValueTypeAny(value, type, schemaType); } @@ -628,19 +628,7 @@ class JsonSchema { return JsonSchemaProxyHandler.getValidValueOrDefault(schema, value); } - static isolate(value) { - if (value === null) { return null; } - - switch (typeof value) { - case 'boolean': - case 'number': - case 'string': - case 'bigint': - case 'symbol': - return value; - } - - const stringValue = JSON.stringify(value); - return typeof stringValue === 'string' ? JSON.parse(stringValue) : null; + static clone(value) { + return clone(value); } } diff --git a/ext/bg/js/util.js b/ext/bg/js/util.js index edc19c6e..fa31b0d8 100644 --- a/ext/bg/js/util.js +++ b/ext/bg/js/util.js @@ -15,26 +15,10 @@ * along with this program. If not, see . */ -function utilIsolate(value) { - if (value === null) { return null; } - - switch (typeof value) { - case 'boolean': - case 'number': - case 'string': - case 'bigint': - case 'symbol': - return value; - } - - const stringValue = JSON.stringify(value); - return typeof stringValue === 'string' ? JSON.parse(stringValue) : null; -} - function utilFunctionIsolate(func) { return function isolatedFunction(...args) { try { - args = args.map((v) => utilIsolate(v)); + args = args.map((v) => clone(v)); return func.call(this, ...args); } catch (e) { try { @@ -50,7 +34,7 @@ function utilFunctionIsolate(func) { function utilBackgroundIsolate(data) { const backgroundPage = chrome.extension.getBackgroundPage(); - return backgroundPage.utilIsolate(data); + return backgroundPage.clone(data); } function utilBackgroundFunctionIsolate(func) { diff --git a/ext/mixed/js/core.js b/ext/mixed/js/core.js index 21b7bf5e..9b3ea3e2 100644 --- a/ext/mixed/js/core.js +++ b/ext/mixed/js/core.js @@ -157,6 +157,73 @@ function getSetDifference(set1, set2) { ); } +const clone = (() => { + // eslint-disable-next-line no-shadow + function clone(value) { + if (value === null) { return null; } + switch (typeof value) { + case 'boolean': + case 'number': + case 'string': + case 'bigint': + case 'symbol': + case 'undefined': + return value; + default: + return cloneInternal(value, new Set()); + } + } + + function cloneInternal(value, visited) { + if (value === null) { return null; } + switch (typeof value) { + case 'boolean': + case 'number': + case 'string': + case 'bigint': + case 'symbol': + case 'undefined': + return value; + case 'function': + return cloneObject(value, visited); + case 'object': + return Array.isArray(value) ? cloneArray(value, visited) : cloneObject(value, visited); + } + } + + function cloneArray(value, visited) { + if (visited.has(value)) { throw new Error('Circular'); } + try { + visited.add(value); + const result = []; + for (const item of value) { + result.push(cloneInternal(item, visited)); + } + return result; + } finally { + visited.delete(value); + } + } + + function cloneObject(value, visited) { + if (visited.has(value)) { throw new Error('Circular'); } + try { + visited.add(value); + const result = {}; + for (const key in value) { + if (Object.prototype.hasOwnProperty.call(value, key)) { + result[key] = cloneInternal(value[key], visited); + } + } + return result; + } finally { + visited.delete(value); + } + } + + return clone; +})(); + /* * Async utilities diff --git a/test/dictionary-validate.js b/test/dictionary-validate.js index f1730852..446de38d 100644 --- a/test/dictionary-validate.js +++ b/test/dictionary-validate.js @@ -21,7 +21,10 @@ const {JSZip} = require('./yomichan-test'); const {VM} = require('./yomichan-vm'); const vm = new VM(); -vm.execute('bg/js/json-schema.js'); +vm.execute([ + 'mixed/js/core.js', + 'bg/js/json-schema.js' +]); const JsonSchema = vm.get('JsonSchema'); diff --git a/test/schema-validate.js b/test/schema-validate.js index 761f0a1c..4c01fa70 100644 --- a/test/schema-validate.js +++ b/test/schema-validate.js @@ -19,7 +19,10 @@ const fs = require('fs'); const {VM} = require('./yomichan-vm'); const vm = new VM(); -vm.execute('bg/js/json-schema.js'); +vm.execute([ + 'mixed/js/core.js', + 'bg/js/json-schema.js' +]); const JsonSchema = vm.get('JsonSchema'); diff --git a/test/test-schema.js b/test/test-schema.js index 7620ab16..f0a99c3b 100644 --- a/test/test-schema.js +++ b/test/test-schema.js @@ -19,7 +19,10 @@ const assert = require('assert'); const {VM} = require('./yomichan-vm'); const vm = new VM(); -vm.execute('bg/js/json-schema.js'); +vm.execute([ + 'mixed/js/core.js', + 'bg/js/json-schema.js' +]); const JsonSchema = vm.get('JsonSchema');