You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
478 lines
16 KiB
478 lines
16 KiB
<html> |
|
<head> |
|
<title>aspects</title> |
|
<style type="text/css"> |
|
@import "../../../dojo/resources/dojo.css"; |
|
</style> |
|
<script type="text/javascript" src="../../../dojo/dojo.js" data-dojo-config="isDebug:true"></script> |
|
<script type="text/javascript" src="../aspect.js"></script> |
|
<script type="text/javascript" src="../aspect/cflow.js"></script> |
|
<script type="text/javascript" src="../aspect/tracer.js"></script> |
|
<script type="text/javascript" src="../aspect/timer.js"></script> |
|
<script type="text/javascript" src="../aspect/profiler.js"></script> |
|
<script type="text/javascript" src="../aspect/counter.js"></script> |
|
<script type="text/javascript" src="../aspect/memoizer.js"></script> |
|
<script type="text/javascript" src="../aspect/memoizerGuard.js"></script> |
|
<script type="text/javascript"> |
|
dojo.require("dojox.lang.aspect"); |
|
dojo.require("dojox.lang.aspect.cflow"); |
|
dojo.require("dojox.lang.aspect.tracer"); |
|
dojo.require("dojox.lang.aspect.timer"); |
|
dojo.require("dojox.lang.aspect.profiler"); |
|
dojo.require("dojox.lang.aspect.counter"); |
|
dojo.require("dojox.lang.aspect.memoizer"); |
|
dojo.require("dojox.lang.aspect.memoizerGuard"); |
|
dojo.require("dojo.string"); |
|
dojo.require("dojox.lang.functional.listcomp"); |
|
|
|
var test = function(){ |
|
// This is a test class, don't program like this! |
|
var Rect = function(){ |
|
this.x = this.y = this.width = this.height = 0; |
|
}; |
|
dojo.extend(Rect, { |
|
// getters |
|
getX: function(){ return this.x; }, |
|
getY: function(){ return this.y; }, |
|
getWidth: function(){ return this.width; }, |
|
getHeight: function(){ return this.height; }, |
|
// setters |
|
setX: function(val){ this.x = val; }, |
|
setY: function(val){ this.y = val; }, |
|
setWidth: function(val){ this.width = val; }, |
|
setHeight: function(val){ this.height = val; }, |
|
// special methods |
|
getPerimeter: function(){ return 2 * (this.width + this.height); }, |
|
getArea: function(){ return this.width * this.height; }, |
|
getCorner: function(){ return {x: this.x + this.width, y: this.y + this.height}; }, |
|
move: function(x, y){ this.x = x; this.y = y; }, |
|
makeSquare: function(l){ this.width = this.height = l; }, |
|
scale: function(s){ this.width *= s; this.height *= s; }, |
|
pointInside: function(x, y){ |
|
return this.x <= x && x < (this.x + this.width) && |
|
this.y <= y && y < (this.y + this.height); |
|
}, |
|
assertSquare: function(){ |
|
if(this.getHeight() != this.getWidth()){ |
|
throw new Error("NOT A SQUARE!"); |
|
} |
|
} |
|
}); |
|
|
|
var aop = dojox.lang.aspect, df = dojox.lang.functional; |
|
|
|
// our simple advices |
|
var TraceArguments = { |
|
before: function(/*arguments*/){ |
|
var joinPoint = aop.getContext().joinPoint, |
|
args = Array.prototype.join.call(arguments, ", "); |
|
console.log("=> " + joinPoint.targetName + "(" + args + ")"); |
|
} |
|
}; |
|
var TraceReturns = { |
|
afterReturning: function(retVal){ |
|
var joinPoint = aop.getContext().joinPoint; |
|
console.log("<= " + joinPoint.targetName + " returns " + retVal); |
|
}, |
|
afterThrowing: function(excp){ |
|
var joinPoint = aop.getContext().joinPoint; |
|
console.log("<= " + joinPoint.targetName + " throws: " + excp); |
|
} |
|
}; |
|
|
|
console.log("create rect1 and call its methods without aspects."); |
|
var rect1 = new Rect; |
|
rect1.move(100, 100); |
|
rect1.makeSquare(200); |
|
rect1.pointInside(150, 250); |
|
console.log("perimeter: " + rect1.getPerimeter()); |
|
console.log("area: " + rect1.getArea()); |
|
console.log("================================="); |
|
|
|
console.log("create rect2, attach advices to the instance, and repeat..."); |
|
var rect2 = new Rect; |
|
aop.advise(rect2, /^get/, TraceReturns); |
|
aop.advise(rect2, [/^set/, "move", "makeSquare"], TraceArguments); |
|
aop.advise(rect2, "pointInside", [TraceReturns, TraceArguments]); |
|
rect2.move(100, 100); |
|
rect2.makeSquare(200); |
|
rect2.pointInside(150, 250); |
|
console.log("perimeter: " + rect2.getPerimeter()); |
|
console.log("area: " + rect2.getArea()); |
|
console.log("================================="); |
|
|
|
console.log("attach advices to the Rect class, and repeat with rect1..."); |
|
var h1 = aop.advise(Rect, /^get/, TraceReturns); |
|
var h2 = aop.advise(Rect, [/^set/, "move", "makeSquare"], TraceArguments); |
|
var h3 = aop.advise(Rect, "pointInside", [TraceReturns, TraceArguments]); |
|
rect1.move(100, 100); |
|
rect1.makeSquare(200); |
|
rect1.pointInside(150, 250); |
|
console.log("perimeter: " + rect1.getPerimeter()); |
|
console.log("area: " + rect1.getArea()); |
|
console.log("================================="); |
|
|
|
console.log("remove advices for getters and setters from the class, and repeat with rect1..."); |
|
aop.unadvise(h1); |
|
aop.unadvise(h2); |
|
rect1.move(100, 100); |
|
rect1.makeSquare(200); |
|
rect1.pointInside(150, 250); |
|
console.log("perimeter: " + rect1.getPerimeter()); |
|
console.log("area: " + rect1.getArea()); |
|
console.log("================================="); |
|
|
|
console.log("repeat with rect2..."); |
|
rect2.move(100, 100); |
|
rect2.makeSquare(200); |
|
rect2.pointInside(150, 250); |
|
console.log("perimeter: " + rect2.getPerimeter()); |
|
console.log("area: " + rect2.getArea()); |
|
console.log("================================="); |
|
|
|
console.log("test rect2 with throwing an exception..."); |
|
aop.advise(rect2, /^assert/, TraceReturns); |
|
try{ |
|
rect2.assertSquare(); |
|
rect2.width = 300; // triggering exception |
|
rect2.assertSquare(); |
|
}catch(e){ |
|
// squelch |
|
} |
|
console.log("================================="); |
|
|
|
// more complex dynamic tracing advice |
|
var Trace = function(context){ |
|
this.name = context.joinPoint.targetName; |
|
}; |
|
dojo.extend(Trace, { |
|
before: function(/*arguments*/){ |
|
this.args = Array.prototype.join.call(arguments, ", "); |
|
}, |
|
afterReturning: function(retVal){ |
|
var buf = "-- " + this.name + "(" + this.args + ")"; |
|
if(typeof retVal == "undefined"){ |
|
// procedure without a return value |
|
console.log(buf); |
|
}else{ |
|
// function with returned value |
|
console.log(buf + " returns: " + retVal); |
|
} |
|
}, |
|
afterThrowing: function(excp){ |
|
console.log("-- " + this.name + "(" + this.args + ") throws: " + excp); |
|
} |
|
}); |
|
|
|
// remove tracing for pointInside |
|
aop.unadvise(h3); |
|
|
|
console.log("create rect3, and trace all its methods..."); |
|
var rect3 = new Rect; |
|
aop.advise(rect3, /^\S/, Trace); |
|
rect3.move(100, 100); |
|
rect3.makeSquare(200); |
|
rect3.pointInside(150, 250); |
|
console.log("perimeter: " + rect3.getPerimeter()); |
|
console.log("area: " + rect3.getArea()); |
|
rect3.assertSquare(); |
|
console.log("================================="); |
|
|
|
var TraceAll = function(context, id){ |
|
this.name = context.joinPoint.targetName; |
|
this.prefix = dojo.string.pad("", context.depth * 2, "--", true) + "-- #" + (id || 1); |
|
}; |
|
dojo.extend(TraceAll, { |
|
before: function(/*arguments*/){ |
|
var args = Array.prototype.join.call(arguments, ", "); |
|
console.log(this.prefix + " => before " + this.name + "(" + args + ")"); |
|
}, |
|
around: function(/*arguments*/){ |
|
var args = Array.prototype.join.call(arguments, ", "); |
|
console.log(this.prefix + " => around " + this.name + "(" + args + ")"); |
|
var retVal = aop.proceed.apply(null, arguments); |
|
console.log(this.prefix + " <= around " + this.name + " returns " + retVal); |
|
return retVal; // should return a value, if the target returns a value |
|
}, |
|
afterReturning: function(retVal){ |
|
console.log(this.prefix + " <= afterR " + this.name + " returns " + retVal); |
|
}, |
|
afterThrowing: function(excp){ |
|
console.log(this.prefix + " <= afterT " + this.name + " throws: " + excp); |
|
}, |
|
after: function(){ |
|
console.log(this.prefix + " <= after " + this.name); |
|
} |
|
}); |
|
|
|
console.log("create rect4, and attach two tracer to all its methods..."); |
|
var rect4 = new Rect; |
|
aop.advise(rect4, /^\S/, [ |
|
function(context){ return new TraceAll(context, 1); }, |
|
function(context){ return new TraceAll(context, 2); } |
|
]); |
|
rect4.move(100, 100); |
|
rect4.makeSquare(200); |
|
rect4.pointInside(150, 250); |
|
console.log("perimeter: " + rect4.getPerimeter()); |
|
console.log("area: " + rect4.getArea()); |
|
try{ |
|
rect4.assertSquare(); |
|
rect4.width = 300; // triggering exception |
|
rect4.assertSquare(); |
|
}catch(e){ |
|
// squelch |
|
} |
|
console.log("================================="); |
|
|
|
var TraceTopLevel = function(context){ |
|
this.name = context.joinPoint.targetName; |
|
}; |
|
dojo.extend(TraceTopLevel, { |
|
before: function(/*arguments*/){ |
|
var args = Array.prototype.join.call(arguments, ", "); |
|
console.log("=> " + this.name + "(" + args + ")"); |
|
}, |
|
afterReturning: function(retVal){ |
|
console.log("<= " + this.name + " returns: " + retVal); |
|
}, |
|
afterThrowing: function(excp){ |
|
console.log("<= " + this.name + " throws: " + excp); |
|
} |
|
}); |
|
|
|
console.log("create rect5, and track only top-level calls..."); |
|
var rect5 = new Rect; |
|
aop.advise(rect5, /^\S/, |
|
function(context){ |
|
return aop.cflow(context.instance) ? // the advised object |
|
{} : // do nothing |
|
new TraceTopLevel(context); // log top level |
|
} |
|
); |
|
rect5.move(100, 100); |
|
rect5.makeSquare(200); |
|
rect5.pointInside(150, 250); |
|
console.log("perimeter: " + rect5.getPerimeter()); |
|
console.log("area: " + rect5.getArea()); |
|
try{ |
|
rect5.assertSquare(); |
|
rect5.width = 300; // triggering exception |
|
rect5.assertSquare(); |
|
}catch(e){ |
|
// squelch |
|
} |
|
console.log("================================="); |
|
|
|
var log = function(){ |
|
// log the rect1pointInside state |
|
var dispatcher = "native"; |
|
if(rect1.pointInside.target){ |
|
if(rect1.pointInside.advices){ |
|
dispatcher = "dojox.lang.aspect"; |
|
}else if(rect1.pointInside._listeners){ |
|
dispatcher = "dojo.connect"; |
|
} |
|
} |
|
console.log("Dispatcher: " + dispatcher); |
|
}; |
|
|
|
console.log("use dojo.connect() on rect1 to trace..."); |
|
console.log("Running native method."); |
|
log(); |
|
rect1.pointInside(150, 250); |
|
console.log("Connecting an event processor."); |
|
h1 = dojo.connect(rect1, "pointInside", function(){ |
|
var args = Array.prototype.join.call(arguments, ", "); |
|
console.log("from dojo.connect(): " + args); |
|
}); |
|
log(); |
|
rect1.pointInside(150, 250); |
|
console.log("Connecting the TraceAll advice."); |
|
h2 = aop.advise(rect1, "pointInside", TraceAll); |
|
log(); |
|
rect1.pointInside(150, 250); |
|
console.log("Disconnecting the event processor."); |
|
dojo.disconnect(h1); |
|
log(); |
|
rect1.pointInside(150, 250); |
|
console.log("Disconnecting the advise."); |
|
aop.unadvise(h2); |
|
log(); |
|
rect1.pointInside(150, 250); |
|
console.log("Connecting the TraceAll advice."); |
|
h2 = aop.advise(rect1, "pointInside", TraceAll); |
|
log(); |
|
rect1.pointInside(150, 250); |
|
console.log("Connecting an event processor."); |
|
h1 = dojo.connect(rect1, "pointInside", function(){ |
|
var args = Array.prototype.join.call(arguments, ", "); |
|
console.log("from dojo.connect(): " + args); |
|
}); |
|
log(); |
|
rect1.pointInside(150, 250); |
|
console.log("Disconnecting the advise."); |
|
aop.unadvise(h2); |
|
log(); |
|
rect1.pointInside(150, 250); |
|
console.log("Disconnecting the event processor."); |
|
dojo.disconnect(h1); |
|
log(); |
|
rect1.pointInside(150, 250); |
|
console.log("================================="); |
|
|
|
console.log("trace all methods of rect1..."); |
|
h1 = aop.advise(rect1, /^\S/, aop.tracer(true)); |
|
rect1.move(100, 100); |
|
rect1.makeSquare(200); |
|
rect1.pointInside(150, 250); |
|
console.log("perimeter: " + rect1.getPerimeter()); |
|
console.log("area: " + rect1.getArea()); |
|
try{ |
|
rect1.assertSquare(); |
|
rect1.width = 300; // triggering exception |
|
rect1.assertSquare(); |
|
}catch(e){ |
|
// squelch |
|
} |
|
aop.unadvise(h1); |
|
console.log("================================="); |
|
|
|
console.log("count all get* methods of rect1..."); |
|
var counter = aop.counter(); |
|
h1 = aop.advise(rect1, /^get/, counter); |
|
rect1.move(100, 100); |
|
rect1.makeSquare(200); |
|
rect1.pointInside(150, 250); |
|
console.log("perimeter: " + rect1.getPerimeter()); |
|
console.log("area: " + rect1.getArea()); |
|
try{ |
|
rect1.assertSquare(); |
|
rect1.width = 300; // triggering exception |
|
rect1.assertSquare(); |
|
}catch(e){ |
|
// squelch |
|
} |
|
aop.unadvise(h1); |
|
console.log("get* methods were called", counter.calls, "times, with", counter.errors, "errors."); |
|
console.log("================================="); |
|
|
|
console.log("time all methods of rect1..."); |
|
h1 = aop.advise(rect1, /^\S/, aop.timer()); |
|
rect1.move(100, 100); |
|
rect1.makeSquare(200); |
|
rect1.pointInside(150, 250); |
|
console.log("perimeter: " + rect1.getPerimeter()); |
|
console.log("area: " + rect1.getArea()); |
|
try{ |
|
rect1.assertSquare(); |
|
rect1.width = 300; // triggering exception |
|
rect1.assertSquare(); |
|
}catch(e){ |
|
// squelch |
|
} |
|
aop.unadvise(h1); |
|
console.log("================================="); |
|
|
|
/* |
|
console.log("profile all methods of rect1..."); |
|
h1 = aop.advise(rect1, /^\S/, aop.profiler("Profile1")); |
|
rect1.move(100, 100); |
|
rect1.makeSquare(200); |
|
rect1.pointInside(150, 250); |
|
console.log("perimeter: " + rect1.getPerimeter()); |
|
console.log("area: " + rect1.getArea()); |
|
try{ |
|
rect1.assertSquare(); |
|
rect1.width = 300; // triggering exception |
|
rect1.assertSquare(); |
|
}catch(e){ |
|
// squelch |
|
} |
|
aop.unadvise(h1); |
|
console.log("================================="); |
|
*/ |
|
|
|
var Fibonacci = function(order){ |
|
if(arguments.length == 1){ |
|
this.setOrder(order); |
|
}else{ |
|
this.offset = 2; |
|
} |
|
} |
|
dojo.extend(Fibonacci, { |
|
setOrder: function(order){ |
|
this.offset = order + 1; |
|
}, |
|
getOrder: function(){ |
|
return this.offset - 1; |
|
}, |
|
calculate: function(n){ |
|
if(n < 0){ return 0; } |
|
if(n == 0){ return 1; } |
|
return this.calculate(n - 1) + this.calculate(n - this.offset); |
|
}, |
|
calculateN: function(n, o){ |
|
if(n < 0){ return 0; } |
|
if(n == 0){ return 1; } |
|
return this.calculateN(n - 1, o) + this.calculateN(n - 1 - o, o); |
|
} |
|
}); |
|
var args = df.listcomp("i for(i = 0; i < 15; ++i)"); |
|
|
|
console.log("calculate Fibonacci numbers..."); |
|
var fib = new Fibonacci; |
|
h1 = aop.advise(fib, /^calculate/, aop.timer("fib")); |
|
|
|
console.log("before memoization"); |
|
fib.setOrder(0); |
|
console.log("0-order:", dojo.map(args, dojo.hitch(fib, "calculate"))); |
|
fib.setOrder(1); |
|
console.log("1-order:", dojo.map(args, dojo.hitch(fib, "calculate"))); |
|
fib.setOrder(2); |
|
console.log("2-order:", dojo.map(args, dojo.hitch(fib, "calculate"))); |
|
fib.setOrder(3); |
|
console.log("3-order:", dojo.map(args, dojo.hitch(fib, "calculate"))); |
|
|
|
console.log("after memoization"); |
|
h2 = aop.advise(fib, "calculate", aop.memoizer()); |
|
h3 = aop.advise(fib, /^set/, aop.memoizerGuard("calculate")); |
|
fib.setOrder(0); |
|
console.log("0-order:", dojo.map(args, dojo.hitch(fib, "calculate"))); |
|
fib.setOrder(1); |
|
console.log("1-order:", dojo.map(args, dojo.hitch(fib, "calculate"))); |
|
fib.setOrder(2); |
|
console.log("2-order:", dojo.map(args, dojo.hitch(fib, "calculate"))); |
|
fib.setOrder(3); |
|
console.log("3-order:", dojo.map(args, dojo.hitch(fib, "calculate"))); |
|
aop.unadvise(h3); |
|
aop.unadvise(h2); |
|
|
|
console.log("before memoization"); |
|
console.log(fib.calculateN(15, 0)); |
|
console.log(fib.calculateN(15, 1)); |
|
console.log(fib.calculateN(15, 2)); |
|
console.log(fib.calculateN(15, 3)); |
|
|
|
console.log("after memoization"); |
|
h2 = aop.advise(fib, "calculateN", aop.memoizer(function(a, b){ return a + "/" + b; })); |
|
h3 = aop.advise(fib, /^set/, aop.memoizerGuard("calculate")); |
|
console.log(fib.calculateN(15, 0)); |
|
console.log(fib.calculateN(15, 1)); |
|
console.log(fib.calculateN(15, 2)); |
|
console.log(fib.calculateN(15, 3)); |
|
aop.unadvise(h3); |
|
aop.unadvise(h2); |
|
|
|
aop.unadvise(h1); |
|
|
|
console.log("================================="); |
|
}; |
|
//dojo.addOnLoad(test); |
|
</script> |
|
</head> |
|
<body> |
|
<p>This test is meant to run with Firebug. Open the console to see the output.</p> |
|
<p><button onclick="test()">Start</button></p> |
|
</body> |
|
</html>
|
|
|