Skip to content

Instantly share code, notes, and snippets.

@nzakas
Created February 27, 2014 19:13
Show Gist options
  • Save nzakas/9257017 to your computer and use it in GitHub Desktop.
Save nzakas/9257017 to your computer and use it in GitHub Desktop.
Is this a Node.js bug?
value1: {"stack":"MyStack","type":"MyType"} true
value2: {"stack":"MyStack","message":"MyError"} true
value3: {"stack":"MyStack","message":"MyError"} true
value4: {"stack":"MyStack","type":"MyType"} true
value1: {} false
value2: {"stack":"MyStack","message":"MyError"} true
value3: {"message":"MyError"} false
value4: {} false
/*
* 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'));
@nzakas
Copy link
Author

nzakas commented Feb 28, 2014

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?

@nzakas
Copy link
Author

nzakas commented Feb 28, 2014

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).

@CrypticSwarm
Copy link

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