Static `super` may cause a unwanted "memory leak".
/#!/JoePea schrieb:
export default function SomeFactory(name) { let tmp = { [name]() { /* this doesn't use `super` */ } } return tmp[name] }
Then that will store each new
tmp
object in memory although the user of the factory only cares about the functions created.
Why would tmp
be stored as the [[HomeObject]] when the function
doesn't use super
? In that case a [[HomeObject]] is not needed at all.
kind , Bergi
On Tue, Aug 2, 2016 at 3:09 PM, Bergi <a.d.bergi at web.de> wrote:
Why would
tmp
be stored as the [[HomeObject]] when the function doesn't usesuper
? In that case a [[HomeObject]] is not needed at all.
To clarify this: of course the JS engine knows whether the code uses
super
or not and can decide to retain the [[HomeObject]] only when
there's some danger of its actually being needed. But do implementations
actually do this optimization in practice?
SpiderMonkey has a findPath() primitive that searches the GC heap for paths from one object to another, handy for answering questions like this one:
js> function SomeFactory(name) {
let tmp = {
[name]() { /* this doesn't use `super` */ }
}
return findPath(tmp[name], tmp);
}
js> SomeFactory("foo") === undefined
true
That is, there is no GC-path from the method back to the object; apparently
[[HomeObject]] is not being retained. What if we change the method to use
super
?
js> function SomeFactory2(name) {
let tmp = {
[name]() { return super[name](); }
};
return findPath(tmp[name], tmp);
}
js> typeof SomeFactory2("foo")
"object"
js> SomeFactory2("foo").length
1
Now there is a direct GC-reference from the method to its home object, as expected.
Ah, interesting to know. Is such an optimization guaranteed?
Specification doesn't guarantee any optimization, not even garbage collection, so conforming implementation can allocate objects until OOM.
Suppose someone reads about the awesome object-initializer short hand, and writes some code like this:
// How do we create dynamically named functions who's name is guaranteed? Like this: let dynamicMethodName = "someMethod" let tmp = { [dynamicMethodName]() { // ... } } let f = tmp[dynamicMethodName] f.name // someMethod
Little does the unsuspecting author know that the
tmp
object will be stored as theHomeObject
of the created function, therefore it may be considered that some memory has leaked. Suppose an author makes a function factory using that technique,export default function SomeFactory(name) { let tmp = { [name]() { /* this doesn't use `super` */ } } return tmp[name] }
Then that will store each new
tmp
object in memory although the user of the factory only cares about the functions created.If
super
were dynamic,tmp
would be GCable after it is no longer used in that example, and the object-initializer shorthand method could be treated just the same as any other function as opposed to a special "concise method" (where currently, to me, "concise method" simply means "function that uses staticsuper
, otherwise there's no practical difference).I believe that the object-initializer method shorthand should be syntax sugar, nothing more, so that the following is as equivalent as possible (the only difference being that the function referenced in the following example doesn't have an assigned
.name
in all JS engines, although Chrome does assign it nowadays):let dynamicMethodName = "someMethod" let tmp = { [dynamicMethodName]: function () { // ... } } let f = tmp[dynamicMethodName] f.name // sometimes undefined, "someMethod" in Chrome 51.