Yusuke SUZUKI (2015-06-17T16:15:11.000Z)
On Wed, Jun 17, 2015 at 10:41 PM, Mark Miller <erights at gmail.com> wrote:

> Hi Yusuke, congratulations and THANK YOU! I learned something important
> reading your messages. The notion that we can preserve non-observability
> when making one thing a WeakMap iff we make all other WeakMaps be strong
> for those same objects is true, novel, and very surprising. I have been
> working on such concepts for decades and never come across anything like it.
>

Thanks for your clarification!
If the target object is resilient (it can be re-generated without an
observable side effects) and immutable, we can inverse strong & weak maps.

In this case, strong internal Map is converted into WeakMap and user
exposed WeakMap are converted into strong Map.
The internal map itself cannot be collected, but, user exposed maps can be
collected.
So by inverting them, the target objects become collectable.



>
> In this case, I suspect that implementers will continue to choose the
> memory leak rather than make WeakMap more complex in this way. But you have
> now given them a choice, which is great! The spec does not need to change
> to enable this choice. The spec is only about observable differences, and
> the space optimization you suggest would be unobservable.
>

I'm planning to discuss about this implementation in JavaScriptCore because
I'm an template strings implementor in JavaScriptCore.

BTW, by extending the implementation slightly, we can still preserve
performance optimization.
When generating template site objects at first, generate it, register it to
realm's WeakMap and strongly reference it in the JavaScript code site
instead of realm.
This is the same behavior to that immutable JSStrings are stored and
strongly referenced in the JavaScript code itself.
When the JavaScript code is discarded, we can collect them.

And by slightly modifying the per-object table proposal, we can still
support it with this change ;)
We can define WeakMap as

class WeakMap {
    constructor(...) {
        this.privateSymbol = @privateSymbol;
        this.map = new Map();
    }

    get(object) {
        if (object is template site object) {
            return this.map.get(object);
        }
        return object[this.privateSymbol];
    }

    set(object, value) {
        if (object is template site object) {
            this.map.set(object, value);
            return this;
        }
        object[this.privateSymbol] = value;
        return this;
    }
}



