dylanb / gulp-coverage

Gulp coverage reporting for Node.js that is independent of the test runner
MIT License
60 stars 12 forks source link

Doesn't work with native node modules such as crypto. TypeError: Object.defineProperty called on non-object #18

Closed tarunc closed 10 years ago

tarunc commented 10 years ago
TypeError: Object.defineProperty called on non-object
      at Function.defineProperty (native)
      at __intro_MDZac7 (/xxx/src/main.js:24:41)
      at Object.randomString (/xxx/src/main.js:95:64)
      at Context.<anonymous> (/xxx/test/main.js:11:16)
      at callFn (/xxx/node_modules/mocha/lib/runnable.js:223:21)
      at Test.Runnable.run (/xxx/node_modules/mocha/lib/runnable.js:216:7)
      at Runner.runTest (/xxx/node_modules/mocha/lib/runner.js:374:10)
      at /xxx/node_modules/mocha/lib/runner.js:452:12
      at next (/xxx/node_modules/mocha/lib/runner.js:299:14)
      at /xxx/node_modules/mocha/lib/runner.js:309:7

The relevant code is:

var crypto = require('crypto');

var self = {};

var bytesMapper = {
    ascii: 1,
    utf8: 2,
    utf16le: 4,
    ucs2: 8,
    base64: 1.5,
    binary: 0.25,
    hex: 0.5
};

self.randomString = function randomString(length, encoding) {
    encoding = encoding || 'ascii';

    var buf = crypto.pseudoRandomBytes(length * bytesMapper[encoding] * 2);
    return buf.toString(encoding).substr(0, length);
};

module.exports = self;

Here is the instrumented code:


// Instrumentation Header
{
    var fs = require('fs');
    var __statement_Y9dYKY, __expression_s_b1xL, __block_QpunSS;
    var store = require('/xxx/node_modules/gulp-coverage/contrib/coverage_store.js');

    __statement_Y9dYKY = function(i) {
        var fd = store.register('/xxx/src/main.js');
        fs.writeSync(fd, '{"statement": {"node": ' + i + '}},\n');
    }; 

    __expression_s_b1xL = function(i) {
        var fd = store.register('/xxx/src/main.js');
        fs.writeSync(fd, '{"expression": {"node": ' + i + '}},\n');
    }; 

    __block_QpunSS = function(i) {
        var fd = store.register('/xxx/src/main.js');
        fs.writeSync(fd, '{"block": ' + i + '},\n');
    }; 
    __intro_MDZac7 = function(id, obj) {
        // console.log('__intro: ', id, ', obj.__instrumented_miss: ', obj.__instrumented_miss, ', obj.length: ', obj.length);
        Object.defineProperty && Object.defineProperty(obj, '__instrumented_miss', {enumerable: false, writable: true});
        obj.__instrumented_miss = obj.__instrumented_miss || [];
        if ('undefined' !== typeof obj && null !== obj && 'undefined' !== typeof obj.__instrumented_miss) {
            if (obj.length === 0) {
                // console.log('interim miss: ', id);
                obj.__instrumented_miss[id] = true;
            } else {
                obj.__instrumented_miss[id] = false;
            }
        }
        return obj;
    };
    function isProbablyChainable(obj, id) {
        return obj &&
            obj.__instrumented_miss[id] !== undefined &&
            'number' === typeof obj.length;
    }
    __extro_japAko = function(id, obj) {
        var fd = store.register('/xxx/src/main.js');
        // console.log('__extro: ', id, ', obj.__instrumented_miss: ', obj.__instrumented_miss, ', obj.length: ', obj.length);
        if ('undefined' !== typeof obj && null !== obj && 'undefined' !== typeof obj.__instrumented_miss) {
            if (isProbablyChainable(obj, id) && obj.length === 0 && obj.__instrumented_miss[id]) {
                // if the call was not a "constructor" - i.e. it did not add things to the chainable
                // and it did not return anything from the chainable, it is a miss
                // console.log('miss: ', id);
            } else {
                fs.writeSync(fd, '{"chain": {"node": ' + id + '}},\n');
            }
            obj.__instrumented_miss[id] = undefined;
        } else {
            fs.writeSync(fd, '{"chain": {"node": ' + id + '}},\n');
        }
        return obj;
    };
};
////////////////////////

// Instrumented Code
(function () {
    {
        __statement_Y9dYKY(0);
        var crypto = (__expression_s_b1xL(1), require('crypto'));
    }
    {
        __statement_Y9dYKY(2);
        var self = {};
    }
    {
        __statement_Y9dYKY(3);
        var bytesMapper = {
                ascii: 1,
                utf8: 2,
                utf16le: 4,
                ucs2: 8,
                base64: 1.5,
                binary: 0.25,
                hex: 0.5
            };
    }
    {
        __statement_Y9dYKY(4);
        self.randomString = function randomString(length, encoding) {
            __block_QpunSS(0);
            {
                __statement_Y9dYKY(5);
                encoding = (__expression_s_b1xL(6), (__expression_s_b1xL(7), encoding) || 'ascii');
            }
            {
                __statement_Y9dYKY(8);
                var buf = __extro_japAko(9, __intro_MDZac7(9, crypto).pseudoRandomBytes((__expression_s_b1xL(10), (__expression_s_b1xL(11), (__expression_s_b1xL(12), length) * bytesMapper[encoding]) * 2)));
            }
            return __expression_s_b1xL(13), __extro_japAko(14, __intro_MDZac7(14, __extro_japAko(15, __intro_MDZac7(15, buf).toString(encoding))).substr(0, length));
        };
    }
    {
        __statement_Y9dYKY(16);
        module.exports = self;
    }
}());
tarunc commented 10 years ago

Wrapping https://github.com/dylanb/gulp-coverage/blob/master/contrib/templates/instrumentation_header.js#L24 in a try/catch statement seems to work.

        try{
            Object.defineProperty && Object.defineProperty(obj, '__instrumented_miss', {enumerable: false, writable: true});
        }catch(e){obj.__instrumented_miss = [];}
dylanb commented 10 years ago

I think this is the correct fix here:

typeof obj === 'object' && Object.defineProperty && Object.defineProperty(obj, '__instrumented_miss', {enumerable: false, writable: true});
dylanb commented 10 years ago

Fixed in 0.1.23, please validate

tarunc commented 10 years ago

Yes, that works! Thanks for fixing this so quickly!