├── LICENSE ├── README.md ├── core.js ├── img.jpg ├── index.html ├── style.css ├── t.png └── ui.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 weizman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

ProtoTree 🌳

3 |

~ The first tool ever for visually exploring the JavaScript prototype chain that's generated live in your browser ~

4 |
5 | 6 |
7 |

8 |
9 |

How does ProtoTree work?

10 |
11 | 12 | * ProtoTree uses [LavaTube 🌋](https://github.com/LavaMoat/LavaTube) to recursively walk through the entire JavaScript prototype chain in real time in your browser 13 | * With every recursive visit, ProtoTree slowely builds a tree representation of the prototype chain 14 | * In order to avoid external polution that the app creates, ProtoTree runs the recursive operation inside a cross origin iframe, to leverage a fresh new realm for the job 15 | * Follow the instructions on the app itself to take full adventage of this tool 16 | * Enjoy and please consider to ⭐ this project 17 | 18 | > *Read further @ https://twitter.com/WeizmanGal/status/1684608574444785664* 19 | -------------------------------------------------------------------------------- /core.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | function begin() { 3 | function isPrimitive(v) { 4 | if (null === v || undefined === v) { 5 | return true; 6 | } 7 | if (['boolean', 'number', 'string'].includes(typeof v)) { 8 | return true; 9 | } 10 | return false; 11 | } 12 | 13 | // because of adsense 14 | function isFrame(v) { 15 | let i = 0; 16 | let w; 17 | while (w) { 18 | w = top[i]; 19 | if (v === w) { 20 | return true; 21 | } 22 | i++; 23 | } 24 | } 25 | 26 | const root = {}; 27 | const values = {}; 28 | 29 | function cb(path) { 30 | let node = root; 31 | for (let i = 0; i < path.length; i++) { 32 | const val = path[i]; 33 | const prop = getProp(val); 34 | if (!node[prop]) { 35 | id += 1; 36 | values['b' + id] = val; 37 | node[prop] = { 38 | _value_reference: 'vf' + id, 39 | _own_names: Object.getOwnPropertyNames(val), 40 | }; 41 | } 42 | node = node[prop]; 43 | } 44 | } 45 | 46 | function getProp(val) { 47 | switch (val) { 48 | case Symbol.prototype: 49 | return 'Symbol'; 50 | case Number.prototype: 51 | return 'Number'; 52 | case Array.prototype: 53 | return 'Array'; 54 | case Boolean.prototype: 55 | return 'Boolean'; 56 | case String.prototype: 57 | return 'String'; 58 | default: 59 | let v = ''; 60 | try { 61 | if (typeof val === 'symbol') { 62 | v = val.toString(); 63 | } else if (typeof val === 'function') { 64 | v = Function.prototype.toString.call(val); 65 | } else { 66 | v += val; 67 | } 68 | if ((v).startsWith('data:text/html;Base64,')) { 69 | return '[location]'; 70 | } 71 | return v 72 | .split('[object ').join('') 73 | .split('{ [native code] }').join('') 74 | .split(']').join('') 75 | .split('\n').join(''); 76 | } catch (e) { 77 | return ({}).toString.call(val).split('[object ').join('').split(']').join(''); 78 | } 79 | } 80 | } 81 | 82 | function main(obj) { 83 | if (isPrimitive(obj) || isFrame(obj)) return; 84 | const protos = [obj]; 85 | while (true) { 86 | if (isPrimitive(obj) || isFrame(obj)) break; 87 | const proto = Object.getPrototypeOf(obj); 88 | if (!proto) break; 89 | protos.push(proto); 90 | obj = proto; 91 | } 92 | protos.reverse(); 93 | cb(protos); 94 | } 95 | 96 | let id = 0; 97 | 98 | (function () { 99 | const lt = new LavaTube(main, { 100 | onShouldIgnoreError: (p, o, e) => { 101 | // console.error('sync error in LavaTube:', e); 102 | return true; 103 | }, 104 | maxRecursionLimit: 9, 105 | }); 106 | window.LavaTube = undefined; 107 | window.begin = undefined; 108 | lt.walk(window); 109 | const result = JSON.stringify(root); 110 | setTimeout(() => { 111 | window.values = values; 112 | top.postMessage({result}, '*'); 113 | console.log(`%c${decodeURIComponent(escape(window.atob('8J+Xuu+4jw==')))} Map each entry in the full tree below to its real actual object in runtime:`, 'color:deepskyblue'); 114 | console.log('', values); 115 | }, 100); 116 | }()); 117 | } 118 | document.write(` 119 |