Advertisement
Guest User

Untitled

a guest
Aug 22nd, 2019
136
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Haxe 5.26 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.   public static var(default, null) FALLTHROUGH: Dynamic = [];
  9.  
  10.   private var base(default, null): Dynamic;
  11.   // `wrap` functions as passed-in (without the continuation) for
  12.   // uniqueness checks
  13.   private var rawWrap(default, null): Array<Dynamic>;
  14.   private var before(default, null): Array<Dynamic>;
  15.   private var beforeSkip(default, null): Array<Int>;
  16.   private var after(default, null): Array<Dynamic>;
  17.  
  18.   public function new(base: Dynamic) {
  19.     this.base = base;
  20.     this.rawWrap = [];
  21.     this.before = [];
  22.     this.beforeSkip = [];
  23.     this.after = [];
  24.   }
  25.  
  26.   // fn: ...Args -> Ret
  27.   public function addReplace(fn: Dynamic): Bool {
  28.     // We nuke everything
  29.     this.base = fn;
  30.     this.rawWrap = [];
  31.     this.before = [];
  32.     this.beforeSkip = [];
  33.     this.after = [];
  34.     return true;
  35.   }
  36.  
  37.   // fn: Wrap -> ...Args -> Ret | FALLTHROUGH
  38.   public function addWrap(fn: Dynamic): Bool {
  39.     if (this.rawWrap.indexOf(fn) >= 0) {
  40.       return false;
  41.     }
  42.  
  43.     // We implement the wrapper as a "before" handler
  44.     var wrapped: Dynamic = this.createPartialRunner(this.before.length, this.after.length);
  45.     var wrapper: Dynamic = function() {
  46.       var args: Array<Dynamic> = untyped __arguments__;
  47.       var newArgs: Array<Dynamic> = [wrapped];
  48.       for (arg in args) {
  49.         newArgs.push(arg);
  50.       }
  51.       return Reflect.callMethod(untyped __this__, fn, newArgs);
  52.     };
  53.  
  54.     this.before.push(wrapper);
  55.     this.beforeSkip.push(this.after.length);
  56.     this.rawWrap.push(fn);
  57.     return true;
  58.   }
  59.  
  60.   // fn: ...Args -> Void
  61.   public function addBefore(fn: Dynamic): Bool {
  62.     if (this.before.indexOf(fn) >= 0) {
  63.       return false;
  64.     }
  65.     this.before.push(fn);
  66.     this.beforeSkip(-1);
  67.     return true;
  68.   }
  69.  
  70.   // fn: ...Args -> Ret | FALLTHROUGH
  71.   public function addShortCircuit(fn: Dynamic): Bool {
  72.     if (this.before.indexOf(fn) >= 0) {
  73.       return false;
  74.     }
  75.     this.before.push(fn);
  76.     this.beforeSkip(this.after.length);
  77.   }
  78.  
  79.   // fn: ...Args, Ret -> Void
  80.   public function addAfter(fn: Dynamic): Bool {
  81.     if (this.after.indexOf(fn) >= 0) {
  82.       return false;
  83.     }
  84.     this.after.push(fn);
  85.     return true;
  86.   }
  87.  
  88.   public function createRunner(): Dynamic {
  89.     return function(): Dynamic {
  90.       return HandlerStore.run(this, state, this.before.length, this.after.length, untyped __this__, untyped __arguments__);
  91.     };
  92.   }
  93.  
  94.   public function createPartialRunner(beforeFrom: Int, afterUntil: Int) {
  95.     return function(): Dynamic {
  96.       return HandlerStore.run(this, state, beforeFrom, afterUntil, untyped __this__, untyped __arguments__);
  97.     };
  98.   }
  99.  
  100.   public static function getOrCreate(o: Dynamic, field: String): HandlerStore {
  101.     var fn: Dynamic = Reflect.field(o, field);
  102.     var store: Null<HandlerStore> = Reflect.field(fn, HandlerStore.STORE_SYMBOL);
  103.     if (store == null) {
  104.       store = new HandlerStore(fn);
  105.       fn = store.createRunner();
  106.       Reflect.setField(fn, HandlerStore.STORE_SYMBOL, store);
  107.       Reflect.setField(o, field, fn);
  108.     }
  109.     return store;
  110.   }
  111.  
  112.   private static function run(store: HandlerStore, beforeFrom: Int, afterUntil: Int, ctx: Dynamic, args: Array<Dynamic>): Dynamic {
  113.     var result: Dynamic;
  114.     var afterFrom: Int = -1;
  115.  
  116.     // Execute the "before" handlers. If a handler short-circuits, we save the
  117.     // returned result and we skip execution to the corresponding "after" handler.
  118.     while (beforeFrom-- > 0) {
  119.       var handler: Dynamic = store.before[beforeFrom];
  120.       var beforeResult: Dynamic = Reflect.callMethod(ctx, handler, args);
  121.  
  122.       afterFrom = store.beforeSkip[beforeFrom];
  123.       if(afterFrom < 0) continue;
  124.       // The instanceof check is to avoid coercing comparisons
  125.       if(__instanceof__(beforeResult, Array) && beforeResult == FALLTHROUGH) continue;
  126.  
  127.       result = beforeResult;
  128.       break;
  129.     }
  130.  
  131.     // Execute the base function, if it wasn't short-circuited
  132.     if (afterFrom < 0) {
  133.       afterFrom = 0;
  134.       result = Reflect.callMethod(ctx, store.base, args);
  135.     }
  136.  
  137.     // Execute the "after" handlers;
  138.     args.push(result);
  139.     for (i in afterFrom...afterUntil) {
  140.       var handler: Dynamic = store.after[i];
  141.       result = Reflect.callMethod(ctx, handler, args);
  142.     }
  143.  
  144.     return result;
  145.   }
  146.  
  147.   /**
  148.    * Returns a wrapper calling `fn` with the context as the first argument.
  149.    */
  150.   public static function explicitContext(fn: Dynamic): Dynamic {
  151.     return function() {
  152.       var ctx: Dynamic = untyped __this__;
  153.       var args: Array<Dynamic> = untyped __arguments__;
  154.       var newArgs: Array<Dynamic> = [ctx];
  155.       for (arg in args) {
  156.         newArgs.push(arg);
  157.       }
  158.       return Reflect.callMethod(ctx, fn, newArgs);
  159.     };
  160.   }
  161.  
  162.   /**
  163.    * Returns a wrapper calling `fn` without the first argument.
  164.    */
  165.   public static function skipFirst(fn: Dynamic): Dynamic {
  166.     return function() {
  167.       var args: Array<Dynamic> = untyped __arguments__;
  168.       var newArgs: Array<Dynamic> = [];
  169.       for (i in 1...args.length) {
  170.         newArgs.push(args[i]);
  171.       }
  172.       return Reflect.callMethod(untyped __this__, fn, args);
  173.     };
  174.   }
  175. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement