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

Reply via email to