1 | <html> |
---|
2 | <head> |
---|
3 | <title>aspects</title> |
---|
4 | <style type="text/css"> |
---|
5 | @import "../../../dojo/resources/dojo.css"; |
---|
6 | </style> |
---|
7 | <script type="text/javascript" src="../../../dojo/dojo.js" data-dojo-config="isDebug:true"></script> |
---|
8 | <script type="text/javascript" src="../aspect.js"></script> |
---|
9 | <script type="text/javascript" src="../aspect/cflow.js"></script> |
---|
10 | <script type="text/javascript" src="../aspect/tracer.js"></script> |
---|
11 | <script type="text/javascript" src="../aspect/timer.js"></script> |
---|
12 | <script type="text/javascript" src="../aspect/profiler.js"></script> |
---|
13 | <script type="text/javascript" src="../aspect/counter.js"></script> |
---|
14 | <script type="text/javascript" src="../aspect/memoizer.js"></script> |
---|
15 | <script type="text/javascript" src="../aspect/memoizerGuard.js"></script> |
---|
16 | <script type="text/javascript"> |
---|
17 | dojo.require("dojox.lang.aspect"); |
---|
18 | dojo.require("dojox.lang.aspect.cflow"); |
---|
19 | dojo.require("dojox.lang.aspect.tracer"); |
---|
20 | dojo.require("dojox.lang.aspect.timer"); |
---|
21 | dojo.require("dojox.lang.aspect.profiler"); |
---|
22 | dojo.require("dojox.lang.aspect.counter"); |
---|
23 | dojo.require("dojox.lang.aspect.memoizer"); |
---|
24 | dojo.require("dojox.lang.aspect.memoizerGuard"); |
---|
25 | dojo.require("dojo.string"); |
---|
26 | dojo.require("dojox.lang.functional.listcomp"); |
---|
27 | |
---|
28 | var test = function(){ |
---|
29 | // This is a test class, don't program like this! |
---|
30 | var Rect = function(){ |
---|
31 | this.x = this.y = this.width = this.height = 0; |
---|
32 | }; |
---|
33 | dojo.extend(Rect, { |
---|
34 | // getters |
---|
35 | getX: function(){ return this.x; }, |
---|
36 | getY: function(){ return this.y; }, |
---|
37 | getWidth: function(){ return this.width; }, |
---|
38 | getHeight: function(){ return this.height; }, |
---|
39 | // setters |
---|
40 | setX: function(val){ this.x = val; }, |
---|
41 | setY: function(val){ this.y = val; }, |
---|
42 | setWidth: function(val){ this.width = val; }, |
---|
43 | setHeight: function(val){ this.height = val; }, |
---|
44 | // special methods |
---|
45 | getPerimeter: function(){ return 2 * (this.width + this.height); }, |
---|
46 | getArea: function(){ return this.width * this.height; }, |
---|
47 | getCorner: function(){ return {x: this.x + this.width, y: this.y + this.height}; }, |
---|
48 | move: function(x, y){ this.x = x; this.y = y; }, |
---|
49 | makeSquare: function(l){ this.width = this.height = l; }, |
---|
50 | scale: function(s){ this.width *= s; this.height *= s; }, |
---|
51 | pointInside: function(x, y){ |
---|
52 | return this.x <= x && x < (this.x + this.width) && |
---|
53 | this.y <= y && y < (this.y + this.height); |
---|
54 | }, |
---|
55 | assertSquare: function(){ |
---|
56 | if(this.getHeight() != this.getWidth()){ |
---|
57 | throw new Error("NOT A SQUARE!"); |
---|
58 | } |
---|
59 | } |
---|
60 | }); |
---|
61 | |
---|
62 | var aop = dojox.lang.aspect, df = dojox.lang.functional; |
---|
63 | |
---|
64 | // our simple advices |
---|
65 | var TraceArguments = { |
---|
66 | before: function(/*arguments*/){ |
---|
67 | var joinPoint = aop.getContext().joinPoint, |
---|
68 | args = Array.prototype.join.call(arguments, ", "); |
---|
69 | console.log("=> " + joinPoint.targetName + "(" + args + ")"); |
---|
70 | } |
---|
71 | }; |
---|
72 | var TraceReturns = { |
---|
73 | afterReturning: function(retVal){ |
---|
74 | var joinPoint = aop.getContext().joinPoint; |
---|
75 | console.log("<= " + joinPoint.targetName + " returns " + retVal); |
---|
76 | }, |
---|
77 | afterThrowing: function(excp){ |
---|
78 | var joinPoint = aop.getContext().joinPoint; |
---|
79 | console.log("<= " + joinPoint.targetName + " throws: " + excp); |
---|
80 | } |
---|
81 | }; |
---|
82 | |
---|
83 | console.log("create rect1 and call its methods without aspects."); |
---|
84 | var rect1 = new Rect; |
---|
85 | rect1.move(100, 100); |
---|
86 | rect1.makeSquare(200); |
---|
87 | rect1.pointInside(150, 250); |
---|
88 | console.log("perimeter: " + rect1.getPerimeter()); |
---|
89 | console.log("area: " + rect1.getArea()); |
---|
90 | console.log("================================="); |
---|
91 | |
---|
92 | console.log("create rect2, attach advices to the instance, and repeat..."); |
---|
93 | var rect2 = new Rect; |
---|
94 | aop.advise(rect2, /^get/, TraceReturns); |
---|
95 | aop.advise(rect2, [/^set/, "move", "makeSquare"], TraceArguments); |
---|
96 | aop.advise(rect2, "pointInside", [TraceReturns, TraceArguments]); |
---|
97 | rect2.move(100, 100); |
---|
98 | rect2.makeSquare(200); |
---|
99 | rect2.pointInside(150, 250); |
---|
100 | console.log("perimeter: " + rect2.getPerimeter()); |
---|
101 | console.log("area: " + rect2.getArea()); |
---|
102 | console.log("================================="); |
---|
103 | |
---|
104 | console.log("attach advices to the Rect class, and repeat with rect1..."); |
---|
105 | var h1 = aop.advise(Rect, /^get/, TraceReturns); |
---|
106 | var h2 = aop.advise(Rect, [/^set/, "move", "makeSquare"], TraceArguments); |
---|
107 | var h3 = aop.advise(Rect, "pointInside", [TraceReturns, TraceArguments]); |
---|
108 | rect1.move(100, 100); |
---|
109 | rect1.makeSquare(200); |
---|
110 | rect1.pointInside(150, 250); |
---|
111 | console.log("perimeter: " + rect1.getPerimeter()); |
---|
112 | console.log("area: " + rect1.getArea()); |
---|
113 | console.log("================================="); |
---|
114 | |
---|
115 | console.log("remove advices for getters and setters from the class, and repeat with rect1..."); |
---|
116 | aop.unadvise(h1); |
---|
117 | aop.unadvise(h2); |
---|
118 | rect1.move(100, 100); |
---|
119 | rect1.makeSquare(200); |
---|
120 | rect1.pointInside(150, 250); |
---|
121 | console.log("perimeter: " + rect1.getPerimeter()); |
---|
122 | console.log("area: " + rect1.getArea()); |
---|
123 | console.log("================================="); |
---|
124 | |
---|
125 | console.log("repeat with rect2..."); |
---|
126 | rect2.move(100, 100); |
---|
127 | rect2.makeSquare(200); |
---|
128 | rect2.pointInside(150, 250); |
---|
129 | console.log("perimeter: " + rect2.getPerimeter()); |
---|
130 | console.log("area: " + rect2.getArea()); |
---|
131 | console.log("================================="); |
---|
132 | |
---|
133 | console.log("test rect2 with throwing an exception..."); |
---|
134 | aop.advise(rect2, /^assert/, TraceReturns); |
---|
135 | try{ |
---|
136 | rect2.assertSquare(); |
---|
137 | rect2.width = 300; // triggering exception |
---|
138 | rect2.assertSquare(); |
---|
139 | }catch(e){ |
---|
140 | // squelch |
---|
141 | } |
---|
142 | console.log("================================="); |
---|
143 | |
---|
144 | // more complex dynamic tracing advice |
---|
145 | var Trace = function(context){ |
---|
146 | this.name = context.joinPoint.targetName; |
---|
147 | }; |
---|
148 | dojo.extend(Trace, { |
---|
149 | before: function(/*arguments*/){ |
---|
150 | this.args = Array.prototype.join.call(arguments, ", "); |
---|
151 | }, |
---|
152 | afterReturning: function(retVal){ |
---|
153 | var buf = "-- " + this.name + "(" + this.args + ")"; |
---|
154 | if(typeof retVal == "undefined"){ |
---|
155 | // procedure without a return value |
---|
156 | console.log(buf); |
---|
157 | }else{ |
---|
158 | // function with returned value |
---|
159 | console.log(buf + " returns: " + retVal); |
---|
160 | } |
---|
161 | }, |
---|
162 | afterThrowing: function(excp){ |
---|
163 | console.log("-- " + this.name + "(" + this.args + ") throws: " + excp); |
---|
164 | } |
---|
165 | }); |
---|
166 | |
---|
167 | // remove tracing for pointInside |
---|
168 | aop.unadvise(h3); |
---|
169 | |
---|
170 | console.log("create rect3, and trace all its methods..."); |
---|
171 | var rect3 = new Rect; |
---|
172 | aop.advise(rect3, /^\S/, Trace); |
---|
173 | rect3.move(100, 100); |
---|
174 | rect3.makeSquare(200); |
---|
175 | rect3.pointInside(150, 250); |
---|
176 | console.log("perimeter: " + rect3.getPerimeter()); |
---|
177 | console.log("area: " + rect3.getArea()); |
---|
178 | rect3.assertSquare(); |
---|
179 | console.log("================================="); |
---|
180 | |
---|
181 | var TraceAll = function(context, id){ |
---|
182 | this.name = context.joinPoint.targetName; |
---|
183 | this.prefix = dojo.string.pad("", context.depth * 2, "--", true) + "-- #" + (id || 1); |
---|
184 | }; |
---|
185 | dojo.extend(TraceAll, { |
---|
186 | before: function(/*arguments*/){ |
---|
187 | var args = Array.prototype.join.call(arguments, ", "); |
---|
188 | console.log(this.prefix + " => before " + this.name + "(" + args + ")"); |
---|
189 | }, |
---|
190 | around: function(/*arguments*/){ |
---|
191 | var args = Array.prototype.join.call(arguments, ", "); |
---|
192 | console.log(this.prefix + " => around " + this.name + "(" + args + ")"); |
---|
193 | var retVal = aop.proceed.apply(null, arguments); |
---|
194 | console.log(this.prefix + " <= around " + this.name + " returns " + retVal); |
---|
195 | return retVal; // should return a value, if the target returns a value |
---|
196 | }, |
---|
197 | afterReturning: function(retVal){ |
---|
198 | console.log(this.prefix + " <= afterR " + this.name + " returns " + retVal); |
---|
199 | }, |
---|
200 | afterThrowing: function(excp){ |
---|
201 | console.log(this.prefix + " <= afterT " + this.name + " throws: " + excp); |
---|
202 | }, |
---|
203 | after: function(){ |
---|
204 | console.log(this.prefix + " <= after " + this.name); |
---|
205 | } |
---|
206 | }); |
---|
207 | |
---|
208 | console.log("create rect4, and attach two tracer to all its methods..."); |
---|
209 | var rect4 = new Rect; |
---|
210 | aop.advise(rect4, /^\S/, [ |
---|
211 | function(context){ return new TraceAll(context, 1); }, |
---|
212 | function(context){ return new TraceAll(context, 2); } |
---|
213 | ]); |
---|
214 | rect4.move(100, 100); |
---|
215 | rect4.makeSquare(200); |
---|
216 | rect4.pointInside(150, 250); |
---|
217 | console.log("perimeter: " + rect4.getPerimeter()); |
---|
218 | console.log("area: " + rect4.getArea()); |
---|
219 | try{ |
---|
220 | rect4.assertSquare(); |
---|
221 | rect4.width = 300; // triggering exception |
---|
222 | rect4.assertSquare(); |
---|
223 | }catch(e){ |
---|
224 | // squelch |
---|
225 | } |
---|
226 | console.log("================================="); |
---|
227 | |
---|
228 | var TraceTopLevel = function(context){ |
---|
229 | this.name = context.joinPoint.targetName; |
---|
230 | }; |
---|
231 | dojo.extend(TraceTopLevel, { |
---|
232 | before: function(/*arguments*/){ |
---|
233 | var args = Array.prototype.join.call(arguments, ", "); |
---|
234 | console.log("=> " + this.name + "(" + args + ")"); |
---|
235 | }, |
---|
236 | afterReturning: function(retVal){ |
---|
237 | console.log("<= " + this.name + " returns: " + retVal); |
---|
238 | }, |
---|
239 | afterThrowing: function(excp){ |
---|
240 | console.log("<= " + this.name + " throws: " + excp); |
---|
241 | } |
---|
242 | }); |
---|
243 | |
---|
244 | console.log("create rect5, and track only top-level calls..."); |
---|
245 | var rect5 = new Rect; |
---|
246 | aop.advise(rect5, /^\S/, |
---|
247 | function(context){ |
---|
248 | return aop.cflow(context.instance) ? // the advised object |
---|
249 | {} : // do nothing |
---|
250 | new TraceTopLevel(context); // log top level |
---|
251 | } |
---|
252 | ); |
---|
253 | rect5.move(100, 100); |
---|
254 | rect5.makeSquare(200); |
---|
255 | rect5.pointInside(150, 250); |
---|
256 | console.log("perimeter: " + rect5.getPerimeter()); |
---|
257 | console.log("area: " + rect5.getArea()); |
---|
258 | try{ |
---|
259 | rect5.assertSquare(); |
---|
260 | rect5.width = 300; // triggering exception |
---|
261 | rect5.assertSquare(); |
---|
262 | }catch(e){ |
---|
263 | // squelch |
---|
264 | } |
---|
265 | console.log("================================="); |
---|
266 | |
---|
267 | var log = function(){ |
---|
268 | // log the rect1pointInside state |
---|
269 | var dispatcher = "native"; |
---|
270 | if(rect1.pointInside.target){ |
---|
271 | if(rect1.pointInside.advices){ |
---|
272 | dispatcher = "dojox.lang.aspect"; |
---|
273 | }else if(rect1.pointInside._listeners){ |
---|
274 | dispatcher = "dojo.connect"; |
---|
275 | } |
---|
276 | } |
---|
277 | console.log("Dispatcher: " + dispatcher); |
---|
278 | }; |
---|
279 | |
---|
280 | console.log("use dojo.connect() on rect1 to trace..."); |
---|
281 | console.log("Running native method."); |
---|
282 | log(); |
---|
283 | rect1.pointInside(150, 250); |
---|
284 | console.log("Connecting an event processor."); |
---|
285 | h1 = dojo.connect(rect1, "pointInside", function(){ |
---|
286 | var args = Array.prototype.join.call(arguments, ", "); |
---|
287 | console.log("from dojo.connect(): " + args); |
---|
288 | }); |
---|
289 | log(); |
---|
290 | rect1.pointInside(150, 250); |
---|
291 | console.log("Connecting the TraceAll advice."); |
---|
292 | h2 = aop.advise(rect1, "pointInside", TraceAll); |
---|
293 | log(); |
---|
294 | rect1.pointInside(150, 250); |
---|
295 | console.log("Disconnecting the event processor."); |
---|
296 | dojo.disconnect(h1); |
---|
297 | log(); |
---|
298 | rect1.pointInside(150, 250); |
---|
299 | console.log("Disconnecting the advise."); |
---|
300 | aop.unadvise(h2); |
---|
301 | log(); |
---|
302 | rect1.pointInside(150, 250); |
---|
303 | console.log("Connecting the TraceAll advice."); |
---|
304 | h2 = aop.advise(rect1, "pointInside", TraceAll); |
---|
305 | log(); |
---|
306 | rect1.pointInside(150, 250); |
---|
307 | console.log("Connecting an event processor."); |
---|
308 | h1 = dojo.connect(rect1, "pointInside", function(){ |
---|
309 | var args = Array.prototype.join.call(arguments, ", "); |
---|
310 | console.log("from dojo.connect(): " + args); |
---|
311 | }); |
---|
312 | log(); |
---|
313 | rect1.pointInside(150, 250); |
---|
314 | console.log("Disconnecting the advise."); |
---|
315 | aop.unadvise(h2); |
---|
316 | log(); |
---|
317 | rect1.pointInside(150, 250); |
---|
318 | console.log("Disconnecting the event processor."); |
---|
319 | dojo.disconnect(h1); |
---|
320 | log(); |
---|
321 | rect1.pointInside(150, 250); |
---|
322 | console.log("================================="); |
---|
323 | |
---|
324 | console.log("trace all methods of rect1..."); |
---|
325 | h1 = aop.advise(rect1, /^\S/, aop.tracer(true)); |
---|
326 | rect1.move(100, 100); |
---|
327 | rect1.makeSquare(200); |
---|
328 | rect1.pointInside(150, 250); |
---|
329 | console.log("perimeter: " + rect1.getPerimeter()); |
---|
330 | console.log("area: " + rect1.getArea()); |
---|
331 | try{ |
---|
332 | rect1.assertSquare(); |
---|
333 | rect1.width = 300; // triggering exception |
---|
334 | rect1.assertSquare(); |
---|
335 | }catch(e){ |
---|
336 | // squelch |
---|
337 | } |
---|
338 | aop.unadvise(h1); |
---|
339 | console.log("================================="); |
---|
340 | |
---|
341 | console.log("count all get* methods of rect1..."); |
---|
342 | var counter = aop.counter(); |
---|
343 | h1 = aop.advise(rect1, /^get/, counter); |
---|
344 | rect1.move(100, 100); |
---|
345 | rect1.makeSquare(200); |
---|
346 | rect1.pointInside(150, 250); |
---|
347 | console.log("perimeter: " + rect1.getPerimeter()); |
---|
348 | console.log("area: " + rect1.getArea()); |
---|
349 | try{ |
---|
350 | rect1.assertSquare(); |
---|
351 | rect1.width = 300; // triggering exception |
---|
352 | rect1.assertSquare(); |
---|
353 | }catch(e){ |
---|
354 | // squelch |
---|
355 | } |
---|
356 | aop.unadvise(h1); |
---|
357 | console.log("get* methods were called", counter.calls, "times, with", counter.errors, "errors."); |
---|
358 | console.log("================================="); |
---|
359 | |
---|
360 | console.log("time all methods of rect1..."); |
---|
361 | h1 = aop.advise(rect1, /^\S/, aop.timer()); |
---|
362 | rect1.move(100, 100); |
---|
363 | rect1.makeSquare(200); |
---|
364 | rect1.pointInside(150, 250); |
---|
365 | console.log("perimeter: " + rect1.getPerimeter()); |
---|
366 | console.log("area: " + rect1.getArea()); |
---|
367 | try{ |
---|
368 | rect1.assertSquare(); |
---|
369 | rect1.width = 300; // triggering exception |
---|
370 | rect1.assertSquare(); |
---|
371 | }catch(e){ |
---|
372 | // squelch |
---|
373 | } |
---|
374 | aop.unadvise(h1); |
---|
375 | console.log("================================="); |
---|
376 | |
---|
377 | /* |
---|
378 | console.log("profile all methods of rect1..."); |
---|
379 | h1 = aop.advise(rect1, /^\S/, aop.profiler("Profile1")); |
---|
380 | rect1.move(100, 100); |
---|
381 | rect1.makeSquare(200); |
---|
382 | rect1.pointInside(150, 250); |
---|
383 | console.log("perimeter: " + rect1.getPerimeter()); |
---|
384 | console.log("area: " + rect1.getArea()); |
---|
385 | try{ |
---|
386 | rect1.assertSquare(); |
---|
387 | rect1.width = 300; // triggering exception |
---|
388 | rect1.assertSquare(); |
---|
389 | }catch(e){ |
---|
390 | // squelch |
---|
391 | } |
---|
392 | aop.unadvise(h1); |
---|
393 | console.log("================================="); |
---|
394 | */ |
---|
395 | |
---|
396 | var Fibonacci = function(order){ |
---|
397 | if(arguments.length == 1){ |
---|
398 | this.setOrder(order); |
---|
399 | }else{ |
---|
400 | this.offset = 2; |
---|
401 | } |
---|
402 | } |
---|
403 | dojo.extend(Fibonacci, { |
---|
404 | setOrder: function(order){ |
---|
405 | this.offset = order + 1; |
---|
406 | }, |
---|
407 | getOrder: function(){ |
---|
408 | return this.offset - 1; |
---|
409 | }, |
---|
410 | calculate: function(n){ |
---|
411 | if(n < 0){ return 0; } |
---|
412 | if(n == 0){ return 1; } |
---|
413 | return this.calculate(n - 1) + this.calculate(n - this.offset); |
---|
414 | }, |
---|
415 | calculateN: function(n, o){ |
---|
416 | if(n < 0){ return 0; } |
---|
417 | if(n == 0){ return 1; } |
---|
418 | return this.calculateN(n - 1, o) + this.calculateN(n - 1 - o, o); |
---|
419 | } |
---|
420 | }); |
---|
421 | var args = df.listcomp("i for(i = 0; i < 15; ++i)"); |
---|
422 | |
---|
423 | console.log("calculate Fibonacci numbers..."); |
---|
424 | var fib = new Fibonacci; |
---|
425 | h1 = aop.advise(fib, /^calculate/, aop.timer("fib")); |
---|
426 | |
---|
427 | console.log("before memoization"); |
---|
428 | fib.setOrder(0); |
---|
429 | console.log("0-order:", dojo.map(args, dojo.hitch(fib, "calculate"))); |
---|
430 | fib.setOrder(1); |
---|
431 | console.log("1-order:", dojo.map(args, dojo.hitch(fib, "calculate"))); |
---|
432 | fib.setOrder(2); |
---|
433 | console.log("2-order:", dojo.map(args, dojo.hitch(fib, "calculate"))); |
---|
434 | fib.setOrder(3); |
---|
435 | console.log("3-order:", dojo.map(args, dojo.hitch(fib, "calculate"))); |
---|
436 | |
---|
437 | console.log("after memoization"); |
---|
438 | h2 = aop.advise(fib, "calculate", aop.memoizer()); |
---|
439 | h3 = aop.advise(fib, /^set/, aop.memoizerGuard("calculate")); |
---|
440 | fib.setOrder(0); |
---|
441 | console.log("0-order:", dojo.map(args, dojo.hitch(fib, "calculate"))); |
---|
442 | fib.setOrder(1); |
---|
443 | console.log("1-order:", dojo.map(args, dojo.hitch(fib, "calculate"))); |
---|
444 | fib.setOrder(2); |
---|
445 | console.log("2-order:", dojo.map(args, dojo.hitch(fib, "calculate"))); |
---|
446 | fib.setOrder(3); |
---|
447 | console.log("3-order:", dojo.map(args, dojo.hitch(fib, "calculate"))); |
---|
448 | aop.unadvise(h3); |
---|
449 | aop.unadvise(h2); |
---|
450 | |
---|
451 | console.log("before memoization"); |
---|
452 | console.log(fib.calculateN(15, 0)); |
---|
453 | console.log(fib.calculateN(15, 1)); |
---|
454 | console.log(fib.calculateN(15, 2)); |
---|
455 | console.log(fib.calculateN(15, 3)); |
---|
456 | |
---|
457 | console.log("after memoization"); |
---|
458 | h2 = aop.advise(fib, "calculateN", aop.memoizer(function(a, b){ return a + "/" + b; })); |
---|
459 | h3 = aop.advise(fib, /^set/, aop.memoizerGuard("calculate")); |
---|
460 | console.log(fib.calculateN(15, 0)); |
---|
461 | console.log(fib.calculateN(15, 1)); |
---|
462 | console.log(fib.calculateN(15, 2)); |
---|
463 | console.log(fib.calculateN(15, 3)); |
---|
464 | aop.unadvise(h3); |
---|
465 | aop.unadvise(h2); |
---|
466 | |
---|
467 | aop.unadvise(h1); |
---|
468 | |
---|
469 | console.log("================================="); |
---|
470 | }; |
---|
471 | //dojo.addOnLoad(test); |
---|
472 | </script> |
---|
473 | </head> |
---|
474 | <body> |
---|
475 | <p>This test is meant to run with Firebug. Open the console to see the output.</p> |
---|
476 | <p><button onclick="test()">Start</button></p> |
---|
477 | </body> |
---|
478 | </html> |
---|