Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- private typedef BeforeResult = {
- ret: Dynamic,
- skip: Int,
- };
- class HandlerStore {
- public static inline var STORE_SYMBOL: String = "__pmStore";
- private var base(default, null): Dynamic;
- // `wrap` functions as passed-in (without the continuation) for
- // uniqueness checks
- private var rawWrap(default, null): Array<Dynamic>;
- private var before(default, null): Array<Dynamic>;
- private var after(default, null): Array<Dynamic>;
- public function new(base: Dynamic) {
- this.base = base;
- this.rawWrap = [];
- this.before = [];
- this.after = [];
- }
- public function addReplace(fn: Dynamic): Bool {
- // We nuke everything
- this.base = fn;
- this.rawWrap = [];
- this.before = [];
- this.after = [];
- return true;
- }
- public function addWrap(fn: Dynamic): Bool {
- if (this.rawWrap.indexOf(fn) >= 0) {
- return false;
- }
- // We implement the wrapper as a "before" handler
- var wrapped: Dynamic = this.createRunnerShortCircuit(this.before.length, this.after.length);
- var wrapper: Dynamic = function() {
- // Here I would prefer (...args, wrapped) instead of (wrapped, ...args)
- // but I don't want to alter the interface ^^
- var args: Array<Dynamic> = untyped __arguments__;
- args.unshift(wrapped);
- return Reflect.callMethod(untyped __this__, fn, args);
- };
- this.wrap.push(wrapper);
- this.rawWrap.push(fn);
- return true;
- }
- public function addBefore(fn: Dynamic): Bool {
- if (this.before.indexOf(fn) >= 0) {
- return false;
- }
- this.before.push(fn);
- return true;
- }
- public function addAfter(fn: Dynamic): Bool {
- if (this.after.indexOf(fn) >= 0) {
- return false;
- }
- this.after.push(fn);
- return true;
- }
- public function createRunner(): Dynamic {
- return function(): Dynamic {
- return HandlerStore.run(this, state, this.before.length, this.after.length, untyped __this__, untyped __arguments__);
- };
- }
- public function createRunnerShortCircuit(beforeFrom: Int, afterUntil: Int) {
- return function(): Dynamic {
- var result = HandlerStore.run(this, state, beforeFrom, afterUntil, untyped __this__, untyped __arguments__);
- return { ret: result, skip: afterUntil };
- };
- }
- public static function getOrCreate(o: Dynamic, field: String): HandlerStore {
- var fn: Dynamic = Reflect.field(o, field);
- var store: Null<HandlerStore> = Reflect.field(fn, HandlerStore.STORE_SYMBOL);
- if (store == null) {
- store = new HandlerStore(fn);
- fn = store.createRunner();
- Reflect.setField(fn, HandlerStore.STORE_SYMBOL, store);
- Reflect.setField(o, field, fn);
- }
- return store;
- }
- private static function run(store: HandlerStore, beforeFrom: Int, afterUntil: Int, ctx: Dynamic, args: Array<Dynamic>): Dynamic {
- var result: Dynamic;
- var afterFrom: Int = -1;
- // Execute the "before" handlers. If a handler short-circuits, we save the
- // returned result and we skip execution to the corresponding "after" handler.
- while (beforeFrom-- > 0) {
- var handler: Dynamic = store.before[beforeFrom];
- var beforeResult: Null<BeforeResult> = Reflect.callMethod(ctx, handler, args);
- if (beforeResult != null) {
- afterFrom = beforeResult.skip;
- result = beforeResult.ret;
- break;
- }
- }
- // Execute the base function, if it wasn't short-circuited
- if (afterFrom < 0) {
- afterFrom = 0;
- result = Reflect.callMethod(ctx, store.base, args);
- }
- // Execute the "after" handlers, threading the return value
- var retValuePos = args.length;
- for (i in afterFrom...afterUntil) {
- var handler: Dynamic = store.after[i];
- args[retValuePos] = result;
- result = Reflect.callMethod(ctx, handler, args);
- }
- return result;
- }
- /**
- * Returns a wrapper calling `fn` with the context as the first argument.
- */
- public static function explicitContext(fn: Dynamic): Dynamic {
- return function() {
- var ctx: Dynamic = untyped __this__;
- var args: Array<Dynamic> = untyped __arguments__;
- args.unshift(ctx);
- return Reflect.callMethod(ctx, fn, args);
- };
- }
- /**
- * Returns a wrapper calling `fn` without the first argument.
- */
- public static function skipFirst(fn: Dynamic): Dynamic {
- return function() {
- var args: Array<Dynamic> = untyped __arguments__;
- args.shift();
- return Reflect.callMethod(untyped __this__, fn, args);
- };
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement