Bug Description
Hey, I'm back with more Proxy issues.
I encountered a bug in a RN application that works through a Proxy-based state manager. It turned out that the array length subscription wasn't working. A closer look revealed that Hermes wasn't calling the defineProperty() hook when the length changed, as stated in the specification.
Correct flow by spec for push (and one level proxying Proxy -> Array):
...
Array.prototype.push
// item operations
[[Proxy::Set]](length)
// calling proxy set() hook or...
[[Ordinary::Set]](length, receiver=Proxy)
OrdinarySet(length, receiver=Proxy)
OrdinarySetWithOwnDescriptor(length, receiver=Proxy)
[[Proxy::DefineOwnProperty]](length)
// calling proxy defineProperty() hook or...
[[Array::DefineOwnProperty]](length)
[[Array::ArraySetLength]]
OrdinaryDefineOwnProperty(length)
...
Hermes git revision (if applicable): 896ee1e
Steps To Reproduce
let log = (...args) => typeof print === 'undefined' ? console.log(JSON.stringify(args)) : print(JSON.stringify(args))
let arr = new Proxy([], {
defineProperty(target, property, attributes) {
log('define', target, property, attributes)
return Reflect.defineProperty(target, property, attributes)
},
deleteProperty(target, p) {
log('del', target, p)
return Reflect.deleteProperty(target, p)
},
})
log('push')
arr.push('a')
log('push')
arr.push('b')
log('push')
arr.push('c')
log('pop')
arr.pop()
log('shift')
arr.shift()
log('length=')
arr.length = 1
An example of how hooks work in v8
node t.js
["push"]
["define",[],"0",{"value":"a","writable":true,"enumerable":true,"configurable":true}]
["define",["a"],"length",{"value":1}]
["push"]
["define",["a"],"1",{"value":"b","writable":true,"enumerable":true,"configurable":true}]
["define",["a","b"],"length",{"value":2}]
["push"]
["define",["a","b"],"2",{"value":"c","writable":true,"enumerable":true,"configurable":true}]
["define",["a","b","c"],"length",{"value":3}]
["pop"]
["del",["a","b","c"],"2"]
["define",["a","b",null],"length",{"value":2}]
["shift"]
["define",["a","b"],"0",{"value":"b"}]
["del",["b","b"],"1"]
["define",["b",null],"length",{"value":1}]
["length="]
["define",["b"],"length",{"value":1}]
And this is how it works in hermes static_h:
hermes t.js
["push"]
["define",[],"0",{"value":"a","writable":true,"enumerable":true,"configurable":true}]
["push"]
["define",["a"],"1",{"value":"b","writable":true,"enumerable":true,"configurable":true}]
["push"]
["define",["a","b"],"2",{"value":"c","writable":true,"enumerable":true,"configurable":true}]
["pop"]
["del",["a","b","c"],"2"]
["shift"]
["define",["a","b"],"0",{"value":"b"}]
["del",["b","b"],"1"]
["length="]
Bug Description
Hey, I'm back with more Proxy issues.
I encountered a bug in a RN application that works through a Proxy-based state manager. It turned out that the array length subscription wasn't working. A closer look revealed that Hermes wasn't calling the
defineProperty()hook when the length changed, as stated in the specification.Correct flow by spec for push (and one level proxying Proxy -> Array):
...
Array.prototype.push
// item operations
[[Proxy::Set]](length)
// calling proxy
set()hook or...[[Ordinary::Set]](length, receiver=Proxy)
OrdinarySet(length, receiver=Proxy)
OrdinarySetWithOwnDescriptor(length, receiver=Proxy)
[[Proxy::DefineOwnProperty]](length)
// calling proxy
defineProperty()hook or...[[Array::DefineOwnProperty]](length)
[[Array::ArraySetLength]]
OrdinaryDefineOwnProperty(length)
...
gradle cleanand confirmed this bug does not occur with JSCHermes git revision (if applicable): 896ee1e
Steps To Reproduce
An example of how hooks work in v8
And this is how it works in hermes static_h: