SHARE
TWEET

Untitled

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