import * as Utils from './utils';import Exception from './exception';import {  COMPILER_REVISION,  createFrame,  LAST_COMPATIBLE_COMPILER_REVISION,  REVISION_CHANGES} from './base';import { moveHelperToHooks } from './helpers';import { wrapHelper } from './internal/wrapHelper';import {  createProtoAccessControl,  resultIsAllowed} from './internal/proto-access';export function checkRevision(compilerInfo) {  const compilerRevision = (compilerInfo && compilerInfo[0]) || 1,    currentRevision = COMPILER_REVISION;  if (    compilerRevision >= LAST_COMPATIBLE_COMPILER_REVISION &&    compilerRevision <= COMPILER_REVISION  ) {    return;  }  if (compilerRevision < LAST_COMPATIBLE_COMPILER_REVISION) {    const runtimeVersions = REVISION_CHANGES[currentRevision],      compilerVersions = REVISION_CHANGES[compilerRevision];    throw new Exception(      'Template was precompiled with an older version of Handlebars than the current runtime. ' +        'Please update your precompiler to a newer version (' +        runtimeVersions +        ') or downgrade your runtime to an older version (' +        compilerVersions +        ').'    );  } else {    // Use the embedded version info since the runtime doesn't know about this revision yet    throw new Exception(      'Template was precompiled with a newer version of Handlebars than the current runtime. ' +        'Please update your runtime to a newer version (' +        compilerInfo[1] +        ').'    );  }}export function template(templateSpec, env) {  /* istanbul ignore next */  if (!env) {    throw new Exception('No environment passed to template');  }  if (!templateSpec || !templateSpec.main) {    throw new Exception('Unknown template object: ' + typeof templateSpec);  }  templateSpec.main.decorator = templateSpec.main_d;  // Note: Using env.VM references rather than local var references throughout this section to allow  // for external users to override these as pseudo-supported APIs.  env.VM.checkRevision(templateSpec.compiler);  // backwards compatibility for precompiled templates with compiler-version 7 (<4.3.0)  const templateWasPrecompiledWithCompilerV7 =    templateSpec.compiler && templateSpec.compiler[0] === 7;  function invokePartialWrapper(partial, context, options) {    if (options.hash) {      context = Utils.extend({}, context, options.hash);      if (options.ids) {        options.ids[0] = true;      }    }    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    );    if (result == null && env.compile) {      options.partials[options.name] = env.compile(        partial,        templateSpec.compilerOptions,        env      );      result = options.partials[options.name](context, extendedOptions);    }    if (result != null) {      if (options.indent) {        let lines = result.split('\n');        for (let i = 0, l = lines.length; i < l; i++) {          if (!lines[i] && i + 1 === l) {            break;          }          lines[i] = options.indent + lines[i];        }        result = lines.join('\n');      }      return result;    } else {      throw new Exception(        'The partial ' +          options.name +          ' could not be compiled when running in runtime-only mode'      );    }  }  // Just add water  let container = {    strict: function(obj, name, loc) {      if (!obj || !(name in obj)) {        throw new Exception('"' + name + '" not defined in ' + obj, {          loc: loc        });      }      // BUG: NVD-CWE-noinfo Insufficient Information      // return obj[name];      // FIXED:       return container.lookupProperty(obj, name);    },    lookupProperty: function(parent, propertyName) {      let result = parent[propertyName];      if (result == null) {        return result;      }      if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {        return result;      }      if (resultIsAllowed(result, container.protoAccessControl, propertyName)) {        return result;      }      return undefined;    },    lookup: function(depths, name) {      const len = depths.length;      for (let i = 0; i < len; i++) {        let result = depths[i] && container.lookupProperty(depths[i], name);        if (result != null) {          return depths[i][name];        }      }    },    lambda: function(current, context) {      return typeof current === 'function' ? current.call(context) : current;    },    escapeExpression: Utils.escapeExpression,    invokePartial: invokePartialWrapper,    fn: function(i) {      let ret = templateSpec[i];      ret.decorator = templateSpec[i + '_d'];      return ret;    },    programs: [],    program: function(i, data, declaredBlockParams, blockParams, depths) {      let programWrapper = this.programs[i],        fn = this.fn(i);      if (data || depths || blockParams || declaredBlockParams) {        programWrapper = wrapProgram(          this,          i,          fn,          data,          declaredBlockParams,          blockParams,          depths        );      } else if (!programWrapper) {        programWrapper = this.programs[i] = wrapProgram(this, i, fn);      }      return programWrapper;    },    data: function(value, depth) {      while (value && depth--) {        value = value._parent;      }      return value;    },    mergeIfNeeded: function(param, common) {      let obj = param || common;      if (param && common && param !== common) {        obj = Utils.extend({}, common, param);      }      return obj;    },    // An empty object to use as replacement for null-contexts    nullContext: Object.seal({}),    noop: env.VM.noop,    compilerInfo: templateSpec.compiler  };  function ret(context, options = {}) {    let data = options.data;    ret._setup(options);    if (!options.partial && templateSpec.useData) {      data = initData(context, data);    }    let depths,      blockParams = templateSpec.useBlockParams ? [] : undefined;    if (templateSpec.useDepths) {      if (options.depths) {        depths =          context != options.depths[0]            ? [context].concat(options.depths)            : options.depths;      } else {        depths = [context];      }    }    function main(context /*, options*/) {      return (        '' +        templateSpec.main(          container,          context,          container.helpers,          container.partials,          data,          blockParams,          depths        )      );    }    main = executeDecorators(      templateSpec.main,      main,      container,      options.depths || [],      data,      blockParams    );    return main(context, options);  }  ret.isTop = true;  ret._setup = function(options) {    if (!options.partial) {      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        container.partials = container.mergeIfNeeded(          options.partials,          env.partials        );      }      if (templateSpec.usePartial || templateSpec.useDecorators) {        container.decorators = Utils.extend(          {},          env.decorators,          options.decorators        );      }      container.hooks = {};      container.protoAccessControl = createProtoAccessControl(options);      let keepHelperInHelpers =        options.allowCallsToHelperMissing ||        templateWasPrecompiledWithCompilerV7;      moveHelperToHooks(container, 'helperMissing', keepHelperInHelpers);      moveHelperToHooks(container, 'blockHelperMissing', keepHelperInHelpers);    } else {      container.protoAccessControl = options.protoAccessControl; // internal option      container.helpers = options.helpers;      container.partials = options.partials;      container.decorators = options.decorators;      container.hooks = options.hooks;    }  };  ret._child = function(i, data, blockParams, depths) {    if (templateSpec.useBlockParams && !blockParams) {      throw new Exception('must pass block params');    }    if (templateSpec.useDepths && !depths) {      throw new Exception('must pass parent depths');    }    return wrapProgram(      container,      i,      templateSpec[i],      data,      0,      blockParams,      depths    );  };  return ret;}export function wrapProgram(  container,  i,  fn,  data,  declaredBlockParams,  blockParams,  depths) {  function prog(context, options = {}) {    let currentDepths = depths;    if (      depths &&      context != depths[0] &&      !(context === container.nullContext && depths[0] === null)    ) {      currentDepths = [context].concat(depths);    }    return fn(      container,      context,      container.helpers,      container.partials,      options.data || data,      blockParams && [options.blockParams].concat(blockParams),      currentDepths    );  }  prog = executeDecorators(fn, prog, container, depths, data, blockParams);  prog.program = i;  prog.depth = depths ? depths.length : 0;  prog.blockParams = declaredBlockParams || 0;  return prog;}/** * This is currently part of the official API, therefore implementation details should not be changed. */export function resolvePartial(partial, context, options) {  if (!partial) {    if (options.name === '@partial-block') {      partial = options.data['partial-block'];    } else {      partial = options.partials[options.name];    }  } else if (!partial.call && !options.name) {    // This is a dynamic partial that returned a string    options.name = partial;    partial = options.partials[partial];  }  return partial;}export function invokePartial(partial, context, options) {  // Use the current closure context to save the partial-block if this partial  const currentPartialBlock = options.data && options.data['partial-block'];  options.partial = true;  if (options.ids) {    options.data.contextPath = options.ids[0] || options.data.contextPath;  }  let partialBlock;  if (options.fn && options.fn !== noop) {    options.data = createFrame(options.data);    // Wrapper function to get access to currentPartialBlock from the closure    let fn = options.fn;    partialBlock = options.data['partial-block'] = function partialBlockWrapper(      context,      options = {}    ) {      // Restore the partial-block from the closure for the execution of the block      // i.e. the part inside the block of the partial call.      options.data = createFrame(options.data);      options.data['partial-block'] = currentPartialBlock;      return fn(context, options);    };    if (fn.partials) {      options.partials = Utils.extend({}, options.partials, fn.partials);    }  }  if (partial === undefined && partialBlock) {    partial = partialBlock;  }  if (partial === undefined) {    throw new Exception('The partial ' + options.name + ' could not be found');  } else if (partial instanceof Function) {    return partial(context, options);  }}export function noop() {  return '';}function initData(context, data) {  if (!data || !('root' in data)) {    data = data ? createFrame(data) : {};    data.root = context;  }  return data;}function executeDecorators(fn, prog, container, depths, data, blockParams) {  if (fn.decorator) {    let props = {};    prog = fn.decorator(      prog,      props,      container,      depths && depths[0],      data,      blockParams,      depths    );    Utils.extend(prog, props);  }  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);  });}