diff --git a/lib/ejs.js b/lib/ejs.js index bd7ca927..13dc7392 100755 --- a/lib/ejs.js +++ b/lib/ejs.js @@ -67,17 +67,6 @@ var _OPTS_PASSABLE_WITH_DATA_EXPRESS = _OPTS_PASSABLE_WITH_DATA.concat('cache'); var _BOM = /^\uFEFF/; var _JS_IDENTIFIER = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/; -var createObj = function() { - if (typeof Object.create !== 'function') { - return function (o) { - function F() {} - F.prototype = o; - return new F(); - }; - } - return Object.create; -}(); - /** * EJS template function cache. This can be a LRU object from lru-cache NPM * module. By default, it is {@link module:utils.cache}, a simple in-process @@ -318,7 +307,7 @@ function fileLoader(filePath){ */ function includeFile(path, options) { - var opts = utils.shallowCopy(createObj(null), options); + var opts = utils.shallowCopy(utils.createNullProtoObjWherePossible(), options); opts.filename = getIncludePath(path, opts); if (typeof options.includer === 'function') { var includerResult = options.includer(path, opts.filename); @@ -424,8 +413,8 @@ exports.compile = function compile(template, opts) { */ exports.render = function (template, d, o) { - var data = d || createObj(null); - var opts = o || createObj(null); + var data = d || utils.createNullProtoObjWherePossible(); + var opts = o || utils.createNullProtoObjWherePossible(); // No options object -- if there are optiony names // in the data, copy them to options @@ -496,7 +485,7 @@ exports.renderFile = function () { opts.filename = filename; } else { - data = createObj(null); + data = utils.createNullProtoObjWherePossible(); } return tryHandleCache(opts, data, cb); @@ -518,8 +507,8 @@ exports.clearCache = function () { }; function Template(text, opts) { - opts = opts || createObj(null); - var options = createObj(null); + opts = opts || utils.createNullProtoObjWherePossible(); + var options = utils.createNullProtoObjWherePossible(); this.templateText = text; /** @type {string | null} */ this.mode = null; @@ -705,14 +694,14 @@ Template.prototype = { // Adds a local `include` function which allows full recursive include var returnedFn = opts.client ? fn : function anonymous(data) { var include = function (path, includeData) { - var d = utils.shallowCopy(createObj(null), data); + var d = utils.shallowCopy(utils.createNullProtoObjWherePossible(), data); if (includeData) { d = utils.shallowCopy(d, includeData); } return includeFile(path, opts)(d); }; return fn.apply(opts.context, - [data || createObj(null), escapeFn, include, rethrow]); + [data || utils.createNullProtoObjWherePossible(), escapeFn, include, rethrow]); }; if (opts.filename && typeof Object.defineProperty === 'function') { var filename = opts.filename; diff --git a/lib/utils.js b/lib/utils.js index b01e37bf..5aef7c82 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -183,3 +183,29 @@ exports.cache = { exports.hyphenToCamel = function (str) { return str.replace(/-[a-z]/g, function (match) { return match[1].toUpperCase(); }); }; + +/** + * Returns a null-prototype object in runtimes that support it + * + * @return {Object} Object, prototype will be set to null where possible + * @static + * @private + */ +exports.createNullProtoObjWherePossible = (function () { + if (typeof Object.create == 'function') { + return function () { + return Object.create(null); + }; + } + if (!({__proto__: null} instanceof Object)) { + return function () { + return {__proto__: null}; + }; + } + // Not possible, just pass through + return function () { + return {}; + }; +})(); + + diff --git a/test/ejs.js b/test/ejs.js index a8d8a814..607b4a71 100755 --- a/test/ejs.js +++ b/test/ejs.js @@ -1183,24 +1183,26 @@ suite('identifier validation', function () { test('invalid outputFunctionName', function() { assert.throws(function() { ejs.compile('

yay

', {outputFunctionName: 'x;console.log(1);x'}); - }, /outputFunctionName is not a valid JS identifier/) + }, /outputFunctionName is not a valid JS identifier/); }); test('invalid localsName', function() { var locals = Object.create(null); + void(locals); // For linting; assert.throws(function() { ejs.compile('

yay

', { localsName: 'function(){console.log(1);return locals;}()'}); - }, /localsName is not a valid JS identifier/) + }, /localsName is not a valid JS identifier/); }); test('invalid destructuredLocals', function() { var locals = {}; + void(locals); // For linting; assert.throws(function() { ejs.compile('

yay

', { destructuredLocals: [ 'console.log(1); //' ]}); - }, /destructuredLocals\[0\] is not a valid JS identifier/) + }, /destructuredLocals\[0\] is not a valid JS identifier/); }); -}); \ No newline at end of file +});