Created
February 27, 2014 19:13
-
-
Save nzakas/9257017 to your computer and use it in GitHub Desktop.
Is this a Node.js bug?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
value1: {"stack":"MyStack","type":"MyType"} true | |
value2: {"stack":"MyStack","message":"MyError"} true | |
value3: {"stack":"MyStack","message":"MyError"} true | |
value4: {"stack":"MyStack","type":"MyType"} true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
value1: {} false | |
value2: {"stack":"MyStack","message":"MyError"} true | |
value3: {"message":"MyError"} false | |
value4: {} false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Help me figure out if this is a bug in Node.js, V8, or someplace else. | |
* | |
* Situation: There's an object that inherits from Error via prototype chaining. | |
* We then overwrite stack and type (effectively shadowing the prototype properties) | |
* because they aren't enumerable by default and we want them to be. Doing this | |
* should create an enumerable stack and enumerable type on the new object. | |
* | |
* In Node.js, however, this is not the case. The properties seem to maintain their | |
* enumerability, and in fact, the properties don't get added to the new object. | |
* | |
* In Chrome, the properties are added as enumerable to the new object. | |
* | |
* I've included output from both Node.js and Chrome in this gist for reference. | |
*/ | |
var err = new Error('MyError'); | |
err.stack = 'MyStack'; | |
err.type = 'MyType'; | |
var value = {}; | |
for (var i in err) { | |
value[i] = err[i]; | |
} | |
console.log('value1:', JSON.stringify(value), value.hasOwnProperty('stack')); | |
value = {}; | |
value.stack = err.stack; | |
value.message = err.message; | |
console.log('value2:', JSON.stringify(value), value.hasOwnProperty('stack')); | |
value = Object.create(err); | |
value.stack = err.stack; | |
value.message = err.message; | |
console.log('value3:', JSON.stringify(value), value.hasOwnProperty('stack')); | |
value = Object.create(err); | |
for (var i in err) { | |
value[i] = err[i]; | |
} | |
console.log('value4:', JSON.stringify(value), value.hasOwnProperty('stack')); |
Ah so you're saying the problem is in the implementation of Error
rather than the implementation of [[Put]]
. Error
does seem like a special case.
var obj1 = {
_name: ""
};
Object.defineProperty(obj1, "name", {
get: function() {
return this._name;
},
set: function(value) {
this._name = value;
},
configurable: true
});
var obj2 = Object.create(obj1);
obj2.name = "bar";
console.log(obj2.hasOwnProperty("name")); // false everywhere
The above works as defined in the spec. But if you try this with Error
:
var err1 = new Error("foo");
var err2 = Object.create(err1);
err2.stack = "bar";
console.log(err2.hasOwnProperty("stack")); // Node: false, FF/Chrome: true
So you're surmising that when the stack
setter is called, calls Object.defineProperty()
to change stack
into a value property?
Confirmed. Here's what happens in Chrome:
var err = new Error("foo");
var descriptor = Object.getOwnPropertyDescriptor(err, "stack");
console.log(descriptor); // Object { get: function, set: function, enumerable: false, configurable: true }
err.stack = "foo";
console.log(descriptor); // Object { value: "foo", writable: true, enumerable: false, configurable: true }
In Node 0.10, the second log statement outputs the same as the first. So it looks like the only real bug is a compatibility issue across browsers (Firefox implements stack
as a data property only).
So it turns out that creating objects that violate [[Get]] what you [[Put]] cause really tricky non-intuitive situations.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I agree that its in the [[Put]]. In V8 the stack property has an get and set accessors.
In the spec listed above it should hit step 5 and do whatever should be done in the property descriptors
set
Also quoted from MDN
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
Shows that setters in the proto chain are also considered.
In my previous comment shows two side by side in the first
bar.abc === 123
in all browsers that I checked.Whereas in second
bar.abc === "hello"
.This implies to me that
Error
in older versions of V8Error
did something like in the first example above.