Closed Bug 492844 Opened 16 years ago Closed 14 years ago

ES5: Implement Object.freeze, Object.isFrozen

Categories

(Core :: JavaScript Engine, defect)

defect
Not set
normal

Tracking

()

RESOLVED FIXED
Tracking Status
blocking2.0 --- beta7+

People

(Reporter: jimb, Assigned: jimb)

References

Details

(Keywords: dev-doc-complete, Whiteboard: [fixed-in-tracemonkey])

Attachments

(1 file, 1 obsolete file)

+++ This bug was initially created as a clone of Bug #492840 +++ Object.freeze and Object.isFrozen are specified by 15.2.3.9 and 15.2.3.12 in the ES5 draft: http://wiki.ecmascript.org/doku.php?id=es3.1:es3.1_proposal_working_draft
Assignee: general → jwalden+bmo
Blocks: es5
blocking2.0: --- → ?
blocking2.0: ? → beta1+
blocking2.0: beta1+ → beta2+
blocking2.0: beta2+ → betaN+
This bug depends on Brendan's scope-removal work in bug 558451.
Depends on: 558451
blocking2.0: betaN+ → beta5+
blocking2.0: beta5+ → beta6+
Blocks: 595081
Assignee: jwalden+bmo → jim
Attachment #476044 - Flags: review?
Attachment #476044 - Flags: review? → review?(jorendorff)
Comment on attachment 476044 [details] [diff] [review] Implement Object.freeze, Object.isFrozen The logic is wrong here, see ES5 15.2.3.9 Step 2, noted in bug 492849 comment 38. /be
Attachment #476044 - Flags: review?(jorendorff) → review-
Comment on attachment 476044 [details] [diff] [review] Implement Object.freeze, Object.isFrozen I'm clearly too tired to review, or do much of anything else. Sorry about that. To make up for it, some nits and an r+. >+ /* If no change is necessary, avoid the setAttributes call. */ > if ((attrs & JSPROP_PERMANENT) && >- (!(attrs & (JSPROP_GETTER | JSPROP_SETTER)) || (attrs & JSPROP_READONLY))) { >+ ((attrs & JSPROP_READONLY) || (attrs & (JSPROP_GETTER | JSPROP_SETTER)))) > continue; >- } Keep the parens per house style (multiline condition is enough to trigger them). Combine all the tests: (attrs & (JSPROP_READONLY | JSPROP_GETTER | JSPROP_SETTER)). /be
Attachment #476044 - Flags: review?(jorendorff)
Attachment #476044 - Flags: review-
Attachment #476044 - Flags: review+
Revised per comments.
Attachment #476044 - Attachment is obsolete: true
Attachment #476839 - Flags: review?(jorendorff)
Attachment #476044 - Flags: review?(jorendorff)
Comment on attachment 476839 [details] [diff] [review] Implement Object.freeze, Object.isFrozen Shouldn't deepEqual use === instead of ==? Even === will equate -0 and 0, and not equate NaN with itself. See http://wiki.ecmascript.org/doku.php?id=strawman:egal where the fix is coded in JS: Object.eq = function(x, y) { if (x === y) { // 0 === -0, but they are not identical return x !== 0 || 1/x === 1/y; } // NaN !== NaN, but they are identical. // NaNs are the only non-reflexive value, i.e., if x !== x, // then x is a NaN. // isNaN is broken: it converts its argument to number, so // isNaN("foo") => true return x !== x && y !== y; } /be
FWIW, I agree deepEqual should handle -0 and NaN specially so that deepEqual(NaN, NaN) ===> true deepEqual(0, -0) ===> false and I think it should check that Object.prototype.toString.call produces the same result for both operands (that is, that they have the same [[Class]]). (Admittedly you eventually should stop and go work on other important stuff...)
Comment on attachment 476839 [details] [diff] [review] Implement Object.freeze, Object.isFrozen In jsobj.cpp, in GetFirstArgumentAsObject: > if (v.isPrimitive()) { Uber-micro-nit: it would be nice for this to say instead: if (!v.isObject()) { Exactly the same meaning, but this goes nicer with the .toObject() call that follows. In jsobj.cpp: >+static JSBool >+obj_freeze(JSContext *cx, uintN argc, Value *vp) >+{ >+ JSObject *obj; >+ if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.preventExtensions", &obj)) The string there should say "Object.freeze". I could stand to see a few more tests: - calling freeze() and isFrozen() without arguments or with non-object arguments should throw - freezing one object shouldn't freeze a separate object with the same (empty or non-empty) shape, of course - after freezing an Array, .length and its elements, should appear on it, and they should all be read-only -- you shouldn't be able to set .length afterwards. (Actually I may not correctly understand 15.4.5.1 on this point, but whatever it says, we should do...) - likewise, freezing a Function should cause .arity and .name properties to appear. - and freezing an arguments object should do whatever ES5 says, even in a non-strict mode function (shudder) and even after the corresponding function call has returned - freezing an E4X object and then poking at it shouldn't crash - freezing the global object, so help us, should do whatever ES5 says - freezing the global object on trace should deep-bail: var arr = [{}, {}, {}, {}, this]; for (var i = 0; i < arr.length; i++) // once we go on trace, this.i isn't updated Object.freeze(arr[i]); // needs to freeze this.i == 4 assertEq(this.i, arr.indexOf(this)); - freezing shouldn't change [[Class]] (as observable by Object.prototype.toString.call(obj)) - freezing an object while it's the target of an active with-block, and then trying to write to one of its properties, should fail as expected - freezing an object that has methods (in the sense of js::Shape::METHOD), and then getting a method out, should work as expected (basically I think freezing needs to trip the method read barrier and force all the methods to be really truly created) - freezing a proxy should do something (throw, if that's what you've got implemented at this point) - the ES5 spec says that an inextensible object's [[Prototype]] may not change; we should test that. (I don't see a test for it in bug 492849; sorry if I'm missing something!) How is freezing supposed to interact with resolve/newResolve hooks? How does it work now; is that likely to break existing JSAPI code (of which we have a bunch)? r=me with the nits picked and as many tests as you think wise.
Attachment #476839 - Flags: review?(jorendorff) → review+
(In reply to comment #9) > var arr = [{}, {}, {}, {}, this]; > for (var i = 0; i < arr.length; i++) // once we go on trace, this.i isn't > updated > Object.freeze(arr[i]); // needs to freeze this.i == 4 > assertEq(this.i, arr.indexOf(this)); Oops! The loop will never terminate. But you get the idea I hope. :)
I meant to mention: in one of the tests, a function getPropertyOf is defined but never called.
> How is freezing supposed to interact with resolve/newResolve hooks? How does it > work now; is that likely to break existing JSAPI code (of which we have a bunch)? Resolve is just a way of defining (as in JS_DefineProperty) properties on demand, lazily. So API tests that exercise JS_Define* and JS_Set* from a resolve hook could be enough, if we don't already have tests using ES5 Object.defineProperty etc. on frozen objects. I recall some of those, though. Point is, resolve can't do anything not already doable via the JS API if not via ES5's in-language meta-programming API. /be
(In reply to comment #7) > Shouldn't deepEqual use === instead of ==? Even === will equate -0 and 0, and > not equate NaN with itself. See Fixed.
(In reply to comment #9) > In jsobj.cpp, in GetFirstArgumentAsObject: > > if (v.isPrimitive()) { > > Uber-micro-nit: it would be nice for this to say instead: > > if (!v.isObject()) { > > Exactly the same meaning, but this goes nicer with the .toObject() call that > follows. Fixed. > In jsobj.cpp: > >+static JSBool > >+obj_freeze(JSContext *cx, uintN argc, Value *vp) > >+{ > >+ JSObject *obj; > >+ if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.preventExtensions", &obj)) > > The string there should say "Object.freeze". Fixed, thanks. > I could stand to see a few more tests: I agree, but given the time pressure, I've filed this as bug 598390.
Whiteboard: [fixed-in-tracemonkey]
Status: NEW → RESOLVED
Closed: 14 years ago
Resolution: --- → FIXED
(In reply to comment #17) > https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/freeze > https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/isFrozen My understanding is that changing properties on a frozen object will only throw an exception in strict mode but that isn't mentioned in the docs. Am I mistaken?
(In reply to comment #18) > My understanding is that changing properties on a frozen object will only throw > an exception in strict mode but that isn't mentioned in the docs. Am I > mistaken? I can't find anything about that, even not in ECMA-262. Object.freeze loops over all properties of the object and sets writable and configurable to false and I can't also find any documentation that setting, deleting such properties only throw an exception in strict mode.
(In reply to comment #19) > (In reply to comment #18) > > My understanding is that changing properties on a frozen object will only throw > > an exception in strict mode but that isn't mentioned in the docs. Am I > > mistaken? > > I can't find anything about that, even not in ECMA-262. Object.freeze loops > over all properties of the object and sets writable and configurable to false > and I can't also find any documentation that setting, deleting such properties > only throw an exception in strict mode. 11.4.1 The delete Operator The production UnaryExpression : delete UnaryExpression is evaluated as follows: 1. Let ref be the result of evaluating UnaryExpression. 2. If Type(ref) is not Reference, return true. 3. If IsUnresolvableReference(ref) then, a. If IsStrictReference(ref) is true, throw a SyntaxError exception. b. Else, return true. 4. If IsPropertyReference(ref) is true, then a. Return the result of calling the [[Delete]] internal method on ToObject(GetBase(ref) providing GetReferencedName(ref) and IsStrictReference(ref) as the arguments. 5. Else, ref is a Reference to an Environment Record binding, so a. If IsStrictReference(ref) is true, throw a SyntaxError exception. b. Let bindings be GetBase(ref). c. Return the result of calling the DeleteBinding concrete method of bindings, providing GetReferencedName(ref) as the argument. NOTE When a delete operator occurs within strict mode code, a SyntaxError exception is thrown if its UnaryExpression is a direct reference to a variable, function argument, or function name. In addition, if a delete operator occurs within strict mode code and the property to be deleted has the attribute { [[Configurable]]: false }, a TypeError exception is thrown. For assignment, see 8.7.2 PutValue (V, W), IsStrictReference, etc. It's all there, as Mossop said. /be
Added it to the documentation, also at Object.seal, Object.preventExtensions and here: https://developer.mozilla.org/en/JavaScript/Strict_mode
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: