1 // Any copyright is dedicated to the Public Domain.
2 // http://creativecommons.org/licenses/publicdomain/
4 assertEq("defineProperty" in Object, true);
5 assertEq(Object.defineProperty.length, 3);
7 if (!Object.prototype.toSource)
9 Object.defineProperty(Object.prototype, "toSource",
11 value: function toSource()
13 if (this instanceof RegExp)
15 var v = "new RegExp(" + uneval(this.source);
16 var f = (this.multiline ? "m" : "") +
17 (this.global ? "g" : "") +
18 (this.ignoreCase ? "i" : "");
19 return v + (f ? ", '" + f + "'" : "") + ")";
21 return JSON.stringify(this);
28 if (!("uneval" in this))
30 Object.defineProperty(this, "uneval",
32 value: function uneval(v)
36 if (typeof v === "object")
38 if (typeof v === "string")
40 v = JSON.stringify({v:v});
41 return v.substring(5, v.length - 1);
51 // reimplemented for the benefit of engines which don't have this helper
52 function assertEq(v1, v2, m)
54 if (!SameValue(v1, v2))
56 throw "assertion failed: " +
57 "got " + uneval(v1) + ", expected " + uneval(v2) +
62 function SameValue(v1, v2)
64 if (v1 === 0 && v2 === 0)
65 return 1 / v1 === 1 / v2;
66 if (v1 !== v1 && v2 !== v2)
71 function PropertyDescriptor(pd)
76 PropertyDescriptor.prototype.update = function update(pd)
82 if ("configurable" in pd)
83 this.configurable = pd.configurable;
85 this.writable = pd.writable;
86 if ("enumerable" in pd)
87 this.enumerable = pd.enumerable;
89 this.value = pd.value;
91 PropertyDescriptor.prototype.convertToDataDescriptor = function convertToDataDescriptor()
95 this.writable = false;
96 this.value = undefined;
98 PropertyDescriptor.prototype.convertToAccessorDescriptor = function convertToAccessorDescriptor()
100 delete this.writable;
102 this.get = undefined;
103 this.set = undefined;
106 function compareDescriptors(d1, d2)
108 if (d1 === undefined)
110 assertEq(d2, undefined, "non-descriptors");
113 if (d2 === undefined)
115 assertEq(true, false, "descriptor-equality mismatch: " + uneval(d1) + ", " + uneval(d2));
119 var props = ["value", "get", "set", "enumerable", "configurable", "writable"];
120 for (var i = 0, sz = props.length; i < sz; i++)
123 assertEq(p in d1, p in d2, p + " different in d1/d2");
125 assertEq(d1[p], d2[p], p);
129 function examine(desc, field, allowDefault)
133 assertEq(allowDefault, true, "reimplementation error");
145 assertEq(true, false, "bad field name: " + field);
149 function IsAccessorDescriptor(desc)
153 if (!("get" in desc) && !("set" in desc))
158 function IsDataDescriptor(desc)
162 if (!("value" in desc) && !("writable" in desc))
167 function IsGenericDescriptor(desc)
171 if (!IsAccessorDescriptor(desc) && !IsDataDescriptor(desc))
178 function CustomObject()
180 this.properties = {};
181 this.extensible = true;
183 CustomObject.prototype =
185 _reject: function _reject(throwing, msg)
188 throw new TypeError(msg + "; rejected!");
191 defineOwnProperty: function defineOwnProperty(propname, desc, throwing)
193 assertEq(typeof propname, "string", "non-string propname");
196 var current = this.properties[propname];
199 var extensible = this.extensible;
202 if (current === undefined && !extensible)
203 return this._reject(throwing, "object not extensible");
206 if (current === undefined && extensible)
210 if (IsGenericDescriptor(desc) || IsDataDescriptor(desc))
212 p = new PropertyDescriptor();
213 p.value = examine(desc, "value", true);
214 p.writable = examine(desc, "writable", true);
215 p.enumerable = examine(desc, "enumerable", true);
216 p.configurable = examine(desc, "configurable", true);
221 p = new PropertyDescriptor();
222 p.get = examine(desc, "get", true);
223 p.set = examine(desc, "set", true);
224 p.enumerable = examine(desc, "enumerable", true);
225 p.configurable = examine(desc, "configurable", true);
228 this.properties[propname] = p;
235 if (!("value" in desc) && !("get" in desc) && !("set" in desc) &&
236 !("writable" in desc) && !("enumerable" in desc) &&
237 !("configurable" in desc))
247 if (!("value" in current) || !SameValue(desc.value, current.value))
252 if (!("get" in current) || !SameValue(desc.get, current.get))
257 if (!("set" in current) || !SameValue(desc.set, current.set))
260 if ("writable" in desc)
262 if (!("writable" in current) ||
263 !SameValue(desc.writable, current.writable))
268 if ("enumerable" in desc)
270 if (!("enumerable" in current) ||
271 !SameValue(desc.enumerable, current.enumerable))
276 if ("configurable" in desc)
278 if (!("configurable" in current) ||
279 !SameValue(desc.configurable, current.configurable))
285 // all fields in desc also in current, with the same values
291 if (!examine(current, "configurable"))
293 if ("configurable" in desc && examine(desc, "configurable"))
294 return this._reject(throwing, "can't make configurable again");
295 if ("enumerable" in desc &&
296 examine(current, "enumerable") !== examine(desc, "enumerable"))
298 return this._reject(throwing, "can't change enumerability");
303 if (IsGenericDescriptor(desc))
308 else if (IsDataDescriptor(current) !== IsDataDescriptor(desc))
311 if (!examine(current, "configurable"))
312 return this._reject(throwing, "can't change unconfigurable descriptor's type");
314 if (IsDataDescriptor(current))
315 current.convertToAccessorDescriptor();
318 current.convertToDataDescriptor();
321 else if (IsDataDescriptor(current) && IsDataDescriptor(desc))
324 if (!examine(current, "configurable"))
327 if (!examine(current, "writable") &&
328 "writable" in desc && examine(desc, "writable"))
330 return this._reject(throwing, "can't make data property writable again");
333 if (!examine(current, "writable"))
335 if ("value" in desc &&
336 !SameValue(examine(desc, "value"), examine(current, "value")))
338 return this._reject(throwing, "can't change value if not writable");
345 assertEq(examine(current, "configurable"), true,
346 "spec bug step 10(b)");
352 assertEq(IsAccessorDescriptor(current) && IsAccessorDescriptor(desc),
357 if (!examine(current, "configurable"))
361 !SameValue(examine(desc, "set"), examine(current, "set")))
363 return this._reject(throwing, "can't change setter if not configurable");
367 !SameValue(examine(desc, "get"), examine(current, "get")))
369 return this._reject(throwing, "can't change getter if not configurable");
375 current.update(desc);
382 function IsCallable(v)
384 return typeof v === "undefined" || typeof v === "function";
389 newObject: function newObject()
393 defineProperty: function defineProperty(obj, propname, propdesc)
395 Object.defineProperty(obj, propname, propdesc);
397 getDescriptor: function getDescriptor(obj, propname)
399 return Object.getOwnPropertyDescriptor(obj, propname);
405 newObject: function newObject()
407 return new CustomObject();
409 defineProperty: function defineProperty(obj, propname, propdesc)
411 assertEq(obj instanceof CustomObject, true, "obj not instanceof CustomObject");
412 if ("get" in propdesc || "set" in propdesc)
414 if ("value" in propdesc || "writable" in propdesc)
415 throw new TypeError("get/set and value/writable");
416 if (!IsCallable(propdesc.get))
417 throw new TypeError("get defined, uncallable");
418 if (!IsCallable(propdesc.set))
419 throw new TypeError("set defined, uncallable");
421 return obj.defineOwnProperty(propname, propdesc, true);
423 getDescriptor: function getDescriptor(obj, propname)
425 if (!(propname in obj.properties))
428 return new PropertyDescriptor(obj.properties[propname]);
432 var JSVAL_INT_MAX = Math.pow(2, 30) - 1;
433 var JSVAL_INT_MIN = -Math.pow(2, 30);
436 function isValidDescriptor(propdesc)
438 if ("get" in propdesc || "set" in propdesc)
440 if ("value" in propdesc || "writable" in propdesc)
443 // We permit null here simply because this test's author believes the
444 // implementation may sometime be susceptible to making mistakes in this
445 // regard and would prefer to be cautious.
446 if (propdesc.get !== null && propdesc.get !== undefined && !IsCallable(propdesc.get))
448 if (propdesc.set !== null && propdesc.set !== undefined && !IsCallable(propdesc.set))
458 [-Infinity, JSVAL_INT_MIN, -0, +0, 1.5, JSVAL_INT_MAX, Infinity,
459 NaN, "foo", "bar", null, undefined, true, false, {}, /a/, OMIT];
461 [undefined, function get1() { return 1; }, function get2() { return 2; },
464 [undefined, function set1() { return 1; }, function set2() { return 2; },
466 var ENUMERABLES = [true, false, OMIT];
467 var CONFIGURABLES = [true, false, OMIT];
468 var WRITABLES = [true, false, OMIT];
470 function mapTestDescriptors(filter)
475 function put(field, value)
481 VALUES.forEach(function(value)
483 GETS.forEach(function(get)
485 SETS.forEach(function(set)
487 ENUMERABLES.forEach(function(enumerable)
489 CONFIGURABLES.forEach(function(configurable)
491 WRITABLES.forEach(function(writable)
497 put("enumerable", enumerable);
498 put("configurable", configurable);
499 put("writable", writable);
512 var ALL_DESCRIPTORS = mapTestDescriptors(function(d) { return true; });
513 var VALID_DESCRIPTORS = mapTestDescriptors(isValidDescriptor);
515 var SKIP_FULL_FUNCTION_LENGTH_TESTS = true;
517 function TestRunner()
521 TestRunner.prototype =
525 runFunctionLengthTests: function runFunctionLengthTests()
528 function functionLengthTests()
530 if (SKIP_FULL_FUNCTION_LENGTH_TESTS)
532 print("Skipping full tests for redefining Function.length for now " +
533 "because we don't support redefinition of properties with " +
534 "native getter or setter...");
535 self._simpleFunctionLengthTests();
539 self._simpleFunctionLengthTests();
540 self._fullFunctionLengthTests(function() { }, 0);
541 self._fullFunctionLengthTests(function(one) { }, 1);
542 self._fullFunctionLengthTests(function(one, two) { }, 2);
546 this._runTestSet(functionLengthTests, "Function length tests completed!");
549 runNotPresentTests: function runNotPresentTests()
552 function notPresentTests()
554 print("Running not-present tests now...");
556 for (var i = 0, sz = ALL_DESCRIPTORS.length; i < sz; i++)
557 self._runSingleNotPresentTest(ALL_DESCRIPTORS[i]);
560 this._runTestSet(notPresentTests, "Not-present length tests completed!");
563 runPropertyPresentTestsFraction:
564 function runPropertyPresentTestsFraction(part, parts)
567 function propertyPresentTests()
569 print("Running already-present tests now...");
571 var total = VALID_DESCRIPTORS.length;
572 var start = Math.floor((part - 1) / parts * total);
573 var end = Math.floor(part / parts * total);
575 for (var i = start; i < end; i++)
577 var old = VALID_DESCRIPTORS[i];
578 print("Starting test with old descriptor " + old.toSource() + "...");
580 for (var j = 0, sz2 = VALID_DESCRIPTORS.length; j < sz2; j++)
581 self._runSinglePropertyPresentTest(old, VALID_DESCRIPTORS[j], []);
585 this._runTestSet(propertyPresentTests,
586 "Property-present fraction " + part + " of " + parts +
590 runNonTerminalPropertyPresentTestsFraction:
591 function runNonTerminalPropertyPresentTestsFraction(part, parts)
596 * A plain old property to define on the object before redefining the
597 * originally-added property, to test redefinition of a property that's
598 * not also lastProperty. NB: we could loop over every possible
599 * descriptor here if we wanted, even try adding more than one, but we'd
600 * hit cubic complexity and worse, and SpiderMonkey only distinguishes by
601 * the mere presence of the middle property, not its precise details.
607 { value: 17, writable: true, configurable: true, enumerable: true }
610 function nonTerminalPropertyPresentTests()
612 print("Running non-terminal already-present tests now...");
614 var total = VALID_DESCRIPTORS.length;
615 var start = Math.floor((part - 1) / parts * total);
616 var end = Math.floor(part / parts * total);
618 for (var i = start; i < end; i++)
620 var old = VALID_DESCRIPTORS[i];
621 print("Starting test with old descriptor " + old.toSource() + "...");
623 for (var j = 0, sz2 = VALID_DESCRIPTORS.length; j < sz2; j++)
625 self._runSinglePropertyPresentTest(old, VALID_DESCRIPTORS[j],
631 this._runTestSet(nonTerminalPropertyPresentTests,
632 "Non-terminal property-present fraction " +
633 part + " of " + parts + " completed!");
636 runDictionaryPropertyPresentTestsFraction:
637 function runDictionaryPropertyPresentTestsFraction(part, parts)
642 * Add and readd properties such that the scope for the object is in
650 { value: 17, writable: true, configurable: true, enumerable: true }
655 { value: 17, writable: true, configurable: true, enumerable: true }
660 { get: function g() { }, set: function s(v){}, configurable: false,
665 function dictionaryPropertyPresentTests()
667 print("Running dictionary already-present tests now...");
669 var total = VALID_DESCRIPTORS.length;
670 var start = Math.floor((part - 1) / parts * total);
671 var end = Math.floor(part / parts * total);
673 for (var i = start; i < end; i++)
675 var old = VALID_DESCRIPTORS[i];
676 print("Starting test with old descriptor " + old.toSource() + "...");
678 for (var j = 0, sz2 = VALID_DESCRIPTORS.length; j < sz2; j++)
680 self._runSinglePropertyPresentTest(old, VALID_DESCRIPTORS[j],
686 this._runTestSet(dictionaryPropertyPresentTests,
687 "Dictionary property-present fraction " +
688 part + " of " + parts + " completed!");
694 runPropertyPresentTests: function runPropertyPresentTests()
696 print("Running already-present tests now...");
698 for (var i = 0, sz = VALID_DESCRIPTORS.length; i < sz; i++)
700 var old = VALID_DESCRIPTORS[i];
701 print("Starting test with old descriptor " + old.toSource() + "...");
703 for (var j = 0, sz2 = VALID_DESCRIPTORS.length; j < sz2; j++)
704 this._runSinglePropertyPresentTest(old, VALID_DESCRIPTORS[j], []);
707 _runTestSet: function _runTestSet(fun, completeMessage)
713 print(completeMessage);
717 print("ERROR, EXITING (line " + (e.lineNumber || -1) + "): " + e);
722 this._reportAllErrors();
725 _reportAllErrors: function _reportAllErrors()
727 var errorCount = this._logLines.length;
728 print("Full accumulated number of errors: " + errorCount);
730 throw errorCount + " errors detected, FAIL";
732 _simpleFunctionLengthTests: function _simpleFunctionLengthTests(fun)
734 print("Running simple Function.length tests now..");
736 function expectThrowTypeError(o, p, desc)
738 var err = "<none>", passed = false;
741 Object.defineProperty(o, p, desc);
746 passed = e instanceof TypeError;
748 assertEq(passed, true, fun + " didn't throw TypeError when called: " + err);
751 expectThrowTypeError(function a() { }, "length", { value: 1 });
752 expectThrowTypeError(function a() { }, "length", { enumerable: true });
753 expectThrowTypeError(function a() { }, "length", { configurable: true });
754 expectThrowTypeError(function a() { }, "length", { writable: true });
756 _fullFunctionLengthTests: function _fullFunctionLengthTests(fun)
758 var len = fun.length;
759 print("Running Function.length (" + len + ") tests now...");
762 var gen = new DescriptorState();
763 while ((desc = gen.nextDescriptor()))
764 this._runSingleFunctionLengthTest(fun, len, desc);
766 _log: function _log(v)
770 this._logLines.push(m);
772 _runSingleNotPresentTest: function _runSingleNotPresentTest(desc)
774 var nativeObj = NativeTest.newObject();
775 var reimplObj = ReimplTest.newObject();
779 NativeTest.defineProperty(nativeObj, "foo", desc);
785 ReimplTest.defineProperty(reimplObj, "foo", desc);
789 if (e.constructor !== e2.constructor)
791 this._log("Difference when comparing native/reimplementation " +
792 "behavior for new descriptor " + desc.toSource() +
793 ", native threw " + e + ", reimpl threw " + e2);
797 this._log("Difference when comparing native/reimplementation " +
798 "behavior for new descriptor " + desc.toSource() +
805 ReimplTest.defineProperty(reimplObj, "foo", desc);
809 this._log("Reimpl threw defining new descriptor " + desc.toSource() +
814 var nativeDesc = NativeTest.getDescriptor(nativeObj, "foo");
815 var reimplDesc = ReimplTest.getDescriptor(reimplObj, "foo");
818 compareDescriptors(nativeDesc, reimplDesc);
822 this._log("Difference comparing returned descriptors for new " +
823 "property defined with descriptor " + desc.toSource() +
828 _runSinglePropertyPresentTest:
829 function _runSinglePropertyPresentTest(old, add, middleDefines)
831 var nativeObj = NativeTest.newObject();
832 var reimplObj = ReimplTest.newObject();
836 NativeTest.defineProperty(nativeObj, "foo", old);
840 if (!SameValue(NativeTest.getDescriptor(nativeObj, "foo"), undefined))
842 this._log("defining bad property descriptor: " + old.toSource());
848 ReimplTest.defineProperty(reimplObj, "foo", old);
852 if (!SameValue(ReimplTest.getDescriptor(reimplObj, "foo"),
855 this._log("defining bad property descriptor: " + old.toSource() +
856 "; reimplObj: " + uneval(reimplObj));
859 if (e.constructor !== e2.constructor)
861 this._log("Different errors defining bad property descriptor: " +
862 old.toSource() + "; native threw " + e + ", reimpl " +
869 this._log("Difference defining a property with descriptor " +
870 old.toSource() + ", error " + e);
876 ReimplTest.defineProperty(reimplObj, "foo", old);
880 this._log("Difference when comparing native/reimplementation " +
881 "behavior when adding descriptor " + add.toSource() +
886 // Now add (or even readd) however many properties were specified between
887 // the original property to add and the new one, to test redefining
888 // non-last-properties and properties in scopes in dictionary mode.
889 for (var i = 0, sz = middleDefines.length; i < sz; i++)
891 var middle = middleDefines[i];
892 var prop = middle.property;
893 var desc = middle.descriptor;
897 NativeTest.defineProperty(nativeObj, prop, desc);
898 ReimplTest.defineProperty(reimplObj, prop, desc);
902 this._log("failure defining middle descriptor: " + desc.toSource() +
908 var nativeDesc = NativeTest.getDescriptor(nativeObj, prop);
909 var reimplDesc = ReimplTest.getDescriptor(reimplObj, prop);
911 compareDescriptors(nativeDesc, reimplDesc);
912 compareDescriptors(nativeDesc, desc);
917 NativeTest.defineProperty(nativeObj, "foo", add);
923 ReimplTest.defineProperty(reimplObj, "foo", add);
927 if (e.constructor !== e2.constructor)
929 this._log("Difference when comparing native/reimplementation " +
930 "behavior for descriptor " + add.toSource() +
931 " overwriting descriptor " + old.toSource() + "; " +
932 "native threw " + e + ", reimpl threw " + e2);
936 this._log("Difference when comparing native/reimplementation " +
937 "behavior for added descriptor " + add.toSource() + ", " +
938 "initial was " + old.toSource() + "; error: " + e);
944 ReimplTest.defineProperty(reimplObj, "foo", add);
948 this._log("Difference when comparing native/reimplementation " +
949 "behavior for readded descriptor " + add.toSource() + ", " +
950 "initial was " + old.toSource() + "; native readd didn't " +
951 "throw, reimpl add did, error: " + e);
955 var nativeDesc = NativeTest.getDescriptor(nativeObj, "foo");
956 var reimplDesc = ReimplTest.getDescriptor(reimplObj, "foo");
959 compareDescriptors(nativeDesc, reimplDesc);
963 this._log("Difference comparing returned descriptors for readded " +
964 "property defined with descriptor " + add.toSource() + "; " +
965 "initial was " + old.toSource() + "; error: " + e);
969 _runSingleFunctionLengthTest: function _runSingleFunctionLengthTest(fun, len, desc)
972 var reimplObj = ReimplTest.newObject();
973 ReimplTest.defineProperty(reimplObj, "length",
983 NativeTest.defineProperty(nativeObj, "length", desc);
989 ReimplTest.defineProperty(reimplObj, "length", desc);
993 if (e.constructor !== e2.constructor)
995 this._log("Difference when comparing native/reimplementation " +
996 "behavior defining fun.length with " + desc.toSource() +
997 "; native threw " + e + ", reimpl threw " + e2);
1001 this._log("Difference when comparing Function.length native/reimpl " +
1002 "behavior for descriptor " + desc.toSource() +
1003 ", native impl threw error " + e);
1009 ReimplTest.defineProperty(reimplObj, "length", desc);
1013 this._log("Difference defining new Function.length descriptor: impl " +
1014 "succeeded, reimpl threw for descriptor " +
1015 desc.toSource() + ", error: " + e);
1019 var nativeDesc = NativeTest.getDescriptor(nativeObj, "length");
1020 var reimplDesc = ReimplTest.getDescriptor(reimplObj, "length");
1023 compareDescriptors(nativeDesc, reimplDesc);
1027 this._log("Difference comparing returned descriptors for " +
1028 "Function.length with descriptor " + desc.toSource() +
1035 function runDictionaryPropertyPresentTestsFraction(PART, PARTS)
1038 '15.2.3.6-dictionary-redefinition-' + PART + '-of-' + PARTS + '.js';
1039 var BUGNUMBER = 560566;
1041 'ES5 Object.defineProperty(O, P, Attributes): dictionary redefinition ' +
1042 PART + ' of ' + PARTS;
1044 print(BUGNUMBER + ": " + summary);
1048 new TestRunner().runDictionaryPropertyPresentTestsFraction(PART, PARTS);
1052 throw "Error thrown during testing: " + e +
1053 " at line " + e.lineNumber + "\n" +
1055 ? "Stack: " + e.stack.split("\n").slice(2).join("\n") + "\n"
1059 if (typeof reportCompare === "function")
1060 reportCompare(true, true);
1062 print("Tests complete!");
1065 function runNonTerminalPropertyPresentTestsFraction(PART, PARTS)
1067 var BUGNUMBER = 560566;
1069 'ES5 Object.defineProperty(O, P, Attributes): middle redefinition ' +
1070 PART + ' of ' + PARTS;
1072 print(BUGNUMBER + ": " + summary);
1081 new TestRunner().runNonTerminalPropertyPresentTestsFraction(PART, PARTS);
1085 throw "Error thrown during testing: " + e +
1086 " at line " + e.lineNumber + "\n" +
1088 ? "Stack: " + e.stack.split("\n").slice(2).join("\n") + "\n"
1092 /******************************************************************************/
1094 if (typeof reportCompare === "function")
1095 reportCompare(true, true);
1097 print("Tests complete!");