├── .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 | 
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 |
--------------------------------------------------------------------------------