Hi,
This idea comes out of the realization that the entire ES5 object model
(property descriptors, [[Extensible]]) can be implemented on top of
POJSO and proxies. What I call POJSO (plain old js object) is an object
which interface is reduced to get/set/has/delete/keys and with a
prototype which is what most people think when they think of objects.
I've decided to try to implement such a thing to see how far I could go
and see if I could get a perf bonus along the way.
I'll be describing here the methodology, limitations, room for
improvement and start a bit of philosophy at the end.
# Methodology
## Code
All the related work can be found at
https://github.com/DavidBruant/HarmonyProxyLab/tree/ES3AndProxy/ES3AndProxy
(specifically the ES5ObjectModelEmul.js file)
## Description of the new objects
### Creating one of these objects
I'm overriding the "Object" built-in with my own version so that "new
Object" creates a proxy (which could be the self-hosted version). If
redefine most Object.* methods to work only with one of these new
objects (and throw on a normal object).
The target of the proxy is an normal object used as a POJSO (which is
kind of the point of the experiment!)
### Property descriptors
Property descriptors are stored in a nested structure:
A weakmap has object as keys and maps as values. Maps have property
names (strings) as keys and the complete property descriptors as value.
Property Descriptors are objects used as POJSO.
### Extensible
A set (ought to be a weakset!) stores all non-extensible objects.
## Conformance
I ported the relevant portion of the test262 test suite (2823 tests).
Since the objects I care to know the conformance of are the ones I
create with "new Object()", I had to mass-convert all relevant tests. I
wrote a tool (/tests/tools/Test262Converter) to do that. The tool wraps
object/array/function literals as well as "new C()" calls to generate
one of my objects using the literal-generated object as target.
I've reached 1630 pass out of 2823. I am too lazy to look at every
single failure, but clicking at some randomly, I see:
* some tests fail because they use a for-in loop or equivalent
(enumeration in second argument of create) and the enumerate trap in
proxies isn't faithfully implemented yet.
* A built-in object (like "Function" or "Date.prototype") is used in one
of the Object.* method and since my test converter doesn't wrap these,
the test just fails.
I know my Object.seal/freeze/isSealed/isFrozen methods aren't up-to-spec
but that's probably the only major non-conformant parts of my
implementation (and that's easy to do)
## Performance
### Time
I created a single micro-benchmark that does Object.defineProperty, then
Object.getOwnPropertyDescriptor to compare how the native version
compares with mine. I'm ~1.5x faster on Aurora on Linux on this very
micro-benchmark (measured by JSPerf)
I won't claim it means I'm way faster because a lot of code path aren't
tested, but that's something promising.
I haven't bothered testing performance of [[Get]] and [[Set]] since I'm
pretty sure I'll be much slower without being able to do anything about it.
### Memory
No idea how worse I am. Maybe the above time benefit happens at the
expense of memory.
I also have no idea how to measure the difference. I would be interested
if anyone has ideas in this area.
# Room for improvement
If objects were to be self-hosted this way, an hybrid native/self-hosted
would probably yield better results. Also, a specific construct for
property descriptors (instead of generic objects) would probably see
some improvements.
Largely related to recent discussion on es-discuss about performance
characteristics of private symbols against weakmaps, it's almost certain
also that sets/weaksets aren't a good way to represent whether an object
is extensible, but that's the best I had in this instance.
# Thoughts on self-hosting
Forgetting the limitations of the current proxy implementation, I think
I've proven in this experiment that self-hosting the ES5 object model is
possible.
I think this experiment fundamentally asks what the boundary is between
what is part of a core and has to be written in C++ and what can be
self-hosted.
I am nowhere near having an answer to this question, but I think it is
an interesting one.
What are the current thoughts of the JS team on that topic?
Obviously, questions, comments, etc. are more than welcome.
David
_______________________________________________
dev-tech-js-engine-internals mailing list
dev-tech-js-engine-internals@lists.mozilla.org
https://lists.mozilla.org/listinfo/dev-tech-js-engine-internals