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