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 | |
28 | goog.provide('goog.debug.EntryPointMonitor'); |
29 | goog.provide('goog.debug.entryPointRegistry'); |
30 | |
31 | goog.require('goog.asserts'); |
32 | |
33 | |
34 | |
35 | /** |
36 | * @interface |
37 | */ |
38 | goog.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 | */ |
47 | goog.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 | */ |
66 | goog.debug.EntryPointMonitor.prototype.unwrap; |
67 | |
68 | |
69 | /** |
70 | * An array of entry point callbacks. |
71 | * @type {!Array.<function(!Function)>} |
72 | * @private |
73 | */ |
74 | goog.debug.entryPointRegistry.refList_ = []; |
75 | |
76 | |
77 | /** |
78 | * Monitors that should wrap all the entry points. |
79 | * @type {!Array.<!goog.debug.EntryPointMonitor>} |
80 | * @private |
81 | */ |
82 | goog.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 | */ |
91 | goog.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 | */ |
106 | goog.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 | */ |
129 | goog.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 | */ |
149 | goog.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 | }; |