>
> Your observation, being general, may find other applications even if it is
> not used to optimize this one. This observation is not language specific;
> it may well find application in other memory safe languages including those
> yet to be invented. You have added another tool to our toolbox. You have
> deepened our understanding of what is possible.
>
>
>
>
> On Tue, Jun 16, 2015 at 10:45 PM, Yusuke SUZUKI <utatane.tea at gmail.com>
> wrote:
>
>> On Wed, Jun 17, 2015 at 2:29 PM, Yusuke SUZUKI <utatane.tea at gmail.com>
>> wrote:
>>
>>> Thanks. And sorry for the late reply.
>>>
>>> On Wed, Jun 17, 2015 at 11:31 AM, Mark S. Miller <erights at google.com>
>>> wrote:
>>>
>>>> Hi Yusuke, I am not sure I understood your message. Could you show some
>>>> example code that would observe the observable difference you have in mind?
>>>>
>>>>
>>>>
>>>> On Tue, Jun 16, 2015 at 7:25 PM, Yusuke SUZUKI <utatane.tea at gmail.com>
>>>> wrote:
>>>>
>>>>> Hi forks,
>>>>>
>>>>> In ES6 spec, template site objects are strongly referenced by the
>>>>> realm.[[templateMap]].
>>>>> So naive implementation leaks memory because it keeps all the site
>>>>> objects in the realm.
>>>>>
>>>>
>>> To lookup the identical template site objects, template site objects are
>>> stored in the realm.[[templateMap]].
>>> So they are strongly referenced and the naive implementation leaks
>>> memory.
>>>
>>> // By writing the following code, we can leak memory that GC cannot
>>> collect.
>>>
>>> function tag(siteObject)
>>> {
>>>     return siteObject;
>>> }
>>>
>>> for (var i = 0;; ++i) {
>>>     eval("tag`" + i + "`");
>>> }
>>>
>>>
>>>>> However, we can alleviate this situation.
>>>>> Because template site objects are frozen completely, it behaves as if
>>>>> it's a primitive value.
>>>>> It enables the implementation to reference it from the realm weakly.
>>>>> When all disclosed site objects are not referenced, we can GC them because
>>>>> nobody knows the given site object is once collected (& re-generated).
>>>>>
>>>>
>>> By implementing the realm.[[templateMap]] as WeakMap, we can alleviate
>>> this situation.
>>>
>>> function tag(siteObject) {
>>>     // Since siteObject is frozen, we cannot attach a property to it.
>>>     // So if nobody has the reference to the siteObject, we can collect
>>> this siteObject since identity can not be tested across already collected &
>>> newly created site object.
>>> }
>>>
>>>
>>>>
>>>>> But, even if the object is frozen, we can bind the property with it
>>>>> indirectly by using WeakMap.
>>>>> As a result, if the site objects are referenced by the realm weakly,
>>>>> users can observe it by using WeakMap.
>>>>>
>>>>
>>> var map = new WeakMap();
>>> function tag(siteObject) {
>>>     return siteObject;
>>> }
>>>
>>> var siteObject = tag`hello`;
>>> map.set(siteObject, true);
>>>
>>> gc();  // If realm.[[templateMap]] is implemente by the WeakMap,
>>> siteObject will be collected.
>>>
>>> var siteObject = tag`hello`;
>>>
>>> map.get(siteObject);  // false, but should be true.
>>>
>>>
>>>
>>>>
>>>>> To avoid this situation, we need to specially handle template site
>>>>> objects in WeakMap; WeakMap refers template site objects strongly (if we
>>>>> choose the weak reference implementation for realm.[[templateMap]]).
>>>>> But this may complicate the implementation and it may prevent
>>>>> implementing WeakMap as per-object table (it can be done, but it is no
>>>>> longer simple private symbols).
>>>>>
>>>>
>>> var map = new WeakMap();
>>> function tag(siteObject) {
>>>     return siteObject;
>>> }
>>>
>>> tag`hello`;
>>>
>>> gc();  // siteObject can be collected because there's no reference to it
>>> if the [[templateMap]] is implemented as WeakMap.
>>>
>>> var siteObject = tag`hello`;
>>>
>>> map.set(siteObject, true);  // To avoid the previously described
>>> situation, WeakMap specially handles the siteObject. It is now refereneced
>>> strongly by the WeakMap.
>>>
>>> gc();
>>>
>>> var siteObject = tag`hello`;
>>>
>>> map.get(siteObject);  // true
>>>
>>> // And if WeakMap is collected, siteObject can be collected.
>>>
>>
>> Fix.
>>
>>
>> var map = new WeakMap();
>> function tag(siteObject) {
>>     return siteObject;
>> }
>>
>> tag`hello`;
>>
>> gc();  // siteObject can be collected because there's no reference to it
>> if the [[templateMap]] is implemented as WeakMap.
>>
>> (function () {
>>     var siteObject = tag`hello`;
>>     map.set(siteObject, true);  // To avoid the previously described
>> situation, WeakMap specially handles the siteObject. It is now refereneced
>> strongly by the WeakMap.
>> }());
>>
>> gc();
>>
>> (function () {
>>     var siteObject = tag`hello`;
>>     map.get(siteObject);  // true
>> }());
>>
>> // And if WeakMap is collected, siteObject can be collected.
>> map = null;
>> gc();
>>
>>
>>
>>
>>>
>>>
>>>>
>>>>> Is it intentional semantics? I'd like to hear about this.
>>>>> (And please point it if I misunderstood)
>>>>>
>>>>> Best Regards,
>>>>> Yusuke Suzuki
>>>>>
>>>>> _______________________________________________
>>>>> es-discuss mailing list
>>>>> es-discuss at mozilla.org
>>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>>>
>>>>>
>>>>
>>>>
>>>> --
>>>>     Cheers,
>>>>     --MarkM
>>>>
>>>
>>>
>>
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>>
>>
>
>
> --
> Text by me above is hereby placed in the public domain
>
>   Cheers,
>   --MarkM
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20150618/dcff2d40/attachment-0001.html>
d at domenic.me (2015-07-07T01:39:22.170Z)
On Wed, Jun 17, 2015 at 10:41 PM, Mark Miller <erights at gmail.com> wrote:

> Hi Yusuke, congratulations and THANK YOU! I learned something important
> reading your messages. The notion that we can preserve non-observability
> when making one thing a WeakMap iff we make all other WeakMaps be strong
> for those same objects is true, novel, and very surprising. I have been
> working on such concepts for decades and never come across anything like it.
>

Thanks for your clarification!
If the target object is resilient (it can be re-generated without an
observable side effects) and immutable, we can inverse strong & weak maps.

In this case, strong internal Map is converted into WeakMap and user
exposed WeakMap are converted into strong Map.
The internal map itself cannot be collected, but, user exposed maps can be
collected.
So by inverting them, the target objects become collectable.



> In this case, I suspect that implementers will continue to choose the
> memory leak rather than make WeakMap more complex in this way. But you have
> now given them a choice, which is great! The spec does not need to change
> to enable this choice. The spec is only about observable differences, and
> the space optimization you suggest would be unobservable.
>

I'm planning to discuss about this implementation in JavaScriptCore because
I'm an template strings implementor in JavaScriptCore.

BTW, by extending the implementation slightly, we can still preserve
performance optimization.
When generating template site objects at first, generate it, register it to
realm's WeakMap and strongly reference it in the JavaScript code site
instead of realm.
This is the same behavior to that immutable JSStrings are stored and
strongly referenced in the JavaScript code itself.
When the JavaScript code is discarded, we can collect them.

And by slightly modifying the per-object table proposal, we can still
support it with this change ;)
We can define WeakMap as

```js
class WeakMap {
    constructor(...) {
        this.privateSymbol = @privateSymbol;
        this.map = new Map();
    }

    get(object) {
        if (object is template site object) {
            return this.map.get(object);
        }
        return object[this.privateSymbol];
    }

    set(object, value) {
        if (object is template site object) {
            this.map.set(object, value);
            return this;
        }
        object[this.privateSymbol] = value;
        return this;
    }
}
```