From e91f4b79924bf6f58e31fbcacbbcc598883c3e76 Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Sun, 1 Sep 2024 20:23:54 +0400 Subject: [PATCH] Improve rendering performance Avoid unnecessary copies via Utils.extend in hot paths. --- lib/handlebars/helpers.js | 3 +- .../internal/create-new-lookup-object.js | 11 ------- lib/handlebars/internal/proto-access.js | 32 +++++++++---------- lib/handlebars/runtime.js | 32 ++++++++----------- 4 files changed, 31 insertions(+), 47 deletions(-) delete mode 100644 lib/handlebars/internal/create-new-lookup-object.js diff --git a/lib/handlebars/helpers.js b/lib/handlebars/helpers.js index 804796820..0a6e44024 100644 --- a/lib/handlebars/helpers.js +++ b/lib/handlebars/helpers.js @@ -20,7 +20,8 @@ export function moveHelperToHooks(instance, helperName, keepHelper) { if (instance.helpers[helperName]) { instance.hooks[helperName] = instance.helpers[helperName]; if (!keepHelper) { - delete instance.helpers[helperName]; + // Using delete is slow + instance.helpers[helperName] = undefined; } } } diff --git a/lib/handlebars/internal/create-new-lookup-object.js b/lib/handlebars/internal/create-new-lookup-object.js deleted file mode 100644 index 256f1eca1..000000000 --- a/lib/handlebars/internal/create-new-lookup-object.js +++ /dev/null @@ -1,11 +0,0 @@ -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); -} diff --git a/lib/handlebars/internal/proto-access.js b/lib/handlebars/internal/proto-access.js index 9d19c23f7..a9b552e26 100644 --- a/lib/handlebars/internal/proto-access.js +++ b/lib/handlebars/internal/proto-access.js @@ -1,32 +1,30 @@ -import { createNewLookupObject } from './create-new-lookup-object'; +import { extend } from '../utils'; import logger from '../logger'; const loggedProperties = Object.create(null); export function createProtoAccessControl(runtimeOptions) { - let defaultMethodWhiteList = Object.create(null); - defaultMethodWhiteList['constructor'] = false; - defaultMethodWhiteList['__defineGetter__'] = false; - defaultMethodWhiteList['__defineSetter__'] = false; - defaultMethodWhiteList['__lookupGetter__'] = false; - - let defaultPropertyWhiteList = Object.create(null); + // Create an object with "null"-prototype to avoid truthy results on + // prototype properties. + const propertyWhiteList = Object.create(null); // eslint-disable-next-line no-proto - defaultPropertyWhiteList['__proto__'] = false; + propertyWhiteList['__proto__'] = false; + extend(propertyWhiteList, runtimeOptions.allowedProtoProperties); + + const methodWhiteList = Object.create(null); + methodWhiteList['constructor'] = false; + methodWhiteList['__defineGetter__'] = false; + methodWhiteList['__defineSetter__'] = false; + methodWhiteList['__lookupGetter__'] = false; + extend(methodWhiteList, runtimeOptions.allowedProtoMethods); return { properties: { - whitelist: createNewLookupObject( - defaultPropertyWhiteList, - runtimeOptions.allowedProtoProperties - ), + whitelist: propertyWhiteList, defaultValue: runtimeOptions.allowProtoPropertiesByDefault, }, methods: { - whitelist: createNewLookupObject( - defaultMethodWhiteList, - runtimeOptions.allowedProtoMethods - ), + whitelist: methodWhiteList, defaultValue: runtimeOptions.allowProtoMethodsByDefault, }, }; diff --git a/lib/handlebars/runtime.js b/lib/handlebars/runtime.js index 49842c343..ca1084486 100644 --- a/lib/handlebars/runtime.js +++ b/lib/handlebars/runtime.js @@ -71,17 +71,10 @@ export function template(templateSpec, env) { } partial = env.VM.resolvePartial.call(this, partial, context, options); - let extendedOptions = Utils.extend({}, options, { - hooks: this.hooks, - protoAccessControl: this.protoAccessControl, - }); - - let result = env.VM.invokePartial.call( - this, - partial, - context, - extendedOptions - ); + options.hooks = this.hooks; + options.protoAccessControl = this.protoAccessControl; + + let result = env.VM.invokePartial.call(this, partial, context, options); if (result == null && env.compile) { options.partials[options.name] = env.compile( @@ -89,7 +82,7 @@ export function template(templateSpec, env) { templateSpec.compilerOptions, env ); - result = options.partials[options.name](context, extendedOptions); + result = options.partials[options.name](context, options); } if (result != null) { if (options.indent) { @@ -255,8 +248,9 @@ export function template(templateSpec, env) { function _setup(options) { if (!options.partial) { - let mergedHelpers = Utils.extend({}, env.helpers, options.helpers); - wrapHelpersToPassLookupProperty(mergedHelpers, container); + let mergedHelpers = {}; + addHelpers(mergedHelpers, env.helpers, container); + addHelpers(mergedHelpers, options.helpers, container); container.helpers = mergedHelpers; if (templateSpec.usePartial) { @@ -417,9 +411,10 @@ function executeDecorators(fn, prog, container, depths, data, blockParams) { return prog; } -function wrapHelpersToPassLookupProperty(mergedHelpers, container) { - Object.keys(mergedHelpers).forEach((helperName) => { - let helper = mergedHelpers[helperName]; +function addHelpers(mergedHelpers, helpers, container) { + if (!helpers) return; + Object.keys(helpers).forEach((helperName) => { + let helper = helpers[helperName]; mergedHelpers[helperName] = passLookupPropertyOption(helper, container); }); } @@ -427,6 +422,7 @@ function wrapHelpersToPassLookupProperty(mergedHelpers, container) { function passLookupPropertyOption(helper, container) { const lookupProperty = container.lookupProperty; return wrapHelper(helper, (options) => { - return Utils.extend({ lookupProperty }, options); + options.lookupProperty = lookupProperty; + return options; }); }