gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated: idb: custom structured clone,


From: gnunet
Subject: [taler-wallet-core] branch master updated: idb: custom structured clone, don't rely on typeson anymore
Date: Wed, 24 Feb 2021 01:28:08 +0100

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new 3a336799 idb: custom structured clone, don't rely on typeson anymore
3a336799 is described below

commit 3a336799a08564ddc91713a397707b01ae0425c5
Author: Florian Dold <florian@dold.me>
AuthorDate: Wed Feb 24 01:28:02 2021 +0100

    idb: custom structured clone, don't rely on typeson anymore
---
 packages/idb-bridge/package.json                |   4 +-
 packages/idb-bridge/src/index.ts                |   2 +
 packages/idb-bridge/src/util/structuredClone.ts | 227 +++++++++++++++++++++++-
 3 files changed, 228 insertions(+), 5 deletions(-)

diff --git a/packages/idb-bridge/package.json b/packages/idb-bridge/package.json
index 9db293b5..52bc872d 100644
--- a/packages/idb-bridge/package.json
+++ b/packages/idb-bridge/package.json
@@ -28,9 +28,7 @@
     "typescript": "^4.1.3"
   },
   "dependencies": {
-    "tslib": "^2.1.0",
-    "typeson": "^5.18.2",
-    "typeson-registry": "^1.0.0-alpha.38"
+    "tslib": "^2.1.0"
   },
   "ava": {
     "require": [
diff --git a/packages/idb-bridge/src/index.ts b/packages/idb-bridge/src/index.ts
index 2cb8bcf1..a0bd8b86 100644
--- a/packages/idb-bridge/src/index.ts
+++ b/packages/idb-bridge/src/index.ts
@@ -115,3 +115,5 @@ export function shimIndexedDB(factory: BridgeIDBFactory): 
void {
 }
 
 export * from "./idbtypes";
+
+export * from "./util/structuredClone";
\ No newline at end of file
diff --git a/packages/idb-bridge/src/util/structuredClone.ts 
b/packages/idb-bridge/src/util/structuredClone.ts
index 9bbeb715..1c96d743 100644
--- a/packages/idb-bridge/src/util/structuredClone.ts
+++ b/packages/idb-bridge/src/util/structuredClone.ts
@@ -18,15 +18,238 @@
 import Typeson from "typeson";
 // @ts-ignore
 import structuredCloningThrowing from 
"typeson-registry/dist/presets/structured-cloning-throwing";
+import { DataCloneError } from "./errors";
 
 const TSON = new Typeson().register(structuredCloningThrowing);
 
+const { toString: toStr } = {};
+const hasOwn = {}.hasOwnProperty;
+const getProto = Object.getPrototypeOf;
+const fnToString = hasOwn.toString;
+
+function toStringTag(val: any) {
+  return toStr.call(val).slice(8, -1);
+}
+
+function hasConstructorOf(a: any, b: any) {
+  if (!a || typeof a !== "object") {
+    return false;
+  }
+  const proto = getProto(a);
+  if (!proto) {
+    return b === null;
+  }
+  const Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
+  if (typeof Ctor !== "function") {
+    return b === null;
+  }
+  if (b === Ctor) {
+    return true;
+  }
+  if (b !== null && fnToString.call(Ctor) === fnToString.call(b)) {
+    return true;
+  }
+  return false;
+}
+
+/**
+ *
+ * @param {any} val
+ * @returns {boolean}
+ */
+function isPlainObject(val: any): boolean {
+  if (!val || toStringTag(val) !== "Object") {
+    return false;
+  }
+
+  const proto = getProto(val);
+  if (!proto) {
+    // `Object.create(null)`
+    return true;
+  }
+
+  return hasConstructorOf(val, Object);
+}
+
+function isUserObject(val: any): boolean {
+  if (!val || toStringTag(val) !== "Object") {
+    return false;
+  }
+
+  const proto = getProto(val);
+  if (!proto) {
+    // `Object.create(null)`
+    return true;
+  }
+  return hasConstructorOf(val, Object) || isUserObject(proto);
+}
+
+function isRegExp(val: any): boolean {
+  return toStringTag(val) === "RegExp";
+}
+
+function internalEncapsulate(
+  val: any,
+  outRoot: any,
+  path: string[],
+  memo: Map<any, string[]>,
+  types: Array<[string[], string]>,
+): any {
+  const memoPath = memo.get(val);
+  if (memoPath) {
+    types.push([path, "ref"]);
+    return memoPath;
+  }
+  if (val === null) {
+    return null;
+  }
+  if (val === undefined) {
+    types.push([path, "undef"]);
+    return 0;
+  }
+  if (Array.isArray(val)) {
+    memo.set(val, path);
+    const outArr: any[] = [];
+    let special = false;
+    for (const x in val) {
+      const n = Number(x);
+      if (n < 0 || n >= val.length || Number.isNaN(n)) {
+        special = true;
+        break;
+      }
+    }
+    if (special) {
+      types.push([path, "array"]);
+    }
+    for (const x in val) {
+      const p = [...path, x];
+      outArr[x] = internalEncapsulate(val[x], outRoot, p, memo, types);
+    }
+    return outArr;
+  }
+  if (val instanceof Date) {
+    types.push([path, "date"]);
+    return val.getTime();
+  }
+  if (isUserObject(val) || isPlainObject(val)) {
+    memo.set(val, path);
+    const outObj: any = {};
+    for (const x in val) {
+      const p = [...path, x];
+      outObj[x] = internalEncapsulate(val[x], outRoot, p, memo, types);
+    }
+    return outObj;
+  }
+  if (typeof val === "bigint") {
+    types.push([path, "bigint"]);
+    return val.toString();
+  }
+  if (typeof val === "boolean") {
+    return val;
+  }
+  if (typeof val === "number") {
+    return val;
+  }
+  if (typeof val === "string") {
+    return val;
+  }
+  throw Error();
+}
+
+/**
+ * Encapsulate a cloneable value into a plain JSON object.
+ */
 export function structuredEncapsulate(val: any): any {
-  return TSON.encapsulate(val);
+  const outRoot = {};
+  const types: Array<[string[], string]> = [];
+  let res;
+  try {
+    res = internalEncapsulate(val, outRoot, [], new Map(), types);
+  } catch (e) {
+    throw new DataCloneError();
+  }
+  if (res === null) {
+    return res;
+  }
+  // We need to further encapsulate the outer layer
+  if (
+    Array.isArray(res) ||
+    typeof res !== "object" ||
+    "$" in res ||
+    "$types" in res
+  ) {
+    res = { $: res };
+  }
+  if (types.length > 0) {
+    res["$types"] = types;
+  }
+  return res;
+}
+
+export function internalStructuredRevive(val: any): any {
+  val = JSON.parse(JSON.stringify(val));
+  if (val === null) {
+    return null;
+  }
+  if (typeof val === "number") {
+    return val;
+  }
+  if (typeof val === "string") {
+    return val;
+  }
+  if (!isPlainObject(val)) {
+    throw Error();
+  }
+  let types = val.$types ?? [];
+  delete val.$types;
+  let outRoot: any;
+  if ("$" in val) {
+    outRoot = val.$;
+  } else {
+    outRoot = val;
+  }
+  function mutatePath(path: string[], f: (x: any) => any): void {
+    if (path.length == 0) {
+      outRoot = f(outRoot);
+      return;
+    }
+    let obj = outRoot;
+    for (let i = 0; i < path.length - 1; i++) {
+      const n = path[i];
+      if (!(n in obj)) {
+        obj[n] = {};
+      }
+      obj = obj[n];
+    }
+    const last = path[path.length - 1];
+    obj[last] = f(obj[last]);
+  }
+  for (const [path, type] of types) {
+    if (type === "bigint") {
+      mutatePath(path, (x) => BigInt(x));
+    } else if (type === "array") {
+      mutatePath(path, (x) => {
+        const newArr: any = [];
+        for (const k in x) {
+          newArr[k] = x[k];
+        }
+        return newArr;
+      });
+    } else if (type === "date") {
+      mutatePath(path, (x) => new Date(x));
+    } else {
+      throw Error("type not implemented");
+    }
+  }
+  return outRoot;
 }
 
 export function structuredRevive(val: any): any {
-  return TSON.revive(val);
+  try {
+    return internalStructuredRevive(val);
+  } catch (e) {
+    throw new DataCloneError();
+  }
 }
 
 /**

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]