Advertisement
Guest User

Untitled

a guest
Aug 21st, 2019
121
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Haxe 4.56 KB | None | 0 0
  1. private typedef BeforeResult = {
  2.     ret: Dynamic,
  3.     skip: Int,
  4. };
  5.  
  6. class HandlerStore {
  7.   public static inline var STORE_SYMBOL: String = "__pmStore";
  8.  
  9.   private var base(default, null): Dynamic;
  10.   // `wrap` functions as passed-in (without the continuation) for
  11.   // uniqueness checks
  12.   private var rawWrap(default, null): Array<Dynamic>;
  13.   private var before(default, null): Array<Dynamic>;
  14.   private var after(default, null): Array<Dynamic>;
  15.  
  16.   public function new(base: Dynamic) {
  17.     this.base = base;
  18.     this.rawWrap = [];
  19.     this.before = [];
  20.     this.after = [];
  21.   }
  22.  
  23.   public function addReplace(fn: Dynamic): Bool {
  24.     // We nuke everything
  25.     this.base = fn;
  26.     this.rawWrap = [];
  27.     this.before = [];
  28.     this.after = [];
  29.     return true;
  30.   }
  31.  
  32.   public function addWrap(fn: Dynamic): Bool {
  33.     if (this.rawWrap.indexOf(fn) >= 0) {
  34.       return false;
  35.     }
  36.  
  37.     // We implement the wrapper as a "before" handler
  38.     var afterUntil = this.after.length;
  39.     var wrapped: Dynamic = this.createPartialRunner(this.before.length, afterUntil);
  40.     var wrapper: Dynamic = function() {
  41.       // Here I would prefer (...args, wrapped) instead of (wrapped, ...args)
  42.       // but I don't want to alter the interface ^^
  43.       var args: Array<Dynamic> = untyped __arguments__;
  44.       args.unshift(wrapped);
  45.       var result = Reflect.callMethod(untyped __this__, fn, args);
  46.       return { ret: result, skip: afterUntil };
  47.     };
  48.  
  49.     this.wrap.push(wrapper);
  50.     this.rawWrap.push(fn);
  51.     return true;
  52.   }
  53.  
  54.   public function addBefore(fn: Dynamic): Bool {
  55.     if (this.before.indexOf(fn) >= 0) {
  56.       return false;
  57.     }
  58.     this.before.push(fn);
  59.     return true;
  60.   }
  61.  
  62.   public function addAfter(fn: Dynamic): Bool {
  63.     if (this.after.indexOf(fn) >= 0) {
  64.       return false;
  65.     }
  66.     this.after.push(fn);
  67.     return true;
  68.   }
  69.  
  70.   public function createRunner(): Dynamic {
  71.     return function(): Dynamic {
  72.       return HandlerStore.run(this, state, this.before.length, this.after.length, untyped __this__, untyped __arguments__);
  73.     };
  74.   }
  75.  
  76.   public function createPartialRunner(beforeFrom: Int, afterUntil: Int) {
  77.     return function(): Dynamic {
  78.       return HandlerStore.run(this, state, beforeFrom, afterUntil, untyped __this__, untyped __arguments__);
  79.     };
  80.   }
  81.  
  82.   public static function getOrCreate(o: Dynamic, field: String): HandlerStore {
  83.     var fn: Dynamic = Reflect.field(o, field);
  84.     var store: Null<HandlerStore> = Reflect.field(fn, HandlerStore.STORE_SYMBOL);
  85.     if (store == null) {
  86.       store = new HandlerStore(fn);
  87.       fn = store.createRunner();
  88.       Reflect.setField(fn, HandlerStore.STORE_SYMBOL, store);
  89.       Reflect.setField(o, field, fn);
  90.     }
  91.     return store;
  92.   }
  93.  
  94.   private static function run(store: HandlerStore, beforeFrom: Int, afterUntil: Int, ctx: Dynamic, args: Array<Dynamic>): Dynamic {
  95.     var result: Dynamic;
  96.     var afterFrom: Int = -1;
  97.  
  98.     // Execute the "before" handlers. If a handler short-circuits, we save the
  99.     // returned result and we skip execution to the corresponding "after" handler.
  100.     while (beforeFrom-- > 0) {
  101.       var handler: Dynamic = store.before[beforeFrom];
  102.       var beforeResult: Null<BeforeResult> = Reflect.callMethod(ctx, handler, args);
  103.       if (beforeResult != null) {
  104.         afterFrom = beforeResult.skip;
  105.         result = beforeResult.ret;
  106.         break;
  107.       }
  108.     }
  109.  
  110.     // Execute the base function, if it wasn't short-circuited
  111.     if (afterFrom < 0) {
  112.       afterFrom = 0;
  113.       result = Reflect.callMethod(ctx, store.base, args);
  114.     }
  115.  
  116.     // Execute the "after" handlers, threading the return value
  117.     var retValuePos = args.length;
  118.     for (i in afterFrom...afterUntil) {
  119.       var handler: Dynamic = store.after[i];
  120.       args[retValuePos] = result;
  121.       result = Reflect.callMethod(ctx, handler, args);
  122.     }
  123.  
  124.     return result;
  125.   }
  126.  
  127.   /**
  128.    * Returns a wrapper calling `fn` with the context as the first argument.
  129.    */
  130.   public static function explicitContext(fn: Dynamic): Dynamic {
  131.     return function() {
  132.       var ctx: Dynamic = untyped __this__;
  133.       var args: Array<Dynamic> = untyped __arguments__;
  134.       args.unshift(ctx);
  135.       return Reflect.callMethod(ctx, fn, args);
  136.     };
  137.   }
  138.  
  139.   /**
  140.    * Returns a wrapper calling `fn` without the first argument.
  141.    */
  142.   public static function skipFirst(fn: Dynamic): Dynamic {
  143.     return function() {
  144.       var args: Array<Dynamic> = untyped __arguments__;
  145.       args.shift();
  146.       return Reflect.callMethod(untyped __this__, fn, args);
  147.     };
  148.   }
  149. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement