diff --git a/lib/Parser/rterrors.h b/lib/Parser/rterrors.h
index 853178e168b..462a89702ed 100644
--- a/lib/Parser/rterrors.h
+++ b/lib/Parser/rterrors.h
@@ -275,6 +275,8 @@ RT_ERROR_MSG(JSERR_DeletePropertyWithSuper, 5146, "Unable to delete property '%s
RT_ERROR_MSG(JSERR_DetachedTypedArray, 5147, "%s: The ArrayBuffer is detached.", "The ArrayBuffer is detached.", kjstTypeError, 0)
RT_ERROR_MSG(JSERR_AsmJsCompileError, 5148, "%s: Compiling asm.js failed.", "Compiling asm.js failed.", kjstError, 0)
+RT_ERROR_MSG(JSERR_ImmutablePrototypeSlot, 5149, "%s: Can't set the prototype of this object.", "Can't set the prototype of this object.", kjstTypeError, 0)
+
/* Error messages for misbehaved Async Operations for use in Promise.js */
RT_ERROR_MSG(ASYNCERR_NoErrorInErrorState, 5200, "", "Status is 'error', but getResults did not return an error", kjstError, 0)
RT_ERROR_MSG(ASYNCERR_InvalidStatusArg, 5201, "", "Missing or invalid status parameter passed to completed handler", kjstError, 0)
diff --git a/lib/Runtime/Library/JavascriptObject.cpp b/lib/Runtime/Library/JavascriptObject.cpp
index 52b0981fe23..93a4d063148 100644
--- a/lib/Runtime/Library/JavascriptObject.cpp
+++ b/lib/Runtime/Library/JavascriptObject.cpp
@@ -176,6 +176,16 @@ namespace Js
return FALSE;
}
+ if (object->IsProtoImmutable())
+ {
+ // ES2016 19.1.3:
+ // The Object prototype object is the intrinsic object %ObjectPrototype%.
+ // The Object prototype object is an immutable prototype exotic object.
+ // ES2016 9.4.7:
+ // An immutable prototype exotic object is an exotic object that has an immutable [[Prototype]] internal slot.
+ JavascriptError::ThrowTypeError(scriptContext, JSERR_ImmutablePrototypeSlot);
+ }
+
// 6. If V is not null, then
// a. Let p be V.
// b. Repeat, while p is not null
diff --git a/lib/Runtime/Library/ObjectPrototypeObject.h b/lib/Runtime/Library/ObjectPrototypeObject.h
index 8cf28d23d4a..bd9c1608035 100644
--- a/lib/Runtime/Library/ObjectPrototypeObject.h
+++ b/lib/Runtime/Library/ObjectPrototypeObject.h
@@ -36,6 +36,7 @@ namespace Js
// Indicates if __proto__ is enabled currently (note that it can be disabled and re-enabled),
// only useful for diagnostics to decide displaying __proto__ or [prototype].
bool is__proto__Enabled() const { return __proto__Enabled; }
+ BOOL IsProtoImmutable() const { return true; }
void PostDefineOwnProperty__proto__(RecyclableObject* obj);
};
diff --git a/lib/Runtime/Types/RecyclableObject.h b/lib/Runtime/Types/RecyclableObject.h
index 3e133882105..cceda29eb9d 100644
--- a/lib/Runtime/Types/RecyclableObject.h
+++ b/lib/Runtime/Types/RecyclableObject.h
@@ -285,6 +285,7 @@ namespace Js {
virtual BOOL IsConfigurable(PropertyId propertyId) { return false; }
virtual BOOL IsEnumerable(PropertyId propertyId) { return false; }
virtual BOOL IsExtensible() { return false; }
+ virtual BOOL IsProtoImmutable() const { return false; }
virtual BOOL PreventExtensions() { return false; }; // Sets [[Extensible]] flag of instance to false
virtual void ThrowIfCannotDefineProperty(PropertyId propId, PropertyDescriptor descriptor);
virtual void ThrowIfCannotGetOwnPropertyDescriptor(PropertyId propId) {}
diff --git a/test/UnitTestFramework/UnitTestFramework.js b/test/UnitTestFramework/UnitTestFramework.js
index de4af3d9928..865ec507ff0 100644
--- a/test/UnitTestFramework/UnitTestFramework.js
+++ b/test/UnitTestFramework/UnitTestFramework.js
@@ -294,11 +294,15 @@ var assert = function assert() {
///
/// expectedException: A specific exception type, e.g. TypeError.
/// if undefined, this will pass if testFunction throws any exception.
- /// expectedErrorMessage: If specified, verifies the thrown Error has expected message.
+ /// message: Test-provided explanation of why this particular exception should be thrown.
+ /// expectedErrorMessage: If specified, verifies the thrown Error has expected message.
+ /// You only need to specify this if you are looking for a specific error message.
+ /// Does not require the prefix of e.g. "TypeError:" that would be printed by the host.
///
/// Example:
/// assert.throws(function() { eval("{"); }, SyntaxError, "expected SyntaxError")
/// assert.throws(function() { eval("{"); }) // -- use this when you don't care which exception is thrown.
+ /// assert.throws(function() { eval("{"); }, SyntaxError, "expected SyntaxError with message about expected semicolon.", "Expected ';'")
///
var noException = {}; // Some unique object which will not be equal to anything else.
var exception = noException; // Set default value.
diff --git a/test/es6/proto_disable.baseline b/test/es6/proto_disable.baseline
index a3f168a460d..33c7b6471d7 100644
--- a/test/es6/proto_disable.baseline
+++ b/test/es6/proto_disable.baseline
@@ -1,14 +1,11 @@
-*** Running test #1 (0): Change Object.prototype.__proto__ value
-Object.prototype.__proto__ = null
-PASSED
-*** Running test #2 (1): seal/freeze Object.prototype
+*** Running test #1 (0): seal/freeze Object.prototype
Object.seal(Object.prototype)
Object.freeze(Object.prototype)
PASSED
-*** Running test #3 (2): delete Object.prototype.__proto__
+*** Running test #2 (1): delete Object.prototype.__proto__
delete Object.prototype.__proto__
PASSED
-*** Running test #4 (3): DefineOwnProperty with missing/different attribute set
+*** Running test #3 (2): DefineOwnProperty with missing/different attribute set
Object.defineProperty(Object.prototype, "__proto__", {})
Object.defineProperty(Object.prototype, "__proto__", {enumerable: false})
Object.defineProperty(Object.prototype, "__proto__", {configurable: true})
@@ -18,6 +15,6 @@ Object.defineProperty(Object.prototype, "__proto__", {enumerable: false, configu
Object.defineProperty(Object.prototype, "__proto__", {value: 234, writable: true, enumerable: false, configurable: true})
Object.defineProperty(Object.prototype, "__proto__", {set: function () { return "custom setter" }, enumerable: false, configurable: true})
PASSED
-*** Running test #5 (4): Change Object.prototype.__proto__ getter or setter
+*** Running test #4 (3): Change Object.prototype.__proto__ getter or setter
PASSED
-Summary of tests: total executed: 5; passed: 5; failed: 0
+Summary of tests: total executed: 4; passed: 4; failed: 0
diff --git a/test/es6/proto_disable.js b/test/es6/proto_disable.js
index 4f042b33df1..d2c67b7fafb 100644
--- a/test/es6/proto_disable.js
+++ b/test/es6/proto_disable.js
@@ -9,30 +9,6 @@ if (this.WScript && this.WScript.LoadScriptFile) {
}
var tests = [
- {
- name: "Change Object.prototype.__proto__ value",
- body: function () {
- // This considered no-op: Object.prototype.__proto__ = null
- verify_disable("Object.prototype.__proto__ = null", KEEP_ENABLED);
-
- // Set to these primitives will throw and make no change
- [undefined, 0, 123, -12.3, NaN, Infinity, true, false, "str"].forEach(
- function (newValue) {
- Object.prototype.__proto__ = newValue;
- verify__proto__enabled();
- });
-
- // Set to any objects will throw and make no change
- [new Boolean(), new Number(12), new String("string object"), {}, [], Object.prototype, Math.sin, assert.throws].forEach(
- function (newValue) {
- assert.throws__proto__Cyclic(function () {
- Object.prototype.__proto__ = newValue;
- });
- verify__proto__enabled();
- });
- }
- },
-
{
name: "seal/freeze Object.prototype",
body: function () {
diff --git a/test/es7/immutable-prototype.js b/test/es7/immutable-prototype.js
new file mode 100644
index 00000000000..05f9fbed26a
--- /dev/null
+++ b/test/es7/immutable-prototype.js
@@ -0,0 +1,45 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+// ES7 Object Prototype object has an immutable [[Prototype]] internal slot
+// See: 19.1.3 Properties of the Object Prototype Object
+// See: 9.4.7 Immutable Prototype Exotic Objects
+
+WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
+
+var tests = [
+ {
+ name: "Not okay to set Object.prototype.[[Prototype]] using __proto__",
+ body: function () {
+ var objectPrototypeObject = Object.getPrototypeOf(Object.prototype)
+ var b = Object.create(null)
+
+ assert.throws(function () { Object.prototype.__proto__ = b },
+ TypeError,
+ "It should not be okay to set Object.prototype.[[Prototype]] using __proto__",
+ "Can't set the prototype of this object.")
+
+ assert.areEqual(objectPrototypeObject, Object.prototype.__proto__, "Object.prototype.__proto__ is unchanged")
+ assert.areEqual(objectPrototypeObject, Object.getPrototypeOf(Object.prototype), "Object.getPrototypeOf(Object.prototype) is unchanged")
+ }
+ },
+ {
+ name: "Not okay to set Object.prototype.[[Prototype]] using Object.setPrototypeOf",
+ body: function () {
+ var objectPrototypeObject = Object.getPrototypeOf(Object.prototype)
+ var b = Object.create(null)
+
+ assert.throws(function () { Object.setPrototypeOf(Object.prototype, b) },
+ TypeError,
+ "It should not be okay to set Object.prototype.[[Prototype]] using Object.setPrototypeOf",
+ "Can't set the prototype of this object.")
+
+ assert.areEqual(objectPrototypeObject, Object.prototype.__proto__, "Object.prototype.__proto__ is unchanged")
+ assert.areEqual(objectPrototypeObject, Object.getPrototypeOf(Object.prototype), "Object.getPrototypeOf(Object.prototype) is unchanged")
+ }
+ },
+];
+
+testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });
diff --git a/test/es7/rlexe.xml b/test/es7/rlexe.xml
index 9830430fee4..1826d5a8846 100644
--- a/test/es7/rlexe.xml
+++ b/test/es7/rlexe.xml
@@ -59,4 +59,10 @@
BugFix
+
+
+ immutable-prototype.js
+ -args summary -endargs
+
+