Yeah I modified gozala's method a bit. The value isn't kept in valueOf,
rather in a null proto object that allows different WeakMaps to not step on
another one.
Maybe my understanding was flawed, but my understanding was that it's not
an issue for these single purpose objects/closures in terms of garbage
collection. A set of objects (mostly null proto objects that only ever have
one or no refereces to other objects, and only one reference to them from
the local system, essentially will act like one "unit" and ultimately the
livelihood of all of them maps back to the single key object that is the
entry point for them. The only other object that ever gets a reference to
any of them is the weakmap instance, which never keeps any references. It
simply has all the keys to unlock the gates to get to the mapped value when
requested.
Would the garbage collection for this be more complex than I imagine? Or
would it be pretty easy to clearly link them in the way I supposed. If my
understanding is right, the remaining issue is the circular reference one
in terms of key -> value -> key -> value keeping each other alive. I had
kind of supposed that this itself would fall under the same rules though
because it's determinable whether that kind of set is reachable from roots
or not.
I'm also curious of what significance, if any, of the fact that the actual
organization of the data "map" isn't just weak by declaration/fiat but is
weak in the sense that the weakmap itself actually has no references to any
data at all aside from one string key and one hash key that proves identity
and can only do its job by being given references to objects and seeing if
the keys work. Perhaps this is immaterial or I may just not understand the
underlying reality of how the data is organized.
re: Mark there's a few useful tweaks for performance as well as security.
The first for performance is the double array [keys] and [values] outlined
on the wiki and used in your implementation loses out on performance having
to do the O(n) indexOf look up. The structure I ended up using starts with
the same idea of a single global HIDDEN_NAME entry point, but then from
there the next level works by assigning each WeakMap its own random name so
it's just another property lookup. It's probably helpful to show a
condensed definition of what is happening.
`weakmap.get(obj)` turns into:
var unlocker =
weakmap[globalUID][internalUID](identityProofFromInternal).value;
obj[globalUID][unlockerUID](identityProofFromUnlocker).value
Going in reverse order, starting from `.value` at the end here's what
happens:
* The 'value' is the sole property on a "lockbox" and is shaped as
`Object.preventExtensions(Object.create(null,
{ value: { value: undefined, writable: true } }))`. This is the slot for
the value for a single weakmap.
* The closure containing the lockbox is a "locker" created from `new
Function('h','l', '"use strict"; return function(k){ if (k === h) return l;
}')(hashkey, lockbox)`. The lockbox should only have references to one or
two other objects: the hashkey and potentially an object set as the value.
* The "unlocker" creates the lockers using its private hashkey
`Object.create(null)`.
An unlocker also has its own UID in order for it to address the lockers it
makes.
* The lockers, named by unlockerUID, are defined on the `obj[globalUID]`
"perObjectStorage", which is also `Object.create(null)` and created on
demand. In the SES implementation, this is the area where the [key] and
[value] arrays live instead.
* The weakmaps themselves are stored in the same manner, so the first step
is unwrapping the weakmap's locker from the shell public interface. The
"internalUID" would be the UID of the locker for unwrapping weakmaps.
* "globalUID" is generated inside a closured and has a dedicated function
responsible for retrieving existing or defining new perObjectStorages. This
is also where Object.getOwnPropertyNames is shimmed.
getOwnPropertyNames is shimmed such that it first checks for hasOwn(obj,
globalUID) so that in most cases there's no performance penalty added for
looping through the property array to splice out things to hide. If found
then a splice(indexOf) is done. This is the only function that is monkey
patched. This is also the only place indexOf is used. All other lookups are
property lookups or variable records in closures.
With SES the goal is different than mine, as I'm mostly interested in the
various benefits provided by using weakmaps without too much focus on
security. But unless my understanding of the SES WeakMap shim is
incomplete, it is inherently insecure in that it is reliant on preventing
unauthorized access to finding the store. If one is able to get a real
getOwnPropertyNames through some means then all bets are off. With the
scheme in use above, the weakmap has the only unforgeable key to the data,
so even the object itself couldn't unlock it and even if someone was able
to expose the hidden properties by importing GOPN from another frame and
get all the lockers they'd be unable to open them. They'd ruin the garbage
collection by introducing references into an otherwise peaceful system but
it wouldn't be a security breach.
I've tried to minimize any incidental unintended storage so a number of the
functions generated use the `new Function` route of minimizing implicit
scope scope data.
On Fri, May 11, 2012 at 1:38 AM, Allen Wirfs-Brock <[email protected]>wrote:
>
> On May 10, 2012, at 7:24 PM, Brandon Benvie wrote:
>
> > Ahh yes I see. The circular reference case. Well, almost....
>
> And that's not the only leak. You create a new valueOf method for each
> WeakDictionary a given object is used to key. These methods are threaded
> off of the key object starting with the most recently created one and each
> valueOf indirectly captures the value associated with that object key in
> the corresponding WeakDictionary. If there are no reference to a
> WeakDictionary it will get garbage collected. However, the corresponding
> valueOf method for each object that was used as a key for that
> WeakDictionary will stay behind. So will the value that was associated
> with that key in the dictionary.
>
> This isn't a bad solution if you know need a weak keyed map where you know
> that each object is only going to key a single such map
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss