lib/goog/debug/entrypointregistry.js

1// Copyright 2010 The Closure Library Authors. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS-IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15/**
16 * @fileoverview A global registry for entry points into a program,
17 * so that they can be instrumented. Each module should register their
18 * entry points with this registry. Designed to be compiled out
19 * if no instrumentation is requested.
20 *
21 * Entry points may be registered before or after a call to
22 * goog.debug.entryPointRegistry.monitorAll. If an entry point is registered
23 * later, the existing monitor will instrument the new entry point.
24 *
25 * @author nicksantos@google.com (Nick Santos)
26 */
27
28goog.provide('goog.debug.EntryPointMonitor');
29goog.provide('goog.debug.entryPointRegistry');
30
31goog.require('goog.asserts');
32
33
34
35/**
36 * @interface
37 */
38goog.debug.EntryPointMonitor = function() {};
39
40
41/**
42 * Instruments a function.
43 *
44 * @param {!Function} fn A function to instrument.
45 * @return {!Function} The instrumented function.
46 */
47goog.debug.EntryPointMonitor.prototype.wrap;
48
49
50/**
51 * Try to remove an instrumentation wrapper created by this monitor.
52 * If the function passed to unwrap is not a wrapper created by this
53 * monitor, then we will do nothing.
54 *
55 * Notice that some wrappers may not be unwrappable. For example, if other
56 * monitors have applied their own wrappers, then it will be impossible to
57 * unwrap them because their wrappers will have captured our wrapper.
58 *
59 * So it is important that entry points are unwrapped in the reverse
60 * order that they were wrapped.
61 *
62 * @param {!Function} fn A function to unwrap.
63 * @return {!Function} The unwrapped function, or {@code fn} if it was not
64 * a wrapped function created by this monitor.
65 */
66goog.debug.EntryPointMonitor.prototype.unwrap;
67
68
69/**
70 * An array of entry point callbacks.
71 * @type {!Array<function(!Function)>}
72 * @private
73 */
74goog.debug.entryPointRegistry.refList_ = [];
75
76
77/**
78 * Monitors that should wrap all the entry points.
79 * @type {!Array<!goog.debug.EntryPointMonitor>}
80 * @private
81 */
82goog.debug.entryPointRegistry.monitors_ = [];
83
84
85/**
86 * Whether goog.debug.entryPointRegistry.monitorAll has ever been called.
87 * Checking this allows the compiler to optimize out the registrations.
88 * @type {boolean}
89 * @private
90 */
91goog.debug.entryPointRegistry.monitorsMayExist_ = false;
92
93
94/**
95 * Register an entry point with this module.
96 *
97 * The entry point will be instrumented when a monitor is passed to
98 * goog.debug.entryPointRegistry.monitorAll. If this has already occurred, the
99 * entry point is instrumented immediately.
100 *
101 * @param {function(!Function)} callback A callback function which is called
102 * with a transforming function to instrument the entry point. The callback
103 * is responsible for wrapping the relevant entry point with the
104 * transforming function.
105 */
106goog.debug.entryPointRegistry.register = function(callback) {
107 // Don't use push(), so that this can be compiled out.
108 goog.debug.entryPointRegistry.refList_[
109 goog.debug.entryPointRegistry.refList_.length] = callback;
110 // If no one calls monitorAll, this can be compiled out.
111 if (goog.debug.entryPointRegistry.monitorsMayExist_) {
112 var monitors = goog.debug.entryPointRegistry.monitors_;
113 for (var i = 0; i < monitors.length; i++) {
114 callback(goog.bind(monitors[i].wrap, monitors[i]));
115 }
116 }
117};
118
119
120/**
121 * Configures a monitor to wrap all entry points.
122 *
123 * Entry points that have already been registered are immediately wrapped by
124 * the monitor. When an entry point is registered in the future, it will also
125 * be wrapped by the monitor when it is registered.
126 *
127 * @param {!goog.debug.EntryPointMonitor} monitor An entry point monitor.
128 */
129goog.debug.entryPointRegistry.monitorAll = function(monitor) {
130 goog.debug.entryPointRegistry.monitorsMayExist_ = true;
131 var transformer = goog.bind(monitor.wrap, monitor);
132 for (var i = 0; i < goog.debug.entryPointRegistry.refList_.length; i++) {
133 goog.debug.entryPointRegistry.refList_[i](transformer);
134 }
135 goog.debug.entryPointRegistry.monitors_.push(monitor);
136};
137
138
139/**
140 * Try to unmonitor all the entry points that have already been registered. If
141 * an entry point is registered in the future, it will not be wrapped by the
142 * monitor when it is registered. Note that this may fail if the entry points
143 * have additional wrapping.
144 *
145 * @param {!goog.debug.EntryPointMonitor} monitor The last monitor to wrap
146 * the entry points.
147 * @throws {Error} If the monitor is not the most recently configured monitor.
148 */
149goog.debug.entryPointRegistry.unmonitorAllIfPossible = function(monitor) {
150 var monitors = goog.debug.entryPointRegistry.monitors_;
151 goog.asserts.assert(monitor == monitors[monitors.length - 1],
152 'Only the most recent monitor can be unwrapped.');
153 var transformer = goog.bind(monitor.unwrap, monitor);
154 for (var i = 0; i < goog.debug.entryPointRegistry.refList_.length; i++) {
155 goog.debug.entryPointRegistry.refList_[i](transformer);
156 }
157 monitors.length--;
158};