[Rhino] Setting (function () {}).__proto__ broken in recent version of HtmlUnit
See original GitHub issueProblem in brief
Setting of (function () {}).__proto__
is a no-op in recent versions of HtmlUnit breaking some libraries.
Setting of new Object().__proto__
still works as expected.
This problem affects the Knockout library of at least v3.3.0 and the current version v3.5.1.
Details
When Rhino is running in ES6 mode, setting of _proto__
on a function object is denied by this condition in SpecialRef that only allows setting on objects. This behaviour is incompatible with major browsers (Chrome / FF / Edge / IE11) and breaks libraries that rely on this.
Test case
<html>
<head>
<script>
function test() {
var proto = { foo: function (n) { console.log('foo'); } }
proto.foo();
var obj = function () {}
console.log("Original __proto__:", obj.__proto__);
obj.__proto__ = proto;
console.log("Modified __proto__:", obj.__proto__);
console.log("obj.foo:", obj.foo);
try {
obj.foo();
} catch (e) {
console.log(e);
}
}
</script>
</head>
<body onload="test()">
</body>
</html>
Expected:
foo
Original __proto__: function () {[native code]}
Modified __proto__: ({foo: (function (n) { console.log("foo"); })})
obj.foo: (function (n) { console.log("foo"); })
foo
HtmlUnit 2.46.0 fails with the following:
foo
Original __proto__: function () {[native code]}
Modified __proto__: function () {[native code]}
obj.foo: net.sourceforge.htmlunit.corejs.javascript.Undefined@0
TypeError: Cannot find function foo in object function () {...}.
Suggested fix
The documentation for Function’s __proto__
(Object.prototype__proto__
) doesn’t seem to make any distinction between the state of support for objects and function objects so it seems they should be equally supported, if supported at all.
Out of the possible values returned by typeof
, it seems “function” and “object” are the only ones that make sense:
[undefined, null, true, 123, 123n, "abc", Symbol(), function () {}, {}].forEach(x => {
try {
var proto = {}
var r = (x.__proto__ = proto);
console.log(typeof x + ": returned=" + (r === proto) + " changed=" + (x.__proto__ === proto))
} catch (e) {
console.log(typeof x + ": " + e);
}
})
/* Output of Chrome / FF:
undefined: TypeError: Cannot set property '__proto__' of undefined
object: TypeError: Cannot set property '__proto__' of null
boolean: returned=true changed=false
number: returned=true changed=false
bigint: returned=true changed=false
string: returned=true changed=false
symbol: returned=true changed=false
function: returned=true changed=true
object: returned=true changed=true
*/
(This can be run in HtmlUnit 2.46.0 by removing 123n
(BigInt(123)
) which is not supported.)
In lieu of this, my suggestion is for this condition in SpecialRef
be changed as follows:
if (cx.getLanguageVersion() >= Context.VERSION_ES6) {
- if ((value != null && !"object".equals(ScriptRuntime.typeof(value))) ||
- !"object".equals(ScriptRuntime.typeof(target))) {
- return Undefined.instance;
- }
+ if (value != null && !"object".equals(ScriptRuntime.typeof(value))) {
+ return obj;
+ }
+ switch (ScriptRuntime.typeof(target)) {
+ case "object":
+ case "function":
+ break;
+ default:
+ return obj;
+ }
target.setPrototype(obj);
} else {
target.setPrototype(obj);
}
Issue Analytics
- State:
- Created 3 years ago
- Comments:5 (5 by maintainers)
Top GitHub Comments
PR https://github.com/mozilla/rhino/pull/822 - hopefully this find a way into Rhino soon
Available with the latest SNAPSHOT.
Thanks for your support.