github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/internal/jsre/pretty.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:38</date> 10 //</624450092966875136> 11 12 13 package jsre 14 15 import ( 16 "fmt" 17 "io" 18 "sort" 19 "strconv" 20 "strings" 21 22 "github.com/fatih/color" 23 "github.com/robertkrimen/otto" 24 ) 25 26 const ( 27 maxPrettyPrintLevel = 3 28 indentString = " " 29 ) 30 31 var ( 32 FunctionColor = color.New(color.FgMagenta).SprintfFunc() 33 SpecialColor = color.New(color.Bold).SprintfFunc() 34 NumberColor = color.New(color.FgRed).SprintfFunc() 35 StringColor = color.New(color.FgGreen).SprintfFunc() 36 ErrorColor = color.New(color.FgHiRed).SprintfFunc() 37 ) 38 39 //打印对象时隐藏这些字段。 40 var boringKeys = map[string]bool{ 41 "valueOf": true, 42 "toString": true, 43 "toLocaleString": true, 44 "hasOwnProperty": true, 45 "isPrototypeOf": true, 46 "propertyIsEnumerable": true, 47 "constructor": true, 48 } 49 50 //预打印将值写入标准输出。 51 func prettyPrint(vm *otto.Otto, value otto.Value, w io.Writer) { 52 ppctx{vm: vm, w: w}.printValue(value, 0, false) 53 } 54 55 //PrettyError将错误写入标准输出。 56 func prettyError(vm *otto.Otto, err error, w io.Writer) { 57 failure := err.Error() 58 if ottoErr, ok := err.(*otto.Error); ok { 59 failure = ottoErr.String() 60 } 61 fmt.Fprint(w, ErrorColor("%s", failure)) 62 } 63 64 func (re *JSRE) prettyPrintJS(call otto.FunctionCall) otto.Value { 65 for _, v := range call.ArgumentList { 66 prettyPrint(call.Otto, v, re.output) 67 fmt.Fprintln(re.output) 68 } 69 return otto.UndefinedValue() 70 } 71 72 type ppctx struct { 73 vm *otto.Otto 74 w io.Writer 75 } 76 77 func (ctx ppctx) indent(level int) string { 78 return strings.Repeat(indentString, level) 79 } 80 81 func (ctx ppctx) printValue(v otto.Value, level int, inArray bool) { 82 switch { 83 case v.IsObject(): 84 ctx.printObject(v.Object(), level, inArray) 85 case v.IsNull(): 86 fmt.Fprint(ctx.w, SpecialColor("null")) 87 case v.IsUndefined(): 88 fmt.Fprint(ctx.w, SpecialColor("undefined")) 89 case v.IsString(): 90 s, _ := v.ToString() 91 fmt.Fprint(ctx.w, StringColor("%q", s)) 92 case v.IsBoolean(): 93 b, _ := v.ToBoolean() 94 fmt.Fprint(ctx.w, SpecialColor("%t", b)) 95 case v.IsNaN(): 96 fmt.Fprint(ctx.w, NumberColor("NaN")) 97 case v.IsNumber(): 98 s, _ := v.ToString() 99 fmt.Fprint(ctx.w, NumberColor("%s", s)) 100 default: 101 fmt.Fprint(ctx.w, "<unprintable>") 102 } 103 } 104 105 func (ctx ppctx) printObject(obj *otto.Object, level int, inArray bool) { 106 switch obj.Class() { 107 case "Array", "GoArray": 108 lv, _ := obj.Get("length") 109 len, _ := lv.ToInteger() 110 if len == 0 { 111 fmt.Fprintf(ctx.w, "[]") 112 return 113 } 114 if level > maxPrettyPrintLevel { 115 fmt.Fprint(ctx.w, "[...]") 116 return 117 } 118 fmt.Fprint(ctx.w, "[") 119 for i := int64(0); i < len; i++ { 120 el, err := obj.Get(strconv.FormatInt(i, 10)) 121 if err == nil { 122 ctx.printValue(el, level+1, true) 123 } 124 if i < len-1 { 125 fmt.Fprintf(ctx.w, ", ") 126 } 127 } 128 fmt.Fprint(ctx.w, "]") 129 130 case "Object": 131 //将bignumber.js中的值打印为常规数字。 132 if ctx.isBigNumber(obj) { 133 fmt.Fprint(ctx.w, NumberColor("%s", toString(obj))) 134 return 135 } 136 //否则,打印所有缩进的字段,但如果太深则停止。 137 keys := ctx.fields(obj) 138 if len(keys) == 0 { 139 fmt.Fprint(ctx.w, "{}") 140 return 141 } 142 if level > maxPrettyPrintLevel { 143 fmt.Fprint(ctx.w, "{...}") 144 return 145 } 146 fmt.Fprintln(ctx.w, "{") 147 for i, k := range keys { 148 v, _ := obj.Get(k) 149 fmt.Fprintf(ctx.w, "%s%s: ", ctx.indent(level+1), k) 150 ctx.printValue(v, level+1, false) 151 if i < len(keys)-1 { 152 fmt.Fprintf(ctx.w, ",") 153 } 154 fmt.Fprintln(ctx.w) 155 } 156 if inArray { 157 level-- 158 } 159 fmt.Fprintf(ctx.w, "%s}", ctx.indent(level)) 160 161 case "Function": 162 //如果可能,使用ToString()显示参数列表。 163 if robj, err := obj.Call("toString"); err != nil { 164 fmt.Fprint(ctx.w, FunctionColor("function()")) 165 } else { 166 desc := strings.Trim(strings.Split(robj.String(), "{")[0], " \t\n") 167 desc = strings.Replace(desc, " (", "(", 1) 168 fmt.Fprint(ctx.w, FunctionColor("%s", desc)) 169 } 170 171 case "RegExp": 172 fmt.Fprint(ctx.w, StringColor("%s", toString(obj))) 173 174 default: 175 if v, _ := obj.Get("toString"); v.IsFunction() && level <= maxPrettyPrintLevel { 176 s, _ := obj.Call("toString") 177 fmt.Fprintf(ctx.w, "<%s %s>", obj.Class(), s.String()) 178 } else { 179 fmt.Fprintf(ctx.w, "<%s>", obj.Class()) 180 } 181 } 182 } 183 184 func (ctx ppctx) fields(obj *otto.Object) []string { 185 var ( 186 vals, methods []string 187 seen = make(map[string]bool) 188 ) 189 add := func(k string) { 190 if seen[k] || boringKeys[k] || strings.HasPrefix(k, "_") { 191 return 192 } 193 seen[k] = true 194 if v, _ := obj.Get(k); v.IsFunction() { 195 methods = append(methods, k) 196 } else { 197 vals = append(vals, k) 198 } 199 } 200 iterOwnAndConstructorKeys(ctx.vm, obj, add) 201 sort.Strings(vals) 202 sort.Strings(methods) 203 return append(vals, methods...) 204 } 205 206 func iterOwnAndConstructorKeys(vm *otto.Otto, obj *otto.Object, f func(string)) { 207 seen := make(map[string]bool) 208 iterOwnKeys(vm, obj, func(prop string) { 209 seen[prop] = true 210 f(prop) 211 }) 212 if cp := constructorPrototype(obj); cp != nil { 213 iterOwnKeys(vm, cp, func(prop string) { 214 if !seen[prop] { 215 f(prop) 216 } 217 }) 218 } 219 } 220 221 func iterOwnKeys(vm *otto.Otto, obj *otto.Object, f func(string)) { 222 Object, _ := vm.Object("Object") 223 rv, _ := Object.Call("getOwnPropertyNames", obj.Value()) 224 gv, _ := rv.Export() 225 switch gv := gv.(type) { 226 case []interface{}: 227 for _, v := range gv { 228 f(v.(string)) 229 } 230 case []string: 231 for _, v := range gv { 232 f(v) 233 } 234 default: 235 panic(fmt.Errorf("Object.getOwnPropertyNames returned unexpected type %T", gv)) 236 } 237 } 238 239 func (ctx ppctx) isBigNumber(v *otto.Object) bool { 240 //使用自定义构造函数处理数字。 241 if v, _ := v.Get("constructor"); v.Object() != nil { 242 if strings.HasPrefix(toString(v.Object()), "function BigNumber") { 243 return true 244 } 245 } 246 //处理默认构造函数。 247 BigNumber, _ := ctx.vm.Object("BigNumber.prototype") 248 if BigNumber == nil { 249 return false 250 } 251 bv, _ := BigNumber.Call("isPrototypeOf", v) 252 b, _ := bv.ToBoolean() 253 return b 254 } 255 256 func toString(obj *otto.Object) string { 257 s, _ := obj.Call("toString") 258 return s.String() 259 } 260 261 func constructorPrototype(obj *otto.Object) *otto.Object { 262 if v, _ := obj.Get("constructor"); v.Object() != nil { 263 if v, _ = v.Object().Get("prototype"); v.Object() != nil { 264 return v.Object() 265 } 266 } 267 return nil 268 } 269