├── .gitignore ├── README.md ├── dev-resources └── public │ ├── clairvoyant.js │ └── index.html ├── dev ├── user.clj └── user.cljs ├── project.clj ├── resources └── public │ └── clairvoyant.js └── src └── clairvoyant ├── core.clj └── core.cljs /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Leiningen template 3 | pom.xml 4 | pom.xml.asc 5 | *jar 6 | /lib/ 7 | /classes/ 8 | /target/ 9 | /checkouts/ 10 | .lein-deps-sum 11 | .lein-repl-history 12 | .lein-plugins/ 13 | .lein-failures 14 | .nrepl-port 15 | clairvoyant.iml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clairvoyant 2 | 3 | Clairvoyant is a library that offers flexible tracing for ClojureScript. 4 | 5 | ## Usage 6 | 7 | Add Clairvoyant to your project `:dependencies`. 8 | 9 | ![Clojars Project](http://clojars.org/spellhouse/clairvoyant/latest-version.svg) 10 | 11 | This library is alpha quality. 12 | 13 | ## Example 14 | 15 | ```clj 16 | (ns foo 17 | (:require [clairvoyant.core :as trace :include-macros true])) 18 | 19 | 20 | (trace/trace-forms {:tracer trace/default-tracer} 21 | (defn add [a b] 22 | (+ a b))) 23 | 24 | (add 1 2) 25 | ``` 26 | 27 | Check your JavaScript console and look for a grouped message titled 28 | `foo/add [a b]`. Open it and you should see 29 | 30 | ``` 31 | ▾ bindings 32 | | ▾ x 1 33 | | | 1 34 | | ▾ y 2 35 | | | 2 36 | ▸ => 3 37 | ``` 38 | 39 | For convenience you may omit the options map to `trace-forms`. 40 | 41 | ## Design 42 | 43 | Clairvoyant is based on two concepts: tracers and source code 44 | transformation. Although this is not a dramatic departure from similar 45 | tools, Clairvoyant maintains an emphasis on extensibility which affords 46 | the programmer finer control over the tracing process. This 47 | extensibility is provided by Clojure's protocols and multimethods. 48 | 49 | 50 | ### Tracers 51 | 52 | Tracers are defined around a small set of protocols which 53 | correspond to key points in a trace's lifecycle: `ITraceEnter`, 54 | `ITraceError`, and `ITraceExit`. Respectively, these align with when 55 | a trace begins, when an error is encountered, and when a trace has 56 | ended. The locations of these points may vary from form to form but in 57 | general they're positioned to strategically. 58 | 59 | At each point in the trace the tracer will receive data in the form of 60 | a map containing at the least the following information. 61 | 62 | Key | Description | Example 63 | ---------|----------------------------------|-------------- 64 | `:op` | The form operator | `fn` 65 | `:form` | The form being traced | `(fn [x] x)` 66 | `:ns` | The form's originating namespace | `foo` 67 | 68 | More information about trace data can be found in the 69 | [Trace Data](#trace-data) section. 70 | 71 | To picture how this looks consider this simplified example of the 72 | tracing life cycle for the `defn` form. 73 | 74 | 75 | ```clj 76 | (trace/trace-forms 77 | {:tracer default-tracer} 78 | (defn example [x] 79 | ;; TRACE ENTER 80 | ;; TRACE ERROR (START) 81 | {:pre [(even? x)]} 82 | (+ x x) 83 | ;; TRACE ERROR (END) 84 | ;; TRACE EXIT 85 | )) 86 | 87 | (example 2) 88 | (example 1) 89 | ``` 90 | 91 | In the first call to `example`, the tracer's `ITraceEnter` function is 92 | called with the following trace data: 93 | 94 | ```clj 95 | {:name 'example 96 | :ns 'foo 97 | :form '(defn example [x] {:pre [(even? x)]} (+ x x)) 98 | :arglist '[x] 99 | :anonymous? false} 100 | ``` 101 | 102 | Notice this occurs _before_ the precondition is evaluated. This is 103 | useful because it means the function's inputs are visible even when the 104 | condition fails which can be useful for debugging. Under the hood the 105 | underlying `fn` form is transformed to make this possible. 106 | 107 | Before the function returns, the tracer's `ITraceExit` function is 108 | called with the same trace data as before, however, it contains one 109 | additional bit of information; the exit value. 110 | 111 | ```clj 112 | {... 113 | :exit 4 114 | ...} 115 | ``` 116 | 117 | In the second call to `example` the tracer's `ITraceEnter` function 118 | would still be called containing similar trace data from the first 119 | example. But when the precondition is evaluated, it will raise an 120 | `AssertionError`. This is when the tracer's `ITraceError` will be 121 | called. As with the exit trace data this will contain the same data 122 | information from the trace entry point but will contain a key for the 123 | error. 124 | 125 | ```clj 126 | {... 127 | :error AssertionError 128 | ...} 129 | ``` 130 | 131 | #### Custom tracers 132 | 133 | To create a custom tracer implement at least one of the protocols for 134 | the trace life cycle. The source for `clairvoyant.core/default-tracer` 135 | can be used as a reference point. Remember, the behavior of a tracer 136 | is not necessarily tied to logging to the console or printing to the 137 | screen. Since the trace values are just data the barrier to creativity 138 | is low. 139 | 140 | 141 | ### Source code transformation 142 | 143 | `clairvoyant.core/trace-forms` is the sole macro used for source code 144 | trasnformation. It walks each form given to it delegating to the public 145 | `trace-form` multimethod that is responsible for returning the transformed 146 | form containing the tracer hook points. To ensure a high level of control 147 | Clairvoyant avoids macroexpansion. This is important because it allows the 148 | programmer to trace any form they wish rather than just the special forms. 149 | 150 | #### Conditional tracing 151 | 152 | If you want to remove tracing on production builds, 153 | `clairvoyant.core/trace-forms` will not add tracing when the `:elide-asserts` 154 | option (under `:compiler` options in your `project.clj` file) is set to true. 155 | Therefore if you set this option in your prod builds you do not need to remove 156 | `clairvoyant.core/trace-forms` from your source. This technique was taken from 157 | the [reagent project](https://github.com/reagent-project/reagent) 158 | 159 | ## Trace data 160 | 161 | ### `defn`, `fn`, `fn*` 162 | 163 | Key | Description | Example 164 | ---------------|------------------------------------------|--------- 165 | `:name` | The function's name (if provided) | `add` 166 | `:arglist` | The function's signature | `[a b]` 167 | `:anonymous?` | Whether or not the function is anonymous | `false` 168 | 169 | ### `defmethod` 170 | 171 | Key | Description | Example 172 | -----------------|------------------------------------|------------------- 173 | `:name` | The multimethod's name | `+` 174 | `:arglist` | The methods's signature | `[a b]` 175 | `:dispatch-val` | The dispatch value | `[Number Number]` 176 | 177 | 178 | ### `reify`, `extend-type`, `extend-protocol` 179 | 180 | Key | Description | Example 181 | -------------|--------------------------------------|------------------------ 182 | `:protocol` | The protocol's name (fully resolved) | `clojure.core/ILookup` 183 | `:name` | The functions's name | `-lookup` 184 | `:arglist` | The function's signature | `[a b]` 185 | 186 | 187 | ## What about Clojure? 188 | 189 | This library was born out of frustration with `println` debugging in 190 | ClojureScript. For the moment it will remain a ClojureScript project but 191 | that shouldn't be for long. 192 | 193 | ## License 194 | 195 | Copyright © 2014 Christopher Joel Holdbrooks 196 | 197 | Distributed under the Eclipse Public License either version 1.0 or (at 198 | your option) any later version. 199 | -------------------------------------------------------------------------------- /dev-resources/public/clairvoyant.js: -------------------------------------------------------------------------------- 1 | goog.addDependency("base.js", ['goog'], []); 2 | goog.addDependency("../cljs/core.js", ['cljs.core'], ['goog.string', 'goog.object', 'goog.string.StringBuffer', 'goog.array']); 3 | goog.addDependency("../clojure/browser/event.js", ['clojure.browser.event'], ['cljs.core', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.events']); 4 | goog.addDependency("../clojure/browser/net.js", ['clojure.browser.net'], ['goog.net.xpc.CfgFields', 'goog.net.XhrIo', 'goog.json', 'goog.Uri', 'cljs.core', 'goog.net.EventType', 'goog.net.xpc.CrossPageChannel', 'clojure.browser.event']); 5 | goog.addDependency("../weasel/impls/websocket.js", ['weasel.impls.websocket'], ['cljs.core', 'clojure.browser.net', 'goog.net.WebSocket', 'clojure.browser.event']); 6 | goog.addDependency("../cljs/reader.js", ['cljs.reader'], ['goog.string', 'cljs.core', 'goog.string.StringBuffer']); 7 | goog.addDependency("../weasel/repl.js", ['weasel.repl'], ['weasel.impls.websocket', 'cljs.core', 'clojure.browser.net', 'cljs.reader', 'clojure.browser.event']); 8 | goog.addDependency("../clairvoyant/core.js", ['clairvoyant.core'], ['cljs.core']); 9 | goog.addDependency("../user.js", ['user'], ['weasel.repl', 'cljs.core', 'clairvoyant.core']); -------------------------------------------------------------------------------- /dev-resources/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /dev/user.clj: -------------------------------------------------------------------------------- 1 | (ns user 2 | (:require 3 | [weasel.repl.websocket] 4 | [cemerick.piggieback])) 5 | 6 | (defn ws-repl [] 7 | (cemerick.piggieback/cljs-repl 8 | :repl-env (weasel.repl.websocket/repl-env :ip "0.0.0.0" :port 9091))) 9 | 10 | (defn go-repl [] 11 | (do (require '[clairvoyant.core :reload true]) 12 | (ws-repl))) 13 | -------------------------------------------------------------------------------- /dev/user.cljs: -------------------------------------------------------------------------------- 1 | (ns user 2 | (:require 3 | [weasel.repl :as ws-repl] 4 | [clairvoyant.core :as trace :include-macros true])) 5 | 6 | (ws-repl/connect "ws://localhost:9091" :verbose true) 7 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (require '[clojure.java.shell]) 2 | (require '[clojure.string]) 3 | 4 | (def VERSION 5 | (do (-> (clojure.java.shell/sh "git" "describe" "--match" "v0.0") 6 | (:out) 7 | (.trim) 8 | (subs 1)))) 9 | 10 | (defproject spellhouse/clairvoyant VERSION 11 | :description "ClojureScript tracing library" 12 | :url "http://github.com/spellhouse/clairvoyant" 13 | :license {:name "Eclipse Public License" 14 | :url "http://www.eclipse.org/legal/epl-v10.html"} 15 | 16 | :dependencies 17 | [[org.clojure/clojure "1.6.0" :scope "provided"] 18 | [org.clojure/clojurescript "0.0-2322" :scope "provided"]] 19 | 20 | :clean-targets 21 | ^{:protect false} ["dev-resources/public/out" 22 | "resources/public/out" 23 | "target"] 24 | 25 | :cljsbuild 26 | {:builds 27 | {:app {:source-paths ["src"] 28 | :compiler 29 | {:output-to "dev-resources/public/clairvoyant.js" 30 | :output-dir "dev-resources/public/out/" 31 | :optimizations :none 32 | :pretty-print true}}}} 33 | 34 | :aliases 35 | {"auto-build" ~(clojure.string/split 36 | "do cljsbuild clean, cljsbuild auto" 37 | #"\s+")} 38 | 39 | :release-tasks 40 | [["clean"] 41 | ["with-profiles" 42 | "-dev,+release" 43 | "cljsbuild" "once"] 44 | ["jar"]] 45 | 46 | :profiles 47 | {:dev 48 | {:dependencies 49 | [[com.cemerick/piggieback "0.1.3"] 50 | [weasel "0.4.0-SNAPSHOT"]] 51 | 52 | :plugins 53 | [[lein-cljsbuild "1.0.3"]] 54 | 55 | :source-paths 56 | ["dev"] 57 | 58 | :repl-options 59 | {:nrepl-middleware 60 | [cemerick.piggieback/wrap-cljs-repl]} 61 | 62 | :cljsbuild 63 | {:builds {:app {:source-paths ["dev"]}}}} 64 | 65 | :release 66 | {:cljsbuild 67 | {:jar true 68 | :builds 69 | {:app {:compiler 70 | {:output-to "resources/public/clairvoyant.js" 71 | :output-dir "resources/public/out/" 72 | :optimizations :advanced 73 | :pretty-print false}}}}}}) 74 | -------------------------------------------------------------------------------- /resources/public/clairvoyant.js: -------------------------------------------------------------------------------- 1 | if(typeof Math.imul == "undefined" || (Math.imul(0xffffffff,5) == 0)) { 2 | Math.imul = function (a, b) { 3 | var ah = (a >>> 16) & 0xffff; 4 | var al = a & 0xffff; 5 | var bh = (b >>> 16) & 0xffff; 6 | var bl = b & 0xffff; 7 | // the shift by 0 fixes the sign on the high part 8 | // the final |0 converts the unsigned value into a signed value 9 | return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0)|0); 10 | } 11 | } 12 | 13 | ;(function(){ 14 | var g; 15 | function p(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if("function"== 16 | b&&"undefined"==typeof a.call)return"object";return b}var ca="closure_uid_"+(1E9*Math.random()>>>0),da=0;function fa(a,b){for(var c in a)b.call(void 0,a[c],c,a)};function ga(a,b){null!=a&&this.append.apply(this,arguments)}ga.prototype.Ha="";ga.prototype.append=function(a,b,c){this.Ha+=a;if(null!=b)for(var d=1;d>>16&65535)*d+c*(b>>>16&65535)<<16>>>0)|0};function ub(a){a=tb(a,3432918353);return tb(a<<15|a>>>-15,461845907)}function vb(a,b){var c=a^b;return tb(c<<13|c>>>-13,5)+3864292196} 33 | function wb(a,b){var c=a^b,c=tb(c^c>>>16,2246822507),c=tb(c^c>>>13,3266489909);return c^c>>>16}var xb={},yb=0;function zb(a){255>2)} 36 | function Db(a,b){if(r(Eb.a?Eb.a(a,b):Eb.call(null,a,b)))return 0;var c=r(a.Y)?!1:!0;if(r(c?b.Y:c))return-1;if(r(a.Y)){if(!r(b.Y))return 1;c=Fb.a?Fb.a(a.Y,b.Y):Fb.call(null,a.Y,b.Y);return 0===c?Fb.a?Fb.a(a.name,b.name):Fb.call(null,a.name,b.name):c}return Fb.a?Fb.a(a.name,b.name):Fb.call(null,a.name,b.name)}function K(a,b,c,d,e){this.Y=a;this.name=b;this.Aa=c;this.Da=d;this.Z=e;this.h=2154168321;this.o=4096}g=K.prototype;g.C=function(a,b){return J(b,this.Aa)}; 37 | g.D=function(){var a=this.Da;return null!=a?a:this.Da=a=Cb(this)};g.F=function(a,b){return new K(this.Y,this.name,this.Aa,this.Da,b)};g.G=function(){return this.Z};g.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return I.c(c,this,null);case 3:return I.c(c,this,d)}throw Error("Invalid arity: "+arguments.length);};a.a=function(a,c){return I.c(c,this,null)};a.c=function(a,c,d){return I.c(c,this,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(B(b)))}; 38 | g.e=function(a){return I.c(a,this,null)};g.a=function(a,b){return I.c(a,this,b)};g.A=function(a,b){return b instanceof K?this.Aa===b.Aa:!1};g.toString=function(){return this.Aa}; 39 | var Gb=function(){function a(a,b){var c=null!=a?""+z.e(a)+"/"+z.e(b):b;return new K(a,b,c,null,null)}function b(a){return a instanceof K?a:c.a(null,a)}var c=null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,c);case 2:return a.call(this,c,e)}throw Error("Invalid arity: "+arguments.length);};c.e=b;c.a=a;return c}(); 40 | function L(a){if(null==a)return null;if(a&&(a.h&8388608||a.Fb))return a.H(null);if(a instanceof Array||"string"===typeof a)return 0===a.length?null:new Hb(a,0);if(t(ab,a))return bb(a);throw Error(""+z.e(a)+" is not ISeqable");}function N(a){if(null==a)return null;if(a&&(a.h&64||a.Sa))return a.S(null);a=L(a);return null==a?null:G(a)}function O(a){return null!=a?a&&(a.h&64||a.Sa)?a.U(null):(a=L(a))?H(a):P:P}function Q(a){return null==a?null:a&&(a.h&128||a.eb)?a.$(null):L(O(a))} 41 | var Eb=function(){function a(a,b){return null==a?null==b:a===b||Za(a,b)}var b=null,c=function(){function a(b,d,k){var l=null;2b?1:ah?1:c.j(a,b,f,0)}var c=null,c=function(c,e,f,h){switch(arguments.length){case 2:return b.call(this,c,e);case 4:return a.call(this,c,e,f,h)}throw Error("Invalid arity: "+arguments.length);};c.a=b;c.j=a;return c}(),Y=function(){function a(a,b,c){for(c=L(c);;)if(c){b=a.a?a.a(b,N(c)):a.call(null,b,N(c));if(Lb(b))return Ra(b);c=Q(c)}else return b} 79 | function b(a,b){var c=L(b);return c?nc.c?nc.c(a,N(c),Q(c)):nc.call(null,a,N(c),Q(c)):a.r?a.r():a.call(null)}var c=null,c=function(c,e,f){switch(arguments.length){case 2:return b.call(this,c,e);case 3:return a.call(this,c,e,f)}throw Error("Invalid arity: "+arguments.length);};c.a=b;c.c=a;return c}(),nc=function(){function a(a,b,c){return c&&(c.h&524288||c.sb)?c.O(null,a,b):c instanceof Array?Nb.c(c,a,b):"string"===typeof c?Nb.c(c,a,b):t(Xa,c)?Ya.c(c,a,b):Y.c(a,b,c)}function b(a,b){return b&&(b.h&524288|| 80 | b.sb)?b.N(null,a):b instanceof Array?Nb.a(b,a):"string"===typeof b?Nb.a(b,a):t(Xa,b)?Ya.a(b,a):Y.a(a,b)}var c=null,c=function(c,e,f){switch(arguments.length){case 2:return b.call(this,c,e);case 3:return a.call(this,c,e,f)}throw Error("Invalid arity: "+arguments.length);};c.a=b;c.c=a;return c}(); 81 | function oc(a){return function(){function b(b,c){return a.a?a.a(b,c):a.call(null,b,c)}function c(){return a.r?a.r():a.call(null)}var d=null,d=function(a,d){switch(arguments.length){case 0:return c.call(this);case 1:return a;case 2:return b.call(this,a,d)}throw Error("Invalid arity: "+arguments.length);};d.r=c;d.e=function(a){return a};d.a=b;return d}()} 82 | var pc=function(){function a(a,b,c,h){a=a.e?a.e(oc(b)):a.call(null,oc(b));c=nc.c(a,c,h);c=a.e?a.e(Lb(c)?Ra(c):c):a.call(null,Lb(c)?Ra(c):c);return Lb(c)?Ra(c):c}function b(a,b,f){return c.j(a,b,b.r?b.r():b.call(null),f)}var c=null,c=function(c,e,f,h){switch(arguments.length){case 3:return b.call(this,c,e,f);case 4:return a.call(this,c,e,f,h)}throw Error("Invalid arity: "+arguments.length);};c.c=b;c.j=a;return c}(); 83 | function qc(a){a=(a-a%2)/2;return 0<=a?Math.floor.e?Math.floor.e(a):Math.floor.call(null,a):Math.ceil.e?Math.ceil.e(a):Math.ceil.call(null,a)}function rc(a){a-=a>>1&1431655765;a=(a&858993459)+(a>>2&858993459);return 16843009*(a+(a>>4)&252645135)>>24} 84 | var z=function(){function a(a){return null==a?"":a.toString()}var b=null,c=function(){function a(b,d){var k=null;1a?0:a-1>>>5<<5}function Xc(a,b,c){for(;;){if(0===b)return c;var d=Vc(a);d.b[0]=c;c=d;b-=5}} 124 | var Zc=function Yc(b,c,d,e){var f=new Uc(d.p,B(d.b)),h=b.f-1>>>c&31;5===c?f.b[h]=e:(d=d.b[h],b=null!=d?Yc(b,c-5,d,e):Xc(null,c-5,e),f.b[h]=b);return f};function $c(a,b){throw Error("No item "+z.e(a)+" in vector of length "+z.e(b));}function ad(a){var b=a.root;for(a=a.shift;;)if(0=Wc(a))return a.M;for(var c=a.root,d=a.shift;;)if(0>>d&31],d=e;else return c.b}function cd(a,b){return 0<=b&&b>>c&31;b=dd(b,c-5,d.b[k],e,f);h.b[k]=b}return h};function fd(a,b,c,d,e,f){this.i=a;this.f=b;this.shift=c;this.root=d;this.M=e;this.l=f;this.h=167668511;this.o=8196}g=fd.prototype;g.toString=function(){return sb(this)};g.v=function(a,b){return I.c(this,b,null)};g.w=function(a,b,c){return"number"===typeof b?F.c(this,b,c):c};g.R=function(a,b){return cd(this,b)[b&31]}; 126 | g.T=function(a,b,c){return 0<=b&&b=this.f?new Hb(this.M,0):jd.j?jd.j(this,ad(this),0,0):jd.call(null,this,ad(this),0,0)};g.F=function(a,b){return new fd(b,this.f,this.shift,this.root,this.M,this.l)}; 129 | g.I=function(a,b){if(32>this.f-Wc(this)){for(var c=this.M.length,d=Array(c+1),e=0;;)if(e>>5>1<b||this.end<=this.start+b?$c(b,this.end-this.start):F.a(this.fa,this.start+b)};g.T=function(a,b,c){return 0>b||this.end<=this.start+b?c:F.c(this.fa,this.start+b,c)}; 137 | g.$a=function(a,b,c){var d=this,e=d.start+b;return od.B?od.B(d.i,Zb.c(d.fa,e,c),d.start,function(){var a=d.end,b=e+1;return a>b?a:b}(),null):od.call(null,d.i,Zb.c(d.fa,e,c),d.start,function(){var a=d.end,b=e+1;return a>b?a:b}(),null)};g.G=function(){return this.i};g.J=function(){return this.end-this.start};g.D=function(){var a=this.l;return null!=a?a:this.l=a=Jb(this)};g.A=function(a,b){return Qb(this,b)};g.K=function(){return W(Sb,this.i)};g.N=function(a,b){return Mb.a(this,b)}; 138 | g.O=function(a,b,c){return Mb.c(this,b,c)};g.Ia=function(a,b,c){if("number"===typeof b)return Qa(this,b,c);throw Error("Subvec's key for assoc must be a number.");};g.H=function(){var a=this;return function(b){return function d(e){return e===a.end?null:S(F.a(a.fa,e),new xc(null,function(){return function(){return d(e+1)}}(b),null,null))}}(this)(a.start)};g.F=function(a,b){return od.B?od.B(b,this.fa,this.start,this.end,this.l):od.call(null,b,this.fa,this.start,this.end,this.l)}; 139 | g.I=function(a,b){return od.B?od.B(this.i,Qa(this.fa,this.end,b),this.start,this.end+1,null):od.call(null,this.i,Qa(this.fa,this.end,b),this.start,this.end+1,null)};g.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.R(null,c);case 3:return this.T(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.a=function(a,c){return this.R(null,c)};a.c=function(a,c,d){return this.T(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(B(b)))}; 140 | g.e=function(a){return this.R(null,a)};g.a=function(a,b){return this.T(null,a,b)};function od(a,b,c,d,e){for(;;)if(b instanceof nd)c=b.start+c,d=b.start+d,b=b.fa;else{var f=T(b);if(0>c||0>d||c>f||d>f)throw Error("Index out of bounds");return new nd(a,b,c,d,e)}} 141 | var md=function(){function a(a,b,c){return od(null,a,b,c,null)}function b(a,b){return c.c(a,b,T(a))}var c=null,c=function(c,e,f){switch(arguments.length){case 2:return b.call(this,c,e);case 3:return a.call(this,c,e,f)}throw Error("Invalid arity: "+arguments.length);};c.a=b;c.c=a;return c}();function pd(a,b){return a===b.p?b:new Uc(a,B(b.b))}function hd(a){return new Uc({},B(a.b))} 142 | function id(a){var b=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];ic(a,0,b,0,a.length);return b}var rd=function qd(b,c,d,e){d=pd(b.root.p,d);var f=b.f-1>>>c&31;if(5===c)b=e;else{var h=d.b[f];b=null!=h?qd(b,c-5,h,e):Xc(b.root.p,c-5,e)}d.b[f]=b;return d};function gd(a,b,c,d){this.f=a;this.shift=b;this.root=c;this.M=d;this.h=275;this.o=88}g=gd.prototype; 143 | g.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.v(null,c);case 3:return this.w(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.a=function(a,c){return this.v(null,c)};a.c=function(a,c,d){return this.w(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(B(b)))};g.e=function(a){return this.v(null,a)};g.a=function(a,b){return this.w(null,a,b)};g.v=function(a,b){return I.c(this,b,null)}; 144 | g.w=function(a,b,c){return"number"===typeof b?F.c(this,b,c):c};g.R=function(a,b){if(this.root.p)return cd(this,b)[b&31];throw Error("nth after persistent!");};g.T=function(a,b,c){return 0<=b&&b>>a&31,n=f(a-5,l.b[m]);l.b[m]=n}return l}}(this).call(null,d.shift,d.root),d.root=a),this;if(b===d.f)return ib(this,c);throw Error("Index "+z.e(b)+" out of bounds for TransientVector of length"+z.e(d.f));}throw Error("assoc! after persistent!");}; 146 | g.Ka=function(a,b,c){if("number"===typeof b)return lb(this,b,c);throw Error("TransientVector's key for assoc! must be a number.");}; 147 | g.La=function(a,b){if(this.root.p){if(32>this.f-Wc(this))this.M[this.f&31]=b;else{var c=new Uc(this.root.p,this.M),d=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];d[0]=b;this.M=d;if(this.f>>>5>1<=c)return new la(this.i,this.f-1,d,null);Eb.a(b,this.b[e])||(d[f]=this.b[e],d[f+1]=this.b[e+1],f+=2);e+=2}}else return this}; 155 | g.Ia=function(a,b,c){a=vd(this,b);if(-1===a){if(this.fb?4:2*(b+1));ic(this.b,0,c,0,2*b);return new Jd(a,this.q,c)};g.Na=function(){return Kd.e?Kd.e(this.b):Kd.call(null,this.b)};g.ya=function(a,b,c,d){var e=1<<(b>>>a&31);if(0===(this.q&e))return d;var f=rc(this.q&e-1),e=this.b[2*f],f=this.b[2*f+1];return null==e?f.ya(a+5,b,c,d):Fd(c,e)?f:d}; 164 | g.ba=function(a,b,c,d,e,f){var h=1<<(c>>>b&31),k=rc(this.q&h-1);if(0===(this.q&h)){var l=rc(this.q);if(2*l>>b&31]=Ld.ba(a,b+5,c,d,e,f);for(e=d=0;;)if(32>d)0!== 165 | (this.q>>>d&1)&&(k[d]=null!=this.b[e]?Ld.ba(a,b+5,Ab(this.b[e]),this.b[e],this.b[e+1],f):this.b[e+1],e+=2),d+=1;else break;return new Md(a,l+1,k)}b=Array(2*(l+4));ic(this.b,0,b,0,2*k);b[2*k]=d;b[2*k+1]=e;ic(this.b,2*k,b,2*(k+1),2*(l-k));f.da=!0;a=this.Fa(a);a.b=b;a.q|=h;return a}l=this.b[2*k];h=this.b[2*k+1];if(null==l)return l=h.ba(a,b+5,c,d,e,f),l===h?this:Id.j(this,a,2*k+1,l);if(Fd(d,l))return e===h?this:Id.j(this,a,2*k+1,e);f.da=!0;return Id.Q(this,a,2*k,null,2*k+1,Nd.W?Nd.W(a,b+5,l,h,c,d,e): 166 | Nd.call(null,a,b+5,l,h,c,d,e))}; 167 | g.aa=function(a,b,c,d,e){var f=1<<(b>>>a&31),h=rc(this.q&f-1);if(0===(this.q&f)){var k=rc(this.q);if(16<=k){h=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];h[b>>>a&31]=Ld.aa(a+5,b,c,d,e);for(d=c=0;;)if(32>c)0!==(this.q>>>c&1)&&(h[c]=null!=this.b[d]?Ld.aa(a+5,Ab(this.b[d]),this.b[d],this.b[d+1],e):this.b[d+1],d+=2),c+=1;else break;return new Md(null,k+1,h)}a=Array(2*(k+1));ic(this.b, 168 | 0,a,0,2*h);a[2*h]=c;a[2*h+1]=d;ic(this.b,2*h,a,2*(h+1),2*(k-h));e.da=!0;return new Jd(null,this.q|f,a)}k=this.b[2*h];f=this.b[2*h+1];if(null==k)return k=f.aa(a+5,b,c,d,e),k===f?this:new Jd(null,this.q,Gd.c(this.b,2*h+1,k));if(Fd(c,k))return d===f?this:new Jd(null,this.q,Gd.c(this.b,2*h+1,d));e.da=!0;return new Jd(null,this.q,Gd.B(this.b,2*h,null,2*h+1,Nd.Q?Nd.Q(a+5,k,f,b,c,d):Nd.call(null,a+5,k,f,b,c,d)))}; 169 | g.Oa=function(a,b,c){var d=1<<(b>>>a&31);if(0===(this.q&d))return this;var e=rc(this.q&d-1),f=this.b[2*e],h=this.b[2*e+1];return null==f?(a=h.Oa(a+5,b,c),a===h?this:null!=a?new Jd(null,this.q,Gd.c(this.b,2*e+1,a)):this.q===d?null:new Jd(null,this.q^d,Hd(this.b,e))):Fd(c,f)?new Jd(null,this.q^d,Hd(this.b,e)):this};var Ld=new Jd(null,0,[]);function Md(a,b,c){this.p=a;this.f=b;this.b=c}g=Md.prototype;g.Fa=function(a){return a===this.p?this:new Md(a,this.f,B(this.b))}; 170 | g.Na=function(){return Od.e?Od.e(this.b):Od.call(null,this.b)};g.ya=function(a,b,c,d){var e=this.b[b>>>a&31];return null!=e?e.ya(a+5,b,c,d):d};g.ba=function(a,b,c,d,e,f){var h=c>>>b&31,k=this.b[h];if(null==k)return a=Id.j(this,a,h,Ld.ba(a,b+5,c,d,e,f)),a.f+=1,a;b=k.ba(a,b+5,c,d,e,f);return b===k?this:Id.j(this,a,h,b)}; 171 | g.aa=function(a,b,c,d,e){var f=b>>>a&31,h=this.b[f];if(null==h)return new Md(null,this.f+1,Gd.c(this.b,f,Ld.aa(a+5,b,c,d,e)));a=h.aa(a+5,b,c,d,e);return a===h?this:new Md(null,this.f,Gd.c(this.b,f,a))}; 172 | g.Oa=function(a,b,c){var d=b>>>a&31,e=this.b[d];if(null!=e){a=e.Oa(a+5,b,c);if(a===e)d=this;else if(null==a)if(8>=this.f)a:{e=this.b;a=2*(this.f-1);b=Array(a);c=0;for(var f=1,h=0;;)if(ca?d:Fd(c,this.b[a])?this.b[a+1]:d}; 174 | g.ba=function(a,b,c,d,e,f){if(c===this.ua){b=Pd(this.b,this.f,d);if(-1===b){if(this.b.length>2*this.f)return a=Id.Q(this,a,2*this.f,d,2*this.f+1,e),f.da=!0,a.f+=1,a;c=this.b.length;b=Array(c+2);ic(this.b,0,b,0,c);b[c]=d;b[c+1]=e;f.da=!0;f=this.f+1;a===this.p?(this.b=b,this.f=f,a=this):a=new Qd(this.p,this.ua,f,b);return a}return this.b[b+1]===e?this:Id.j(this,a,b+1,e)}return(new Jd(a,1<<(this.ua>>>b&31),[null,this,null,null])).ba(a,b,c,d,e,f)}; 175 | g.aa=function(a,b,c,d,e){return b===this.ua?(a=Pd(this.b,this.f,c),-1===a?(a=2*this.f,b=Array(a+2),ic(this.b,0,b,0,a),b[a]=c,b[a+1]=d,e.da=!0,new Qd(null,this.ua,this.f+1,b)):Eb.a(this.b[a],d)?this:new Qd(null,this.ua,this.f,Gd.c(this.b,a+1,d))):(new Jd(null,1<<(this.ua>>>a&31),[null,this])).aa(a,b,c,d,e)};g.Oa=function(a,b,c){a=Pd(this.b,this.f,c);return-1===a?this:1===this.f?null:new Qd(null,this.ua,this.f-1,Hd(this.b,qc(a)))}; 176 | var Nd=function(){function a(a,b,c,h,k,l,m){var n=Ab(c);if(n===k)return new Qd(null,n,2,[c,h,l,m]);var q=new Ed;return Ld.ba(a,b,n,c,h,q).ba(a,b,k,l,m,q)}function b(a,b,c,h,k,l){var m=Ab(b);if(m===h)return new Qd(null,m,2,[b,c,k,l]);var n=new Ed;return Ld.aa(a,m,b,c,n).aa(a,h,k,l,n)}var c=null,c=function(c,e,f,h,k,l,m){switch(arguments.length){case 6:return b.call(this,c,e,f,h,k,l);case 7:return a.call(this,c,e,f,h,k,l,m)}throw Error("Invalid arity: "+arguments.length);};c.Q=b;c.W=a;return c}(); 177 | function Rd(a,b,c,d,e){this.i=a;this.ca=b;this.m=c;this.s=d;this.l=e;this.o=0;this.h=32374860}g=Rd.prototype;g.toString=function(){return sb(this)};g.G=function(){return this.i};g.D=function(){var a=this.l;return null!=a?a:this.l=a=Jb(this)};g.A=function(a,b){return Qb(this,b)};g.K=function(){return W(P,this.i)};g.N=function(a,b){return Y.a(b,this)};g.O=function(a,b,c){return Y.c(b,c,this)};g.S=function(){return null==this.s?new fd(null,2,5,kd,[this.ca[this.m],this.ca[this.m+1]],null):N(this.s)}; 178 | g.U=function(){return null==this.s?Kd.c?Kd.c(this.ca,this.m+2,null):Kd.call(null,this.ca,this.m+2,null):Kd.c?Kd.c(this.ca,this.m,Q(this.s)):Kd.call(null,this.ca,this.m,Q(this.s))};g.H=function(){return this};g.F=function(a,b){return new Rd(b,this.ca,this.m,this.s,this.l)};g.I=function(a,b){return S(b,this)}; 179 | var Kd=function(){function a(a,b,c){if(null==c)for(c=a.length;;)if(bia)return J(a,"#");J(a,c);L(h)&&(b.c?b.c(N(h),a,f):b.call(null,N(h),a,f));for(var l=Q(h),m=sa.e(f)-1;;)if(!l||null!=m&&0===m){L(l)&&0===m&&(J(a,d),J(a,"..."));break}else{J(a,d);b.c?b.c(N(l),a,f):b.call(null,N(l),a,f);var n=Q(l);c=m-1;l=n;m=c}return J(a,e)}finally{ia=k}} 206 | var ie=function(){function a(a,d){var e=null;1 (fn inc [x]) 52 | 53 | (fn-signature map) 54 | ;; => (fn [f c1 c2 c3 var_args]) 55 | " 56 | [f] 57 | (let [[_ name sig] (re-find fn-re (str f)) 58 | arglist (mapv symbol (.split (str sig) ","))] 59 | (if name 60 | (list 'fn (symbol name) arglist) 61 | (list 'fn arglist)))) 62 | 63 | 64 | (def default-tracer 65 | (let [pr-val* (fn pr-val* [x] 66 | (cond 67 | (fn? x) 68 | (fn-signature x) 69 | (coll? x) 70 | (walk pr-val* identity x) 71 | :else x)) 72 | pr-val (fn [x] (pr-str (pr-val* x))) 73 | log-binding (fn [form init] 74 | (.groupCollapsed js/console "%c%s %c%s" 75 | "font-weight:bold;" 76 | (pr-str form) 77 | "font-weight:normal;" 78 | (pr-val init))) 79 | log-exit (fn [exit] 80 | (.groupCollapsed js/console "=>" (pr-val exit)) 81 | (.log js/console exit) 82 | (.groupEnd js/console)) 83 | has-bindings? #{'fn* 84 | `fn 85 | 'fn 86 | 'defn 87 | `defn 88 | 'defn- 89 | `defn- 90 | 'defmethod 91 | `defmethod 92 | 'deftype 93 | `deftype 94 | 'defrecord 95 | `defrecord 96 | 'reify 97 | `reify 98 | 'let 99 | `let 100 | 'extend-type 101 | `extend-type 102 | 'extend-protocol 103 | `extend-protocol} 104 | fn-like? (disj has-bindings? 'let `let)] 105 | (reify 106 | ITraceEnter 107 | (-trace-enter 108 | [_ {:keys [anonymous? arglist args dispatch-val form init name ns op protocol]}] 109 | (cond 110 | (fn-like? op) 111 | (let [title (if protocol 112 | (str protocol " " name " " arglist) 113 | (str ns "/" name 114 | (when dispatch-val 115 | (str " " (pr-str dispatch-val))) 116 | (str " " arglist) 117 | (when anonymous? " (anonymous)"))) 118 | arglist (remove '#{&} arglist)] 119 | (.groupCollapsed js/console title) 120 | (.groupCollapsed js/console "bindings")) 121 | 122 | (#{'let `let} op) 123 | (let [title (str op)] 124 | (.groupCollapsed js/console title) 125 | (.groupCollapsed js/console "bindings")) 126 | 127 | (#{'binding} op) 128 | (log-binding form init))) 129 | 130 | ITraceExit 131 | (-trace-exit [_ {:keys [op exit]}] 132 | (cond 133 | (#{'binding} op) 134 | (do (log-exit exit) 135 | (.groupEnd js/console)) 136 | 137 | (has-bindings? op) 138 | (do (.groupEnd js/console) 139 | (log-exit exit) 140 | (.groupEnd js/console)))) 141 | 142 | ITraceError 143 | (-trace-error [_ {:keys [op form error ex-data]}] 144 | (cond 145 | (#{'binding} op) 146 | (do 147 | (.error js/console (.-stack error)) 148 | (when ex-data 149 | (.groupCollapsed js/console "ex-data") 150 | (.groupCollapsed js/console (pr-val ex-data)) 151 | (.log js/console ex-data) 152 | (.groupEnd js/console) 153 | (.groupEnd js/console))) 154 | 155 | (has-bindings? op) 156 | (do (.groupEnd js/console) 157 | (do 158 | (.error js/console (.-stack error)) 159 | (when ex-data 160 | (.groupCollapsed js/console "ex-data") 161 | (.groupCollapsed js/console (pr-val ex-data)) 162 | (.log js/console ex-data) 163 | (.groupEnd js/console) 164 | (.groupEnd js/console))) 165 | (.groupEnd js/console))))))) 166 | --------------------------------------------------------------------------------