github.com/igggame/nebulas-go@v2.1.0+incompatible/cmd/console/jsvm.go (about)

     1  // Copyright (C) 2017 go-nebulas authors
     2  //
     3  // This file is part of the go-nebulas library.
     4  //
     5  // the go-nebulas library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // the go-nebulas library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU General Public License
    16  // along with the go-nebulas library.  If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  
    19  package console
    20  
    21  import (
    22  	"sort"
    23  	"strings"
    24  
    25  	"github.com/robertkrimen/otto"
    26  )
    27  
    28  // JSVM javascript runtime environment
    29  type JSVM struct {
    30  
    31  	// the representation of the JavaScript runtime
    32  	vm *otto.Otto
    33  }
    34  
    35  func newJSVM() *JSVM {
    36  	vm := &JSVM{}
    37  	vm.vm = otto.New()
    38  	return vm
    39  }
    40  
    41  // Run will run the given source (parsing it first if necessary), returning the resulting value and error (if any)
    42  func (v *JSVM) Run(src string) (otto.Value, error) {
    43  	return v.vm.Run(src)
    44  }
    45  
    46  // Get returns the value of a variable in the JS environment.
    47  func (v *JSVM) Get(name string) (otto.Value, error) {
    48  	return v.vm.Get(name)
    49  }
    50  
    51  // Set assigns value v to a variable in the JS environment.
    52  func (v *JSVM) Set(name string, value interface{}) error {
    53  	return v.vm.Set(name, value)
    54  }
    55  
    56  // Compile compiles and then runs JS code.
    57  func (v *JSVM) Compile(filename string, src interface{}) error {
    58  	script, err := v.vm.Compile(filename, src)
    59  	if err != nil {
    60  		return err
    61  	}
    62  	_, err = v.vm.Run(script)
    63  	return err
    64  }
    65  
    66  // JSONString convert value to json string
    67  func (v *JSVM) JSONString(val otto.Value) (string, error) {
    68  	JSON, _ := v.vm.Object("JSON")
    69  	jsonVal, err := JSON.Call("stringify", val)
    70  	if err != nil {
    71  		return "", err
    72  	}
    73  	return jsonVal.String(), nil
    74  }
    75  
    76  // CompleteKeywords returns potential continuations for the given line.
    77  func (v *JSVM) CompleteKeywords(line string) []string {
    78  	parts := strings.Split(line, ".")
    79  	objRef := "this"
    80  	prefix := line
    81  	if len(parts) > 1 {
    82  		objRef = strings.Join(parts[0:len(parts)-1], ".")
    83  		prefix = parts[len(parts)-1]
    84  	}
    85  
    86  	obj, _ := v.vm.Object(objRef)
    87  	if obj == nil {
    88  		return nil
    89  	}
    90  	properties := v.getObjectKeys(obj, objRef, prefix)
    91  	// only not golbal prototype should be use
    92  	if objRef != "this" {
    93  		if c, _ := obj.Get("constructor"); c.Object() != nil {
    94  			if p, _ := c.Object().Get("prototype"); p.Object() != nil {
    95  				keys := v.getObjectKeys(p.Object(), objRef, prefix)
    96  				// remove the duplicate property
    97  				set := make(map[string]bool)
    98  				for _, key := range keys {
    99  					set[key] = true
   100  				}
   101  				for _, key := range properties {
   102  					set[key] = true
   103  				}
   104  				properties = make([]string, 0, len(set))
   105  				for k := range set {
   106  					properties = append(properties, k)
   107  				}
   108  			}
   109  		}
   110  	}
   111  	tmp := make([]string, len(properties))
   112  	copy(tmp, properties)
   113  	for _, v := range tmp {
   114  		tmps := strings.Split(v, ".")
   115  		f := tmps[len(tmps)-1]
   116  		// only out property use,remove request func
   117  		if f == "request" || f == "constructor" || strings.HasPrefix(f, "_") {
   118  			properties = sliceRemove(properties, v)
   119  		}
   120  	}
   121  	// Append opening parenthesis (for functions) or dot (for objects)
   122  	// if the line itself is the only completion.
   123  	if len(properties) == 1 && properties[0] == line {
   124  		obj, _ := v.vm.Object(line)
   125  		if obj != nil {
   126  			if obj.Class() == "Function" {
   127  				properties[0] += "()"
   128  			} else {
   129  				properties[0] += "."
   130  			}
   131  		}
   132  	}
   133  	sort.Strings(properties)
   134  	return properties
   135  }
   136  
   137  func (v *JSVM) getObjectKeys(obj *otto.Object, objRef, prefix string) (properties []string) {
   138  	Object, _ := v.vm.Object("Object")
   139  	rv, _ := Object.Call("getOwnPropertyNames", obj.Value())
   140  	gv, _ := rv.Export()
   141  	switch gv := gv.(type) {
   142  	case []string:
   143  		properties = parseOwnKeys(objRef, prefix, gv)
   144  	}
   145  	return properties
   146  }
   147  
   148  func parseOwnKeys(objRef, prefix string, properties []string) []string {
   149  	//fmt.Println("parse keys:", properties)
   150  	var results []string
   151  	for _, property := range properties {
   152  		//fmt.Println("property is:", property)
   153  		if len(prefix) == 0 || strings.HasPrefix(property, prefix) {
   154  			if objRef == "this" {
   155  				results = append(results, property)
   156  			} else {
   157  				results = append(results, objRef+"."+property)
   158  			}
   159  		}
   160  	}
   161  	return results
   162  }
   163  
   164  func sliceRemove(slices []string, value string) []string {
   165  	for i, v := range slices {
   166  		if v == value {
   167  			slices = append(slices[:i], slices[i+1:]...)
   168  			break
   169  		}
   170  	}
   171  	return slices
   172  }