domenic / get-originals

A web platform API that allows access to the "original" versions of the global built-in objects' properties and methods
28 stars 1 forks source link

It looks like there is no way to call the original Array.prototype.map #8

Closed bzbarsky closed 5 years ago

bzbarsky commented 6 years ago

The language for callOriginalMethod says that the target can be "Any of the classes defined in the JavaScript specification which perform brand checks". But as far as I know Array does not perform any brand checks... There's a concept of "Array exotic object", but the only thing that checks that concept is Array.isArray.

ljharb commented 6 years ago

I'm also confused why "it performs brand checks" would affect anything; I'd expect to be able to get the original of anything - whether to get a brand checker, or only to obtain the original optimized behavior after it's been replaced.

domenic commented 6 years ago

Hmm, yeah, I guess we should probably allow this. Although it's not strictly necessary, since the non-brand-checking methods don't use any magic internal slots, and so can be emulated with author code.

The reason for the brand check restriction was that, if you just do getOriginalProperty(nonBrandedObject, "foo"), the implementation has no idea what nonBrandedObject is, and so can't look up its original "foo" method.

Not sure how to solve this.

bzbarsky commented 6 years ago

Are there non-branded things with spec-defined prototypes other than Arrays?

domenic commented 6 years ago

Object, I guess? Although that barely counts.

https://streams.spec.whatwg.org/#blqs-class and https://streams.spec.whatwg.org/#cqs-class are the other examples I can think of. Definitely starting to regret not doing streams in Web IDL :-/. They're trivial though, and mostly used for communicating intent; any usage of them could be replaced with a one-liner.

ljharb commented 6 years ago

Also Error, prior to the stacks proposal landing.

I think it would simplify the mental model greatly if this could be used to get any "original".

domenic commented 6 years ago

How?

ljharb commented 6 years ago

Because then consumers of this “get originals” feature wouldn’t have to memorize which things have brand checks and which do not.

domenic commented 6 years ago

No, I meant, how would you do that? Given getOriginalProperty(nonBrandedObject, "foo"), there's no way to figure out what the original method is.

ljharb commented 6 years ago

Presumably the engine could retain a weak map that has the object as a key and a map of original methods as the value - but I’m sure there’s tons of ways to implement it, that’s just what occurs to me.

domenic commented 6 years ago

I don't think slowing down every object produced by the engine by making it a weak map key is feasible.

ljharb commented 6 years ago

Can you elaborate how that “slows down” anything?

domenic commented 6 years ago

Tracking objects as weak map keys in the garbage collector is not free; it puts them on a very different path from normal objects, which requires more processing and tracking of their lifetimes so that associated data (such as the weak map itself) can get its lifetime appropriately entangled with the key.

ljharb commented 6 years ago

Hmm - so you're saying that this cost would apply to any value made available via this proposal, and so there's some value in minimizing the number of those objects?

domenic commented 6 years ago

No, it applies to anything you put in a weak map to compensate for its lack of existing brand.

ljharb commented 6 years ago

Without a weak map (or any equivalent concept), how would the implementation hold on to the original values of branded objects, that's different from how it'd hold on to unbranded objects?

domenic commented 6 years ago

Roughly speaking, those functions already exist in memory. The brand is then used to look up the place in memory where they are, so that you can call them.

ljharb commented 6 years ago

Unbranded functions already exist in memory too, do they not?

domenic commented 6 years ago

Yes. What's missing is the brand, to look them up when given a target object.

ljharb commented 6 years ago

Sorry to keep going back and forth on this, but I'm still not clear why holding on to Array.prototype.slice is any different than holding on to String.prototype.toString :-/

bzbarsky commented 6 years ago

That's not the hard part. The hard part is knowing that the function you want is Array.prototype.slice, because in spec terms you can't really tell apart an array from any other object. Except by examining its internal methods, of course.

It's worse for Error instances; those don't even have interesting internal methods in the spec, so in spec terms they're just like Object instances with a different prototype.

Of course in implementations that's not how it actually works.