Skip to content

Commit

Permalink
Merge branch '4.x'
Browse files Browse the repository at this point in the history
# Conflicts:
#	components/bower.json
#	components/handlebars.js.nuspec
#	components/package.json
#	package.json
  • Loading branch information
nknapp committed Jan 8, 2020
2 parents d23ccf6 + 91a1b5d commit 0c8230c
Show file tree
Hide file tree
Showing 18 changed files with 550 additions and 145 deletions.
2 changes: 1 addition & 1 deletion lib/handlebars/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { registerDefaultHelpers } from './helpers';
import { registerDefaultDecorators } from './decorators';
import logger from './logger';

export const VERSION = '4.5.3';
export const VERSION = '4.6.0';
export const COMPILER_REVISION = 8;
export const LAST_COMPATIBLE_COMPILER_REVISION = 7;

Expand Down
54 changes: 31 additions & 23 deletions lib/handlebars/compiler/javascript-compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { COMPILER_REVISION, REVISION_CHANGES } from '../base';
import Exception from '../exception';
import { isArray } from '../utils';
import CodeGen from './code-gen';
import { dangerousPropertyRegex } from '../helpers/lookup';

function Literal(value) {
this.value = value;
Expand All @@ -13,27 +12,8 @@ function JavaScriptCompiler() {}
JavaScriptCompiler.prototype = {
// PUBLIC API: You can override these methods in a subclass to provide
// alternative compiled forms for name lookup and buffering semantics
nameLookup: function(parent, name /* , type*/) {
if (dangerousPropertyRegex.test(name)) {
const isEnumerable = [
this.aliasable('container.propertyIsEnumerable'),
'.call(',
parent,
',',
JSON.stringify(name),
')'
];
return ['(', isEnumerable, '?', _actualLookup(), ' : undefined)'];
}
return _actualLookup();

function _actualLookup() {
if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
return [parent, '.', name];
} else {
return [parent, '[', JSON.stringify(name), ']'];
}
}
nameLookup: function(parent, name /*, type */) {
return this.internalNameLookup(parent, name);
},
depthedLookup: function(name) {
return [this.aliasable('container.lookup'), '(depths, "', name, '")'];
Expand Down Expand Up @@ -69,6 +49,12 @@ JavaScriptCompiler.prototype = {
return this.quotedString('');
},
// END PUBLIC API
internalNameLookup: function(parent, name) {
this.lookupPropertyFunctionIsUsed = true;
return ['lookupProperty(', parent, ',', JSON.stringify(name), ')'];
},

lookupPropertyFunctionIsUsed: false,

compile: function(environment, options, context, asObject) {
this.environment = environment;
Expand Down Expand Up @@ -129,7 +115,11 @@ JavaScriptCompiler.prototype = {
if (!this.decorators.isEmpty()) {
this.useDecorators = true;

this.decorators.prepend('var decorators = container.decorators;\n');
this.decorators.prepend([
'var decorators = container.decorators, ',
this.lookupPropertyFunctionVarDeclaration(),
';\n'
]);
this.decorators.push('return fn;');

if (asObject) {
Expand Down Expand Up @@ -246,6 +236,10 @@ JavaScriptCompiler.prototype = {
}
});

if (this.lookupPropertyFunctionIsUsed) {
varDeclarations += ', ' + this.lookupPropertyFunctionVarDeclaration();
}

let params = ['container', 'depth0', 'helpers', 'partials', 'data'];

if (this.useBlockParams || this.useDepths) {
Expand Down Expand Up @@ -333,6 +327,17 @@ JavaScriptCompiler.prototype = {
return this.source.merge();
},

lookupPropertyFunctionVarDeclaration: function() {
return `
lookupProperty = container.lookupProperty || function(parent, propertyName) {
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
return parent[propertyName];
}
return undefined
}
`.trim();
},

// [blockValue]
//
// On stack, before: hash, inverse, program, value
Expand Down Expand Up @@ -1132,6 +1137,9 @@ JavaScriptCompiler.prototype = {
}
})();

/**
* @deprecated May be removed in the next major version
*/
JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
return (
!JavaScriptCompiler.RESERVED_WORDS[name] &&
Expand Down
13 changes: 3 additions & 10 deletions lib/handlebars/helpers/lookup.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
export const dangerousPropertyRegex = /^(constructor|__defineGetter__|__defineSetter__|__lookupGetter__|__proto__)$/;

export default function(instance) {
instance.registerHelper('lookup', function(obj, field) {
instance.registerHelper('lookup', function(obj, field, options) {
if (!obj) {
// Note for 5.0: Change to "obj == null" in 5.0
return obj;
}
if (
dangerousPropertyRegex.test(String(field)) &&
!Object.prototype.propertyIsEnumerable.call(obj, field)
) {
return undefined;
}
return obj[field];
return options.lookupProperty(obj, field);
});
}
11 changes: 11 additions & 0 deletions lib/handlebars/internal/createNewLookupObject.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { extend } from '../utils';

/**
* Create a new object with "null"-prototype to avoid truthy results on prototype properties.
* The resulting object can be used with "object[property]" to check if a property exists
* @param {...object} sources a varargs parameter of source objects that will be merged
* @returns {object}
*/
export function createNewLookupObject(...sources) {
return extend(Object.create(null), ...sources);
}
8 changes: 8 additions & 0 deletions lib/handlebars/internal/wrapHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export function wrapHelper(helper, transformOptionsFn) {
let wrapper = function(/* dynamic arguments */) {
const options = arguments[arguments.length - 1];
arguments[arguments.length - 1] = transformOptionsFn(options);
return helper.apply(this, arguments);
};
return wrapper;
}
56 changes: 51 additions & 5 deletions lib/handlebars/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
REVISION_CHANGES
} from './base';
import { moveHelperToHooks } from './helpers';
import { wrapHelper } from './internal/wrapHelper';
import { createNewLookupObject } from './internal/createNewLookupObject';

export function checkRevision(compilerInfo) {
const compilerRevision = (compilerInfo && compilerInfo[0]) || 1,
Expand Down Expand Up @@ -66,13 +68,17 @@ export function template(templateSpec, env) {
}
partial = env.VM.resolvePartial.call(this, partial, context, options);

let optionsWithHooks = Utils.extend({}, options, { hooks: this.hooks });
let extendedOptions = Utils.extend({}, options, {
hooks: this.hooks,
allowedProtoMethods: this.allowedProtoMethods,
allowedProtoProperties: this.allowedProtoProperties
});

let result = env.VM.invokePartial.call(
this,
partial,
context,
optionsWithHooks
extendedOptions
);

if (result == null && env.compile) {
Expand All @@ -81,7 +87,7 @@ export function template(templateSpec, env) {
templateSpec.compilerOptions,
env
);
result = options.partials[options.name](context, optionsWithHooks);
result = options.partials[options.name](context, extendedOptions);
}
if (result != null) {
if (options.indent) {
Expand Down Expand Up @@ -115,10 +121,26 @@ export function template(templateSpec, env) {
}
return obj[name];
},
lookupProperty: function(parent, propertyName) {
let result = parent[propertyName];
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
return result;
}
const whitelist =
typeof result === 'function'
? container.allowedProtoMethods
: container.allowedProtoProperties;

if (whitelist[propertyName] === true) {
return result;
}
return undefined;
},
lookup: function(depths, name) {
const len = depths.length;
for (let i = 0; i < len; i++) {
if (depths[i] && depths[i][name] != null) {
let result = depths[i] && container.lookupProperty(depths[i], name);
if (result != null) {
return depths[i][name];
}
}
Expand Down Expand Up @@ -226,7 +248,9 @@ export function template(templateSpec, env) {

function _setup(options) {
if (!options.partial) {
container.helpers = Utils.extend({}, env.helpers, options.helpers);
let mergedHelpers = Utils.extend({}, env.helpers, options.helpers);
wrapHelpersToPassLookupProperty(mergedHelpers, container);
container.helpers = mergedHelpers;

if (templateSpec.usePartial) {
// Use mergeIfNeeded here to prevent compiling global partials multiple times
Expand All @@ -244,13 +268,21 @@ export function template(templateSpec, env) {
}

container.hooks = {};
container.allowedProtoProperties = createNewLookupObject(
options.allowedProtoProperties
);
container.allowedProtoMethods = createNewLookupObject(
options.allowedProtoMethods
);

let keepHelperInHelpers =
options.allowCallsToHelperMissing ||
templateWasPrecompiledWithCompilerV7;
moveHelperToHooks(container, 'helperMissing', keepHelperInHelpers);
moveHelperToHooks(container, 'blockHelperMissing', keepHelperInHelpers);
} else {
container.allowedProtoProperties = options.allowedProtoProperties;
container.allowedProtoMethods = options.allowedProtoMethods;
container.helpers = options.helpers;
container.partials = options.partials;
container.decorators = options.decorators;
Expand Down Expand Up @@ -381,3 +413,17 @@ function executeDecorators(fn, prog, container, depths, data, blockParams) {
}
return prog;
}

function wrapHelpersToPassLookupProperty(mergedHelpers, container) {
Object.keys(mergedHelpers).forEach(helperName => {
let helper = mergedHelpers[helperName];
mergedHelpers[helperName] = passLookupPropertyOption(helper, container);
});
}

function passLookupPropertyOption(helper, container) {
const lookupProperty = container.lookupProperty;
return wrapHelper(helper, options => {
return Utils.extend({ lookupProperty }, options);
});
}
17 changes: 17 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"babel-runtime": "^5.1.10",
"benchmark": "~1.0",
"chai": "^4.2.0",
"chai-diff": "^1.0.1",
"concurrently": "^5.0.0",
"dirty-chai": "^2.0.1",
"dtslint": "^0.5.5",
Expand Down Expand Up @@ -84,7 +85,7 @@
"scripts": {
"format": "prettier --write '**/*.js' && eslint --fix .",
"check-format": "prettier --check '**/*.js'",
"lint": "eslint --max-warnings 0 . ",
"lint": "eslint --max-warnings 0 .",
"dtslint": "dtslint types",
"test": "grunt",
"extensive-tests-and-publish-to-aws": "npx mocha tasks/task-tests/ && grunt --stack extensive-tests-and-publish-to-aws",
Expand Down
Loading

0 comments on commit 0c8230c

Please sign in to comment.