github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/internal/jsre/pretty.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package jsre 18 19 import ( 20 "fmt" 21 "io" 22 "sort" 23 "strconv" 24 "strings" 25 26 "github.com/robertkrimen/otto" 27 ) 28 29 const ( 30 maxPrettyPrintLevel = 3 31 indentString = " " 32 ) 33 34 // these fields are hidden when printing objects. 35 var boringKeys = map[string]bool{ 36 "valueOf": true, 37 "toString": true, 38 "toLocaleString": true, 39 "hasOwnProperty": true, 40 "isPrototypeOf": true, 41 "propertyIsEnumerable": true, 42 "constructor": true, 43 } 44 45 // prettyPrint writes value to standard output. 46 func prettyPrint(vm *otto.Otto, value otto.Value, w io.Writer) { 47 ppctx{vm: vm, w: w}.printValue(value, 0, false) 48 } 49 50 // prettyError writes err to standard output. 51 func prettyError(vm *otto.Otto, err error, w io.Writer) { 52 failure := err.Error() 53 if ottoErr, ok := err.(*otto.Error); ok { 54 failure = ottoErr.String() 55 } 56 fmt.Fprint(w, "\x1b[91m", failure, "\x1b[0m") 57 } 58 59 func prettyPrintJS(call otto.FunctionCall, w io.Writer) otto.Value { 60 for _, v := range call.ArgumentList { 61 prettyPrint(call.Otto, v, w) 62 fmt.Fprintln(w) 63 } 64 return otto.UndefinedValue() 65 } 66 67 type ppctx struct { 68 vm *otto.Otto 69 w io.Writer 70 } 71 72 func (ctx ppctx) printFunction(a interface{}) { 73 fmt.Fprint(ctx.w, "\x1b[35m", a, "\x1b[0m") 74 } 75 76 func (ctx ppctx) printSpecial(a interface{}) { 77 fmt.Fprint(ctx.w, "\x1b[1m", a, "\x1b[0m") 78 } 79 80 func (ctx ppctx) printNumber(a interface{}) { 81 fmt.Fprint(ctx.w, "\x1b[31m", a, "\x1b[0m") 82 } 83 84 func (ctx ppctx) printString(a interface{}) { 85 fmt.Fprint(ctx.w, "\x1b[32m", a, "\x1b[0m") 86 } 87 88 func (ctx ppctx) indent(level int) string { 89 return strings.Repeat(indentString, level) 90 } 91 92 func (ctx ppctx) printValue(v otto.Value, level int, inArray bool) { 93 switch { 94 case v.IsObject(): 95 ctx.printObject(v.Object(), level, inArray) 96 case v.IsNull(): 97 ctx.printSpecial("null") 98 case v.IsUndefined(): 99 ctx.printSpecial("undefined") 100 case v.IsString(): 101 s, _ := v.ToString() 102 ctx.printString(strconv.Quote(s)) 103 case v.IsBoolean(): 104 b, _ := v.ToBoolean() 105 ctx.printSpecial(b) 106 case v.IsNaN(): 107 ctx.printNumber("NaN") 108 case v.IsNumber(): 109 s, _ := v.ToString() 110 ctx.printNumber(s) 111 default: 112 fmt.Fprint(ctx.w, "<unprintable>") 113 } 114 } 115 116 func (ctx ppctx) printObject(obj *otto.Object, level int, inArray bool) { 117 switch obj.Class() { 118 case "Array": 119 lv, _ := obj.Get("length") 120 len, _ := lv.ToInteger() 121 if len == 0 { 122 fmt.Fprintf(ctx.w, "[]") 123 return 124 } 125 if level > maxPrettyPrintLevel { 126 fmt.Fprint(ctx.w, "[...]") 127 return 128 } 129 fmt.Fprint(ctx.w, "[") 130 for i := int64(0); i < len; i++ { 131 el, err := obj.Get(strconv.FormatInt(i, 10)) 132 if err == nil { 133 ctx.printValue(el, level+1, true) 134 } 135 if i < len-1 { 136 fmt.Fprintf(ctx.w, ", ") 137 } 138 } 139 fmt.Fprint(ctx.w, "]") 140 141 case "Object": 142 // Print values from bignumber.js as regular numbers. 143 if ctx.isBigNumber(obj) { 144 ctx.printNumber(toString(obj)) 145 return 146 } 147 // Otherwise, print all fields indented, but stop if we're too deep. 148 keys := ctx.fields(obj) 149 if len(keys) == 0 { 150 fmt.Fprint(ctx.w, "{}") 151 return 152 } 153 if level > maxPrettyPrintLevel { 154 fmt.Fprint(ctx.w, "{...}") 155 return 156 } 157 fmt.Fprintln(ctx.w, "{") 158 for i, k := range keys { 159 v, _ := obj.Get(k) 160 fmt.Fprintf(ctx.w, "%s%s: ", ctx.indent(level+1), k) 161 ctx.printValue(v, level+1, false) 162 if i < len(keys)-1 { 163 fmt.Fprintf(ctx.w, ",") 164 } 165 fmt.Fprintln(ctx.w) 166 } 167 if inArray { 168 level-- 169 } 170 fmt.Fprintf(ctx.w, "%s}", ctx.indent(level)) 171 172 case "Function": 173 // Use toString() to display the argument list if possible. 174 if robj, err := obj.Call("toString"); err != nil { 175 ctx.printFunction("function()") 176 } else { 177 desc := strings.Trim(strings.Split(robj.String(), "{")[0], " \t\n") 178 desc = strings.Replace(desc, " (", "(", 1) 179 ctx.printFunction(desc) 180 } 181 182 case "RegExp": 183 ctx.printString(toString(obj)) 184 185 default: 186 if v, _ := obj.Get("toString"); v.IsFunction() && level <= maxPrettyPrintLevel { 187 s, _ := obj.Call("toString") 188 fmt.Fprintf(ctx.w, "<%s %s>", obj.Class(), s.String()) 189 } else { 190 fmt.Fprintf(ctx.w, "<%s>", obj.Class()) 191 } 192 } 193 } 194 195 func (ctx ppctx) fields(obj *otto.Object) []string { 196 var ( 197 vals, methods []string 198 seen = make(map[string]bool) 199 ) 200 add := func(k string) { 201 if seen[k] || boringKeys[k] || strings.HasPrefix(k, "_") { 202 return 203 } 204 seen[k] = true 205 if v, _ := obj.Get(k); v.IsFunction() { 206 methods = append(methods, k) 207 } else { 208 vals = append(vals, k) 209 } 210 } 211 iterOwnAndConstructorKeys(ctx.vm, obj, add) 212 sort.Strings(vals) 213 sort.Strings(methods) 214 return append(vals, methods...) 215 } 216 217 func iterOwnAndConstructorKeys(vm *otto.Otto, obj *otto.Object, f func(string)) { 218 seen := make(map[string]bool) 219 iterOwnKeys(vm, obj, func(prop string) { 220 seen[prop] = true 221 f(prop) 222 }) 223 if cp := constructorPrototype(obj); cp != nil { 224 iterOwnKeys(vm, cp, func(prop string) { 225 if !seen[prop] { 226 f(prop) 227 } 228 }) 229 } 230 } 231 232 func iterOwnKeys(vm *otto.Otto, obj *otto.Object, f func(string)) { 233 Object, _ := vm.Object("Object") 234 rv, _ := Object.Call("getOwnPropertyNames", obj.Value()) 235 gv, _ := rv.Export() 236 switch gv := gv.(type) { 237 case []interface{}: 238 for _, v := range gv { 239 f(v.(string)) 240 } 241 case []string: 242 for _, v := range gv { 243 f(v) 244 } 245 default: 246 panic(fmt.Errorf("Object.getOwnPropertyNames returned unexpected type %T", gv)) 247 } 248 } 249 250 func (ctx ppctx) isBigNumber(v *otto.Object) bool { 251 // Handle numbers with custom constructor. 252 if v, _ := v.Get("constructor"); v.Object() != nil { 253 if strings.HasPrefix(toString(v.Object()), "function BigNumber") { 254 return true 255 } 256 } 257 // Handle default constructor. 258 BigNumber, _ := ctx.vm.Object("BigNumber.prototype") 259 if BigNumber == nil { 260 return false 261 } 262 bv, _ := BigNumber.Call("isPrototypeOf", v) 263 b, _ := bv.ToBoolean() 264 return b 265 } 266 267 func toString(obj *otto.Object) string { 268 s, _ := obj.Call("toString") 269 return s.String() 270 } 271 272 func constructorPrototype(obj *otto.Object) *otto.Object { 273 if v, _ := obj.Get("constructor"); v.Object() != nil { 274 if v, _ = v.Object().Get("prototype"); v.Object() != nil { 275 return v.Object() 276 } 277 } 278 return nil 279 }