我正在寻求忠实地为IndexedDB polyfill(包括Node)实现结构化克隆算法。虽然算法的某些类型的明确白名单大部分是明确的,但我更理解它的全面黑名单(以及如何检测这种黑名单类型)。
结构化克隆算法states:
默认情况下,平台对象不是可复制对象。
...“平台对象”是defined in WebIDL:
在一组IDL片段的给定实现中,对象可以被描述为平台对象,用户对象或两者都不是。有两种对象被认为是平台对象:
- 实现非回调接口的对象;
- 表示IDL DOMExceptions的对象。
对于另一类(可能)不可克隆的对象,SCA在将某些类型列入白名单后mentions:
- 否则,如果输入有[[Prototype]]或[[Extensible]]以外的任何内部插槽,则抛出“DataCloneError”DOMException。
醇>例如,[[PromiseState]]或[[WeakMapData]]内部插槽。
- 否则,如果input是异物,则抛出“DataCloneError”DOMException。
醇>
“异国情调的对象”在ECMAScript 2017中定义为:
对象没有一个或多个基本内部方法的默认行为 注意 任何不是普通对象的对象都是异国情调的对象。
由于SCA抛出IsCallable
检查[[Call]]
(和“所有ECMAScript函数对象都有[[Call]]内部方法定义在这里”),我已经排除了“基本内部方法”对于函数,导致以下内容:
因此,关于拒绝的条件出现了一些问题:
[[Clone]]
内部方法且因此可以列入白名单的平台对象到目前为止我的实施是:
function throwUponDataCloneError (val) {
// Should also throw with:
// 1. `IsDetachedBuffer` (a process not called within the ECMAScript spec)
// 2. We are not dealing with `Blob`/`File` at this point as the
// IndexedDB spec requires synchronous rejection and we'd apparently
// have to use deprecated sync XHR (or `readFileSync` in Node) to get this
// 'Proxy' should be rejected but no apparent way to introspect on it
const stringTag = ({}.toString.call(val).slice(8, -1));
if (typeof val === 'symbol' || // Symbol's `toStringTag` is only "Symbol" for its initial value, so safer to check `typeof`
[
'Function', // All functions including bound function exotic objects; rejected per `IsCallable` check (could also be covered by `typeof === 'function'`)
'Arguments', // A non-array exotic object
'Error', // `Error` and other errors have the [[ErrorData]] internal slot and give "Error"
// The following checks are only based on the default expected `toStringTag` values
'Module', // A non-array exotic object
'Promise', // Promise instances have an extra slot ([[PromiseState]]) but not throwing in Chrome `postMessage`
'WeakMap', // WeakMap instances have an extra slot ([[WeakMapData]]) but not throwing in Chrome `postMessage`
'WeakSet' // WeakSet instances have an extra slot ([[WeakSetData]]) but not throwing in Chrome `postMessage`
].includes(stringTag) ||
val === Object.prototype || // A non-array exotic object but not throwing in Chrome `postMessage`
) {
throw new DOMException('The object cannot be cloned.', 'DataCloneError');
}
}
作为检测平台对象的一种方法,WebIDL声明:
实现一个或多个接口的平台对象的类字符串必须是平台对象主接口的标识符。
...“类字符串”为defined as:
要包含在从
Object.prototype.toString
返回的字符串中的字符串。如果对象具有类字符串,则该对象在创建时必须具有名称为@@toStringTag
符号且其值为指定字符串的属性。
事实上,我可以找到一个SCA的JS实现,CycloneJS采用这种方法来抛弃任何未被识别的@@toStringTag
。
然而,我担心:
@@toStringTag
不是内部插槽,因此具有不同内容的对象不是自动取消限定符,我会担心拒绝任何具有这种不同标记的对象会过于禁止。但也许这些案例太少而无法引起关注。@@toStringTag
,因此可能会因为关注方法#1而被拒绝。基本上,我想知道Object.prototype.toString
白名单方法是否应该足以避免误报或漏报,以及任何其他检测技术是否可以提供更细粒度的控制。