Distributive-Network / PythonMonkey

A Mozilla SpiderMonkey JavaScript engine embedded into the Python VM, using the Python engine to provide the JS host environment.
https://pythonmonkey.io
Other
826 stars 38 forks source link

object prototype chaining does not work correctly on proxied python objects #399

Open caleb-distributive opened 1 month ago

caleb-distributive commented 1 month ago

Describe your feature request here.

Proxied python objects currently do not have a proper prototype chain, and we are instead mimicking prototype chain behaviour by special-casing [[GetOwnPropertyDescriptor]] when the prop name is equal to a default prop name on the intended prototype.

For example, in:

import pythonmonkey as pm
l = [1, 2, 3]
pm.eval("(l) => l.pop()")(l)

l.pop is accomplished by us intercepting prop lookup on l, seeing that the prop name is "pop", and passing back our own implementation of pop that is special-cased for python lists. This has some interesting consequences:

pm.eval("(l) => Object.getPrototypeOf(l) === Array.prototype")(l) # True
pm.eval("(l) => l.pop === Array.prototype.pop")(l) # False, should be True
pm.eval("(l) => l.pop === l.pop")(l) # False!!, should DEFINITELY be True

I propose that we define our own classes/prototypes for our proxied objects, and have those classes/prototypes inherit from the respective mimicked types, like so:

listObject = []
dictObject = []
bytesObject = b""
iterObject = iter(())

class Class:
  pass
classObject = Class()

pm.eval("""
(listObject, dictObject, bytesObject, iterObject, classObject) => {
  assert(Object.getPrototypeOf(listObject) === PyListProxy.prototype);
  assert(Object.getPrototypeOf(dictObject) === PyDictProxy.prototype);
  assert(Object.getPrototypeOf(bytesObject) === PyBytesProxy.prototype);
  assert(Object.getPrototypeOf(iterObject) === PyIterProxy.prototype);
  assert(Object.getPrototypeOf(classObject) === PyObjectProxy.prototype);  // would be interesting if we could get python class 
  inheritance to be represented in the prototype chain with proxied Classes, though I don't think that's currently in scope, particularly since Python has true multiple inheritance

  assert(Object.getPrototypeOf(PyListProxy.prototype) === Array.prototype);
  assert(Object.getPrototypeOf(PyDictProxy.prototype) === Object.prototype);
  assert(Object.getPrototypeOf(PyBytesProxy.prototype) === Uint8Array.prototype);
  assert(Object.getPrototypeOf(PyIterProxy.prototype) === Object.prototype);
  assert(Object.getPrototypeOf(PyObjectProxy.prototype) === Object.prototype);
}
""")(listObject, dictObject, bytesObject, iterObject, classObject)

Code example

No response

wesgarland commented 2 weeks ago

Q: Is this holding up BiFrost 2? If so, how? If not, we should pull this from the 1.0 Milestone

Some thoughts:

wiwichips commented 2 weeks ago

@wesgarland it is not holding up Bifrost2 - it was moved to the > V1.0.1 release (aka after 1.0 Milestone)