1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 * @fileoverview Provides a system for memoizing computations applied to
7 * DOM nodes within the same call stack.
9 * To make a function memoizable - suppose you have a function
10 * isAccessible that takes a node and returns a boolean:
12 * function isAccessible(node) {
13 * return expensiveComputation(node);
16 * Make it memoizable like this:
18 * function isAccessible(node) {
19 * return cvox.Memoize.memoize(computeIsAccessible_, 'isAccessible', node);
22 * function computeIsAccessible_(node) {
23 * return expensiveComputation(node);
26 * To take advantage of memoization, you need to wrap a sequence of
27 * computations in a call to memoize.scope() - memoization is only
28 * enabled while in that scope, and all cached data is thrown away at
29 * the end. You should use this only when you're sure the computation
30 * being memoized will not change within the scope.
32 * cvox.Memoize.scope(function() {
33 * console.log(isAccessible(document.body));
39 goog.provide('cvox.Memoize');
43 * Create the namespace.
46 cvox.Memoize = function() {
50 * The cache: a map from string function name to a WeakMap from DOM node
51 * to function result. This variable is null when we're out of scope, and it's
52 * a map from string to WeakMap to result when we're in scope.
54 * @type {?Object<WeakMap<Node, *> >}
57 cvox.Memoize.nodeMap_ = null;
60 * Keeps track of how many nested times scope() has been called.
64 cvox.Memoize.scopeCount_ = 0;
68 * Enables memoization within the scope of the given function. You should
69 * ensure that the DOM is not modified within this scope.
71 * It's safe to nest calls to scope. The nested calls have
72 * no effect, only the outermost one.
74 * @param {Function} functionScope The function to call with memoization
76 * @return {*} The value returned by |functionScope|.
78 cvox.Memoize.scope = function(functionScope) {
81 cvox.Memoize.scopeCount_++;
82 if (cvox.Memoize.scopeCount_ == 1) {
83 cvox.Memoize.nodeMap_ = {};
85 result = functionScope();
87 cvox.Memoize.scopeCount_--;
88 if (cvox.Memoize.scopeCount_ == 0) {
89 cvox.Memoize.nodeMap_ = null;
96 * Memoizes the result of a function call, so if you call this again
97 * with the same exact parameters and memoization is currently enabled
98 * (via a call to scope()), the second time the cached result
99 * will just be returned directly.
101 * @param {Function} functionClosure The function to call and cache the
103 * @param {string} functionName The name of the function you're calling.
104 * This string is used to store and retrieve the cached result, so
105 * it should be unique. If the function to be memoized takes simple
106 * arguments in addition to a DOM node, you can incorporate those
107 * arguments into the function name.
108 * @param {Node} node The DOM node that should be passed as the argument
110 * @return {*} The return value of |functionClosure|.
112 cvox.Memoize.memoize = function(functionClosure, functionName, node) {
113 if (cvox.Memoize.nodeMap_ &&
114 cvox.Memoize.nodeMap_[functionName] === undefined) {
115 cvox.Memoize.nodeMap_[functionName] = new WeakMap();
118 // If we're not in scope, just call the function directly.
119 if (!cvox.Memoize.nodeMap_) {
120 return functionClosure(node);
123 var result = cvox.Memoize.nodeMap_[functionName].get(node);
124 if (result === undefined) {
125 result = functionClosure(node);
126 if (result === undefined) {
127 throw 'A memoized function cannot return undefined.';
129 cvox.Memoize.nodeMap_[functionName].set(node, result);