Skip to content

Commit

Permalink
Make __proto__ more closely match the spec
Browse files Browse the repository at this point in the history
Create test cases for the "ES6" behavior that matches the spec,
and different tests for the backwardly-compatible behavior.
  • Loading branch information
gbrail committed Aug 25, 2020
1 parent 5a8fed1 commit 6cc180b
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 55 deletions.
9 changes: 7 additions & 2 deletions src/org/mozilla/javascript/SpecialRef.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,13 @@ public Object set(Context cx, Scriptable scope, Object value)
} while (search != null);
}
if (type == SPECIAL_PROTO) {
if (cx.getLanguageVersion() < Context.VERSION_ES6 ||
!(target instanceof BaseFunction)) {
if (cx.getLanguageVersion() >= Context.VERSION_ES6) {
if ((value != null && !"object".equals(ScriptRuntime.typeof(value))) ||
!"object".equals(ScriptRuntime.typeof(target))) {
return Undefined.instance;
}
target.setPrototype(obj);
} else {
target.setPrototype(obj);
}
} else {
Expand Down
41 changes: 41 additions & 0 deletions testsrc/jstests/backwardcompat/backward-proto-property.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
load('testsrc/assert.js');

// Test old behavior of "__proto__"

// __proto__ can be set to lots of things
let obj = {};
assertTrue(obj.__proto__ === Object.prototype);
obj.__proto__ = undefined;
assertFalse(obj.__proto__ === Object.prototype);
obj.__proto__ = true;
assertFalse(obj.__proto__ === Object.prototype);
obj.__proto__ = 12345;
assertFalse(obj.__proto__ === Object.prototype);
obj.__proto__ = 'foobar';
assertFalse(obj.__proto__ === Object.prototype);

// __proto__ on an object can indeed be set to another object
let prot = {
bar: function() { print('bar'); }
};
obj = {};
assertEquals(prot, obj.__proto__ = prot);
assertFalse(obj.__proto__ === Object.prototype);
assertEquals('function', typeof obj.__proto__.bar);
assertEquals(prot, obj.__proto__);

// __proto__ on an object can be set to null
obj = {};
assertNull(obj.__proto__ = null);
assertFalse(obj.__proto__ === Object.prototype);

//__proto__ setting on a non-object also works
function f() {}
assertTrue(f.__proto__ === Function.prototype);
f.__proto__ = prot;
assertFalse(f.__proto__ === Function.prototype);
assertTrue(f.__proto__ === prot);
f.__proto__ = null;
assertFalse(f.__proto__ === Function.prototype);

"success";
41 changes: 41 additions & 0 deletions testsrc/jstests/es6/proto-property.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
load('testsrc/assert.js');

// Test section B.2.2.1.2 of ECMAScript 6.0, which
// defines the behavior of the "__proto__" special property.

// __proto__ can only be set to an object or to null
let obj = {};
assertTrue(obj.__proto__ === Object.prototype);
assertEquals(undefined, obj.__proto__ = undefined);
assertTrue(obj.__proto__ === Object.prototype);
assertEquals(undefined, obj.__proto__ = true);
assertTrue(obj.__proto__ === Object.prototype);
assertEquals(undefined, obj.__proto__ = 12345);
assertTrue(obj.__proto__ === Object.prototype);
assertEquals(undefined, obj.__proto__ = 'foobar');
assertTrue(obj.__proto__ === Object.prototype);

// __proto__ on an object can indeed be set to another object
let prot = {
bar: function() { print('bar'); }
};
obj = {};
assertEquals(prot, obj.__proto__ = prot);
assertFalse(obj.__proto__ === Object.prototype);
assertEquals('function', typeof obj.__proto__.bar);
assertEquals(prot, obj.__proto__);

// __proto__ on an object can be set to null
obj = {};
assertNull(obj.__proto__ = null);
assertFalse(obj.__proto__ === Object.prototype);

// However, __proto__ setting on a non-object does nothing
function f() {}
assertTrue(f.__proto__ === Function.prototype);
assertEquals(undefined, f.__proto__ = prot);
assertTrue(f.__proto__ === Function.prototype);
assertEquals(undefined, f.__proto__ = null);
assertTrue(f.__proto__ === Function.prototype);

"success";
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.mozilla.javascript.tests.backwardcompat;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.drivers.LanguageVersion;
import org.mozilla.javascript.drivers.RhinoTest;

@RhinoTest("testsrc/jstests/backwardcompat/backward-proto-property.js")
@LanguageVersion(Context.VERSION_1_8)
public class BackwardProtoPropertyTest {}
53 changes: 0 additions & 53 deletions testsrc/org/mozilla/javascript/tests/es5/NativeFunctionTest.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.mozilla.javascript.tests.es6;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.drivers.LanguageVersion;
import org.mozilla.javascript.drivers.RhinoTest;

@RhinoTest("testsrc/jstests/es6/proto-property.js")
@LanguageVersion(Context.VERSION_ES6)
public class ProtoPropertyTest {}

0 comments on commit 6cc180b

Please sign in to comment.