github.com/coyove/nj@v0.0.0-20221110084952-c7f8db1065c3/lib.go (about)

     1  package nj
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base32"
     6  	"encoding/base64"
     7  	"encoding/json"
     8  	"fmt"
     9  	"io"
    10  	"io/fs"
    11  	"io/ioutil"
    12  	"math"
    13  	"math/rand"
    14  	"mime/multipart"
    15  	"net/http"
    16  	"net/url"
    17  	"os"
    18  	"os/exec"
    19  	"path/filepath"
    20  	"regexp"
    21  	"runtime"
    22  	"strconv"
    23  	"strings"
    24  	"sync"
    25  	"time"
    26  	"unicode/utf8"
    27  	"unsafe"
    28  
    29  	"github.com/coyove/nj/bas"
    30  	"github.com/coyove/nj/internal"
    31  	"github.com/coyove/nj/parser"
    32  	"github.com/coyove/nj/typ"
    33  )
    34  
    35  func init() {
    36  	bas.AddTopFunc("chr", func(e *bas.Env) { e.A = bas.Rune(rune(e.Int(0))) })
    37  	bas.AddTopFunc("byte", func(e *bas.Env) { e.A = bas.Byte(byte(e.Int(0))) })
    38  	bas.AddTopFunc("ord", func(e *bas.Env) {
    39  		r, _ := utf8.DecodeRuneInString(e.Str(0))
    40  		e.A = bas.Int64(int64(r))
    41  	})
    42  
    43  	bas.AddTopValue("json", bas.NewNamedObject("json", 0).
    44  		SetProp("stringify", bas.Func("stringify", func(e *bas.Env) { e.A = bas.Str(e.Get(0).JSONString()) })).
    45  		SetProp("dump", bas.Func("dump", func(e *bas.Env) { e.Get(1).Stringify(e.Get(0).Writer(), typ.MarshalToJSON) })).
    46  		SetProp("parse", bas.Func("parse", func(e *bas.Env) {
    47  			s := strings.TrimSpace(e.Str(0))
    48  			if s == "" {
    49  				e.SetError(fmt.Errorf("empty value"))
    50  				return
    51  			}
    52  			switch s[0] {
    53  			case 'n':
    54  				_ = s == "null" && e.SetA(bas.Nil) || e.SetError(fmt.Errorf("invalid value"))
    55  			case 't', 'f':
    56  				e.A = (*env)(e).valueOrError(strconv.ParseBool(s))
    57  			case '[':
    58  				a := []interface{}{}
    59  				err := json.Unmarshal([]byte(s), &a)
    60  				e.A = (*env)(e).valueOrError(bas.ValueOf(a), err)
    61  			case '{':
    62  				a := map[string]interface{}{}
    63  				err := json.Unmarshal([]byte(s), &a)
    64  				e.A = (*env)(e).valueOrError(bas.ValueOf(a), err)
    65  			default:
    66  				e.SetError(fmt.Errorf("invalid value"))
    67  			}
    68  		})).
    69  		ToValue())
    70  	bas.AddTopFunc("loadfile", func(e *bas.Env) {
    71  		path := e.Str(0)
    72  		if e.Shape(1, "Nb").IsTrue() && e.MustProgram().File != "" {
    73  			path = filepath.Join(filepath.Dir(e.MustProgram().File), path)
    74  		}
    75  		e.A = MustRun(LoadFile(path, &LoadOptions{
    76  			MaxStackSize: e.MustProgram().MaxStackSize,
    77  			Globals:      e.MustProgram().Globals,
    78  		}))
    79  	})
    80  	bas.AddTopValue("eval", bas.Func("eval", func(e *bas.Env) {
    81  		p, err := LoadString(e.Str(0), &LoadOptions{
    82  			Globals: e.Shape(1, "No").Object().ToMap(),
    83  		})
    84  		if err != nil {
    85  			e.A = bas.Error(e, err)
    86  		} else {
    87  			e.A = p.Run()
    88  		}
    89  	}).Object().
    90  		SetProp("parse", bas.Func("parse", func(e *bas.Env) {
    91  			e.A = (*env)(e).valueOrError(parser.Parse(e.Str(0), "eval.parse"))
    92  		})).
    93  		SetProp("op", bas.NewObject(0).
    94  			SetProp("set", bas.Int(typ.OpSet)).
    95  			SetProp("store", bas.Int(typ.OpStore)).
    96  			SetProp("load", bas.Int(typ.OpLoad)).
    97  			SetProp("add", bas.Int(typ.OpAdd)).
    98  			SetProp("sub", bas.Int(typ.OpSub)).
    99  			SetProp("mul", bas.Int(typ.OpMul)).
   100  			SetProp("div", bas.Int(typ.OpDiv)).
   101  			SetProp("idiv", bas.Int(typ.OpIDiv)).
   102  			SetProp("inc", bas.Int(typ.OpInc)).
   103  			SetProp("mod", bas.Int(typ.OpMod)).
   104  			SetProp("len", bas.Int(typ.OpLen)).
   105  			SetProp("next", bas.Int(typ.OpNext)).
   106  			SetProp("not", bas.Int(typ.OpNot)).
   107  			SetProp("eq", bas.Int(typ.OpEq)).
   108  			SetProp("neq", bas.Int(typ.OpNeq)).
   109  			SetProp("less", bas.Int(typ.OpLess)).
   110  			SetProp("lesseq", bas.Int(typ.OpLessEq)).
   111  			SetProp("ifnot", bas.Int(typ.OpJmpFalse)).
   112  			SetProp("jmp", bas.Int(typ.OpJmp)).
   113  			SetProp("function", bas.Int(typ.OpFunction)).
   114  			SetProp("push", bas.Int(typ.OpPush)).
   115  			SetProp("pushunpack", bas.Int(typ.OpPushUnpack)).
   116  			SetProp("createarray", bas.Int(typ.OpCreateArray)).
   117  			SetProp("createobject", bas.Int(typ.OpCreateObject)).
   118  			SetProp("call", bas.Int(typ.OpCall)).
   119  			SetProp("tailcall", bas.Int(typ.OpTailCall)).
   120  			SetProp("isproto", bas.Int(typ.OpIsProto)).
   121  			SetProp("slice", bas.Int(typ.OpSlice)).
   122  			SetProp("ret", bas.Int(typ.OpRet)).
   123  			SetProp("loadtop", bas.Int(typ.OpLoadTop)).
   124  			SetProp("ext", bas.Int(typ.OpExt)).
   125  			ToValue(),
   126  		).
   127  		ToValue())
   128  
   129  	bas.AddTopValue("printf", bas.Func("printf", func(e *bas.Env) {
   130  		bas.Fprintf(e.MustProgram().Stdout, e.Str(0), e.Stack()[1:]...)
   131  	}))
   132  	bas.AddTopValue("println", bas.Func("println", func(e *bas.Env) {
   133  		for _, a := range e.Stack() {
   134  			fmt.Fprint(e.MustProgram().Stdout, a.String(), " ")
   135  		}
   136  		fmt.Fprintln(e.MustProgram().Stdout)
   137  	}))
   138  	bas.AddTopValue("print", bas.Func("print", func(e *bas.Env) {
   139  		for _, a := range e.Stack() {
   140  			fmt.Fprint(e.MustProgram().Stdout, a.String())
   141  		}
   142  		fmt.Fprintln(e.MustProgram().Stdout)
   143  	}))
   144  	bas.AddTopValue("input", bas.NewObject(0).
   145  		SetProp("int", bas.Func("int", func(e *bas.Env) {
   146  			fmt.Fprint(e.MustProgram().Stdout, e.StrDefault(0, "", 0))
   147  			var v int64
   148  			fmt.Fscanf(e.MustProgram().Stdin, "%d", &v)
   149  			e.A = bas.Int64(v)
   150  		})).
   151  		SetProp("num", bas.Func("num", func(e *bas.Env) {
   152  			fmt.Fprint(e.MustProgram().Stdout, e.StrDefault(0, "", 0))
   153  			var v float64
   154  			fmt.Fscanf(e.MustProgram().Stdin, "%f", &v)
   155  			e.A = bas.Float64(v)
   156  		})).
   157  		SetProp("str", bas.Func("str", func(e *bas.Env) {
   158  			fmt.Fprint(e.MustProgram().Stdout, e.StrDefault(0, "", 0))
   159  			var v string
   160  			fmt.Fscanln(e.MustProgram().Stdin, &v)
   161  			e.A = bas.Str(v)
   162  		})).
   163  		ToValue())
   164  
   165  	rxMeta := bas.NewEmptyNativeMeta("RegExp", bas.NewObject(0).
   166  		AddMethod("match", func(e *bas.Env) {
   167  			rx := e.This().Interface().(*regexp.Regexp)
   168  			e.A = bas.Bool(rx.MatchString(e.Str(0)))
   169  		}).
   170  		AddMethod("find", func(e *bas.Env) {
   171  			rx := e.This().Interface().(*regexp.Regexp)
   172  			e.A = bas.NewNative(rx.FindStringSubmatch(e.Str(0))).ToValue()
   173  		}).
   174  		AddMethod("findall", func(e *bas.Env) {
   175  			rx := e.This().Interface().(*regexp.Regexp)
   176  			e.A = bas.NewNative(rx.FindAllStringSubmatch(e.Str(0), e.IntDefault(1, -1))).ToValue()
   177  		}).
   178  		AddMethod("replace", func(e *bas.Env) {
   179  			rx := e.This().Interface().(*regexp.Regexp)
   180  			e.A = bas.Str(rx.ReplaceAllString(e.Str(0), e.Str(1)))
   181  		}).
   182  		SetPrototype(&bas.Proto.Native))
   183  
   184  	bas.AddTopFunc("re", func(e *bas.Env) {
   185  		if rx, err := regexp.Compile(e.Str(0)); err != nil {
   186  			e.SetError(err)
   187  		} else {
   188  			e.A = bas.NewNativeWithMeta(rx, rxMeta).ToValue()
   189  		}
   190  	})
   191  
   192  	fileMeta := bas.NewEmptyNativeMeta("File", bas.NewObject(0).
   193  		AddMethod("name", func(e *bas.Env) {
   194  			e.A = bas.Str(e.A.Interface().(*os.File).Name())
   195  		}).
   196  		AddMethod("seek", func(e *bas.Env) {
   197  			e.A = (*env)(e).valueOrError(e.A.Interface().(*os.File).Seek(e.Int64(0), e.Int(1)))
   198  		}).
   199  		AddMethod("sync", func(e *bas.Env) {
   200  			e.A = bas.Error(e, e.A.Interface().(*os.File).Sync())
   201  		}).
   202  		AddMethod("stat", func(e *bas.Env) {
   203  			e.A = (*env)(e).valueOrError(e.A.Interface().(*os.File).Stat())
   204  		}).
   205  		AddMethod("truncate", func(e *bas.Env) {
   206  			f := e.A.Interface().(*os.File)
   207  			if err := f.Truncate(e.Int64(1)); err != nil {
   208  				e.A = bas.Error(e, err)
   209  			} else {
   210  				e.A = (*env)(e).valueOrError(f.Seek(0, 2))
   211  			}
   212  		}).
   213  		SetPrototype(bas.Proto.ReadWriteCloser.Proto))
   214  
   215  	bas.AddTopValue("open", bas.Func("open", func(e *bas.Env) {
   216  		path, flag, perm := e.Str(0), e.StrDefault(1, "r", 1), e.IntDefault(2, 0644)
   217  		var opt int
   218  		for _, f := range flag {
   219  			switch f {
   220  			case 'w':
   221  				opt &^= os.O_RDWR | os.O_RDONLY
   222  				opt |= os.O_WRONLY | os.O_CREATE | os.O_TRUNC
   223  			case 'r':
   224  				opt &^= os.O_RDWR | os.O_WRONLY
   225  				opt |= os.O_RDONLY
   226  			case 'a':
   227  				opt |= os.O_APPEND | os.O_CREATE
   228  			case 'x':
   229  				opt |= os.O_EXCL
   230  			case '+':
   231  				opt &^= os.O_RDONLY | os.O_WRONLY
   232  				opt |= os.O_RDWR | os.O_CREATE
   233  			}
   234  		}
   235  		f, err := os.OpenFile(path, opt, fs.FileMode(perm))
   236  		if err != nil {
   237  			e.A = bas.Error(e, err)
   238  			return
   239  		}
   240  		e.Object(-1).Set(bas.Zero, bas.ValueOf(f))
   241  
   242  		e.A = bas.NewNativeWithMeta(f, fileMeta).ToValue()
   243  	}).Object().
   244  		AddMethod("close", func(e *bas.Env) {
   245  			if f, _ := e.Object(-1).Get(bas.Zero).Interface().(*os.File); f != nil {
   246  				e.A = bas.Error(e, f.Close())
   247  			} else {
   248  				panic("no opened file yet")
   249  			}
   250  		}).ToValue(),
   251  	)
   252  
   253  	bas.AddTopValue("math", bas.NewNamedObject("math", 0).
   254  		SetProp("INF", bas.Float64(math.Inf(1))).
   255  		SetProp("NEG_INF", bas.Float64(math.Inf(-1))).
   256  		SetProp("PI", bas.Float64(math.Pi)).
   257  		SetProp("E", bas.Float64(math.E)).
   258  		SetProp("randomseed", bas.Func("randomseed", func(e *bas.Env) { rand.Seed(int64(e.IntDefault(0, 1))) })).
   259  		SetProp("random", bas.Func("random", func(e *bas.Env) {
   260  			switch len(e.Stack()) {
   261  			case 2:
   262  				ai, bi := e.Int64(0), e.Int64(1)
   263  				e.A = bas.Int64(rand.Int63n(bi-ai+1) + ai)
   264  			case 1:
   265  				e.A = bas.Int64(rand.Int63n(e.Int64(0)))
   266  			default:
   267  				e.A = bas.Float64(rand.Float64())
   268  			}
   269  		})).
   270  		SetProp("sqrt", bas.Func("sqrt", func(e *bas.Env) { e.A = bas.Float64(math.Sqrt(e.Float64(0))) })).
   271  		SetProp("floor", bas.Func("floor", func(e *bas.Env) { e.A = bas.Float64(math.Floor(e.Float64(0))) })).
   272  		SetProp("ceil", bas.Func("ceil", func(e *bas.Env) { e.A = bas.Float64(math.Ceil(e.Float64(0))) })).
   273  		SetProp("min", bas.Func("min", func(e *bas.Env) { minMax(e, false) })).
   274  		SetProp("max", bas.Func("max", func(e *bas.Env) { minMax(e, true) })).
   275  		SetProp("pow", bas.Func("pow", func(e *bas.Env) { e.A = bas.Float64(math.Pow(e.Float64(0), e.Float64(1))) })).
   276  		SetProp("abs", bas.Func("abs", func(e *bas.Env) {
   277  			if e.A = e.Num(0); e.A.IsInt64() {
   278  				if i := e.A.Int64(); i < 0 {
   279  					e.A = bas.Int64(-i)
   280  				}
   281  			} else {
   282  				e.A = bas.Float64(math.Abs(e.A.Float64()))
   283  			}
   284  		})).
   285  		SetProp("remainder", bas.Func("remainder", func(e *bas.Env) { e.A = bas.Float64(math.Remainder(e.Float64(0), e.Float64(1))) })).
   286  		SetProp("mod", bas.Func("mod", func(e *bas.Env) { e.A = bas.Float64(math.Mod(e.Float64(0), e.Float64(1))) })).
   287  		SetProp("cos", bas.Func("cos", func(e *bas.Env) { e.A = bas.Float64(math.Cos(e.Float64(0))) })).
   288  		SetProp("sin", bas.Func("sin", func(e *bas.Env) { e.A = bas.Float64(math.Sin(e.Float64(0))) })).
   289  		SetProp("tan", bas.Func("tan", func(e *bas.Env) { e.A = bas.Float64(math.Tan(e.Float64(0))) })).
   290  		SetProp("acos", bas.Func("acos", func(e *bas.Env) { e.A = bas.Float64(math.Acos(e.Float64(0))) })).
   291  		SetProp("asin", bas.Func("asin", func(e *bas.Env) { e.A = bas.Float64(math.Asin(e.Float64(0))) })).
   292  		SetProp("atan", bas.Func("atan", func(e *bas.Env) { e.A = bas.Float64(math.Atan(e.Float64(0))) })).
   293  		SetProp("atan2", bas.Func("atan2", func(e *bas.Env) { e.A = bas.Float64(math.Atan2(e.Float64(0), e.Float64(1))) })).
   294  		SetProp("ldexp", bas.Func("ldexp", func(e *bas.Env) { e.A = bas.Float64(math.Ldexp(e.Float64(0), e.Int(0))) })).
   295  		SetProp("modf", bas.Func("modf", func(e *bas.Env) {
   296  			a, b := math.Modf(e.Float64(0))
   297  			e.A = bas.Array(bas.Float64(a), bas.Float64(b))
   298  		})).
   299  		ToValue())
   300  
   301  	bas.AddTopValue("os", bas.NewNamedObject("os", 0).
   302  		SetProp("stdout", bas.ValueOf(os.Stdout)).
   303  		SetProp("stdin", bas.ValueOf(os.Stdin)).
   304  		SetProp("stderr", bas.ValueOf(os.Stderr)).
   305  		SetProp("pid", bas.Int(os.Getpid())).
   306  		SetProp("numcpus", bas.Int(runtime.NumCPU())).
   307  		SetProp("args", bas.ValueOf(os.Args)).
   308  		SetProp("exit", bas.Func("exit", func(e *bas.Env) { os.Exit(e.IntDefault(0, 0)) })).
   309  		SetProp("environ", bas.Func("environ", func(e *bas.Env) {
   310  			if env := os.Environ(); e.Get(0).IsTrue() {
   311  				obj := bas.NewObject(len(env))
   312  				for _, e := range env {
   313  					if idx := strings.Index(e, "="); idx > -1 {
   314  						obj.SetProp(e[:idx], bas.Str(e[idx+1:]))
   315  					}
   316  				}
   317  				e.A = obj.ToValue()
   318  			} else {
   319  				e.A = bas.ValueOf(env)
   320  			}
   321  		})).
   322  		SetProp("shell", bas.Func("shell", func(e *bas.Env) {
   323  			win := runtime.GOOS == "windows"
   324  			p := exec.Command(internal.IfStr(win, "cmd", "sh"), internal.IfStr(win, "/c", "-c"), e.Str(0))
   325  			opt := e.Shape(1, "No").Object()
   326  			opt.Get(bas.Str("env")).AssertShape("No", "environment").Object().Foreach(func(k bas.Value, v *bas.Value) bool {
   327  				p.Env = append(p.Env, k.String()+"="+v.String())
   328  				return true
   329  			})
   330  			stdout := &bytes.Buffer{}
   331  			p.Stdout, p.Stderr = stdout, stdout
   332  			p.Dir = opt.GetDefault(bas.Str("dir"), bas.NullStr).AssertShape("s", "directory").Str()
   333  			if tmp := opt.Get(bas.Str("stdout")); tmp != bas.Nil {
   334  				p.Stdout = tmp.Writer()
   335  			}
   336  			if tmp := opt.Get(bas.Str("stderr")); tmp != bas.Nil {
   337  				p.Stderr = tmp.Writer()
   338  			}
   339  			if tmp := opt.Get(bas.Str("stdin")); tmp != bas.Nil {
   340  				p.Stdin = tmp.Reader()
   341  			}
   342  
   343  			to := opt.GetDefault(bas.Str("timeout"), bas.Float64(1<<30)).AssertNumber("timeout seconds").Duration()
   344  			out := make(chan error)
   345  			go func() { out <- p.Run() }()
   346  			select {
   347  			case r := <-out:
   348  				if r != nil {
   349  					e.A = bas.Error(e, r)
   350  					return
   351  				}
   352  			case <-time.After(to):
   353  				p.Process.Kill()
   354  				e.A = bas.Error(e, fmt.Errorf("os.shell timeout: %v", e.Get(0)))
   355  				return
   356  			}
   357  			e.A = bas.Bytes(stdout.Bytes())
   358  		})).
   359  		SetProp("readdir", bas.Func("readdir", func(e *bas.Env) {
   360  			e.A = (*env)(e).valueOrError(ioutil.ReadDir(e.Str(0)))
   361  		})).
   362  		SetProp("remove", bas.Func("remove", func(e *bas.Env) {
   363  			path := e.Str(0)
   364  			fi, err := os.Stat(path)
   365  			if err != nil {
   366  				e.A = bas.Error(e, err)
   367  			} else if fi.IsDir() {
   368  				e.A = bas.Error(e, os.RemoveAll(path))
   369  			} else {
   370  				e.A = bas.Error(e, os.Remove(path))
   371  			}
   372  		})).
   373  		SetProp("pstat", bas.Func("pstat", func(e *bas.Env) {
   374  			fi, err := os.Stat(e.Str(0))
   375  			_ = err == nil && e.SetA(bas.ValueOf(fi)) || e.SetA(bas.Nil)
   376  		})).
   377  		ToValue())
   378  
   379  	bas.AddTopValue("sync", bas.NewNamedObject("sync", 0).
   380  		SetProp("mutex", bas.Func("mutex", func(e *bas.Env) { e.A = bas.ValueOf(&sync.Mutex{}) })).
   381  		SetProp("rwmutex", bas.Func("rwmutex", func(e *bas.Env) { e.A = bas.ValueOf(&sync.RWMutex{}) })).
   382  		SetProp("waitgroup", bas.Func("waitgroup", func(e *bas.Env) { e.A = bas.ValueOf(&sync.WaitGroup{}) })).
   383  		ToValue())
   384  
   385  	bas.AddTopValue("base64", bas.NewObject(0).
   386  		SetProp("std", bas.ValueOf(base64.StdEncoding)).SetProp("rawstd", bas.ValueOf(base64.RawStdEncoding)).
   387  		SetProp("url", bas.ValueOf(base64.URLEncoding)).SetProp("rawurl", bas.ValueOf(base64.RawURLEncoding)).
   388  		ToValue())
   389  
   390  	bas.AddTopValue("base32", bas.NewObject(0).
   391  		SetProp("std", bas.ValueOf(base32.StdEncoding)).SetProp("rawstd", bas.ValueOf(base32.StdEncoding.WithPadding(-1))).
   392  		SetProp("hex", bas.ValueOf(base32.HexEncoding)).SetProp("rawhex", bas.ValueOf(base32.HexEncoding.WithPadding(-1))).
   393  		ToValue())
   394  
   395  	bas.AddTopValue("time", bas.Func("time", func(e *bas.Env) {
   396  		e.A = bas.Float64(float64(time.Now().UnixNano()) / 1e9)
   397  	}).Object().
   398  		SetProp("sleep", bas.Func("sleep", func(e *bas.Env) { time.Sleep(time.Duration(e.Float64(0)*1e6) * 1e3) })).
   399  		SetProp("ymd", bas.Func("ymd", func(e *bas.Env) {
   400  			e.A = bas.ValueOf(time.Date(e.Int(0), time.Month(e.Int(1)), e.Int(2),
   401  				e.IntDefault(3, 0), e.IntDefault(4, 0), e.IntDefault(5, 0), e.IntDefault(6, 0), time.UTC))
   402  		})).
   403  		SetProp("clock", bas.Func("clock", func(e *bas.Env) {
   404  			x := time.Now()
   405  			s := *(*[2]int64)(unsafe.Pointer(&x))
   406  			e.A = bas.Float64(float64(s[1]) / 1e9)
   407  		})).
   408  		SetProp("now", bas.Func("now", func(e *bas.Env) { e.A = bas.ValueOf(time.Now()) })).
   409  		SetProp("after", bas.Func("after", func(e *bas.Env) { e.A = bas.ValueOf(time.After(time.Duration(e.Float64(0)*1e6) * 1e3)) })).
   410  		SetProp("parse", bas.Func("parse", func(e *bas.Env) {
   411  			e.A = (*env)(e).valueOrError(time.Parse(getTimeFormat(e.Str(0)), e.Str(1)))
   412  		})).
   413  		SetProp("format", bas.Func("format", func(e *bas.Env) {
   414  			tt, ok := e.Get(1).Interface().(time.Time)
   415  			if !ok {
   416  				if t := e.Get(1); t.Type() == typ.Number {
   417  					tt = time.Unix(0, int64(t.Float64()*1e9))
   418  				} else {
   419  					tt = time.Now()
   420  				}
   421  			}
   422  			e.A = bas.Str(tt.Format(getTimeFormat(e.StrDefault(0, "", 0))))
   423  		})).
   424  		ToValue())
   425  
   426  	httpLib := bas.Func("http", func(e *bas.Env) {
   427  		args := e.Object(0)
   428  		method := strings.ToUpper(args.GetDefault(bas.Str("method"), bas.Str("GET")).Str())
   429  
   430  		u, err := url.Parse(args.Get(bas.Str("url")).AssertString("http URL"))
   431  		if err != nil {
   432  			e.A = bas.Error(e, err)
   433  			return
   434  		}
   435  
   436  		addKV := func(k string, add func(k, v string)) {
   437  			x := args.Get(bas.Str(k)).AssertShape("No", k)
   438  			x.Object().Foreach(func(k bas.Value, v *bas.Value) bool { add(k.String(), v.String()); return true })
   439  		}
   440  
   441  		additionalQueries := u.Query()
   442  		addKV("query", additionalQueries.Add) // append queries to url
   443  		u.RawQuery = additionalQueries.Encode()
   444  
   445  		var bodyReader io.Reader
   446  		dataForm, urlForm, jsonForm := (*multipart.Writer)(nil), false, false
   447  
   448  		if j := args.Get(bas.Str("json")); j != bas.Nil {
   449  			bodyReader = strings.NewReader(j.JSONString())
   450  			jsonForm = true
   451  		} else {
   452  			var form url.Values
   453  			if args.Contains(bas.Str("form")) {
   454  				form = url.Values{}
   455  				addKV("form", form.Add) // check "form"
   456  			}
   457  			urlForm = len(form) > 0
   458  			if urlForm {
   459  				bodyReader = strings.NewReader(form.Encode())
   460  			} else if rd := args.Get(bas.Str("data")); rd != bas.Nil {
   461  				bodyReader = rd.Reader()
   462  			}
   463  		}
   464  
   465  		if bodyReader == nil && (method == "POST" || method == "PUT") {
   466  			// Check form-data
   467  			payload := bytes.Buffer{}
   468  			writer := multipart.NewWriter(&payload)
   469  			outError := (error)(nil)
   470  			args.Get(bas.Str("multipart")).AssertShape("No", "multipart").Object().
   471  				Foreach(func(k bas.Value, v *bas.Value) bool {
   472  					key, rd := k.String(), *v
   473  					rd.AssertShape("<s,(s,R)>", "http multipart form data format")
   474  					if rd.Type() == typ.Native && rd.Len() == 2 { // [filename, reader]
   475  						fn := rd.Native().Get(0).Str()
   476  						if part, err := writer.CreateFormFile(key, fn); err != nil {
   477  							outError = fmt.Errorf("%s: %v", fn, err)
   478  							return false
   479  						} else if _, err = io.Copy(part, rd.Native().Get(1).Reader()); err != nil {
   480  							outError = fmt.Errorf("%s: %v", fn, err)
   481  							return false
   482  						}
   483  					} else {
   484  						if part, err := writer.CreateFormField(key); err != nil {
   485  							outError = fmt.Errorf("%s: %v", key, err)
   486  							return false
   487  						} else if _, err = io.Copy(part, rd.Reader()); err != nil {
   488  							outError = fmt.Errorf("%s: %v", key, err)
   489  							return false
   490  						}
   491  					}
   492  					return true
   493  				})
   494  			if outError != nil {
   495  				e.A = bas.Error(e, err)
   496  				return
   497  			}
   498  			if err := writer.Close(); err != nil {
   499  				e.A = bas.Error(e, err)
   500  				return
   501  			}
   502  			if payload.Len() > 0 {
   503  				bodyReader = &payload
   504  				dataForm = writer
   505  			}
   506  		}
   507  
   508  		req, err := http.NewRequest(method, u.String(), bodyReader)
   509  		if err != nil {
   510  			e.A = bas.Error(e, err)
   511  			return
   512  		}
   513  
   514  		switch {
   515  		case urlForm:
   516  			req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
   517  		case jsonForm:
   518  			req.Header.Add("Content-Type", "application/json")
   519  		case dataForm != nil:
   520  			req.Header.Add("Content-Type", dataForm.FormDataContentType())
   521  		}
   522  
   523  		addKV("header", req.Header.Add) // append headers
   524  
   525  		// Construct HTTP client
   526  		client := &http.Client{}
   527  		client.Timeout = args.GetDefault(bas.Str("timeout"), bas.Float64(1<<30)).AssertNumber("timeout seconds").Duration()
   528  		if v := args.Get(bas.Str("jar")); v.Type() == typ.Native {
   529  			client.Jar, _ = v.Interface().(http.CookieJar)
   530  		}
   531  		if !args.Get(bas.Str("noredirect")).IsFalse() {
   532  			client.CheckRedirect = func(*http.Request, []*http.Request) error {
   533  				return http.ErrUseLastResponse
   534  			}
   535  		}
   536  		if p := args.GetDefault(bas.Str("proxy"), bas.NullStr).Str(); p != "" {
   537  			client.Transport = &http.Transport{
   538  				Proxy: func(r *http.Request) (*url.URL, error) { return url.Parse(p) },
   539  			}
   540  		}
   541  		send := func(e *bas.Env) (code, headers, buf, jar bas.Value) {
   542  			resp, err := client.Do(req)
   543  			if err != nil {
   544  				err := bas.Error(e, err)
   545  				return err, err, err, err
   546  			}
   547  
   548  			if args.Get(bas.Str("br")).IsFalse() {
   549  				resp.Body.Close()
   550  			} else {
   551  				buf = bas.NewNativeWithMeta(resp.Body, &bas.Proto.ReadCloser).ToValue()
   552  			}
   553  			return bas.Int(resp.StatusCode), bas.ValueOf(resp.Header), buf, bas.ValueOf(client.Jar)
   554  		}
   555  		if f := args.Get(bas.Str("async")); f.IsObject() {
   556  			go func(e *bas.Env) {
   557  				code, hdr, buf, jar := send(e)
   558  				f.Object().Call(e, code, hdr, buf, jar)
   559  			}(e.Copy())
   560  			return
   561  		}
   562  		e.A = bas.Array(send(e))
   563  	}).Object().
   564  		SetProp("urlescape", bas.Func("urlescape", func(e *bas.Env) { e.A = bas.Str(url.QueryEscape(e.Str(0))) })).
   565  		SetProp("urlunescape", bas.Func("urlunescape", func(e *bas.Env) {
   566  			e.A = (*env)(e).valueOrError(url.QueryUnescape(e.Str(0)))
   567  		})).
   568  		SetProp("pathescape", bas.Func("pathescape", func(e *bas.Env) { e.A = bas.Str(url.PathEscape(e.Str(0))) })).
   569  		SetProp("pathunescape", bas.Func("pathunescape", func(e *bas.Env) {
   570  			e.A = (*env)(e).valueOrError(url.PathUnescape(e.Str(0)))
   571  		}))
   572  	for _, m := range []string{"get", "post", "put", "delete", "head", "patch"} {
   573  		m := m
   574  		httpLib = httpLib.AddMethod(m, func(e *bas.Env) {
   575  			ex := e.Shape(1, "No").Object()
   576  			e.A = e.Object(-1).Call(e, bas.NewObject(0).SetProp("method", bas.Str(m)).SetProp("url", e.Get(0)).Merge(ex).ToValue())
   577  		})
   578  	}
   579  	bas.AddTopValue("http", httpLib.ToValue())
   580  
   581  	bufferMeta := bas.NewEmptyNativeMeta("Buffer", bas.NewObject(0).
   582  		SetPrototype(bas.Proto.ReadWriter.Proto).
   583  		AddMethod("reset", func(e *bas.Env) { e.A.Interface().(*internal.LimitedBuffer).Reset() }).
   584  		AddMethod("value", func(e *bas.Env) { e.A = bas.UnsafeStr(e.A.Interface().(*internal.LimitedBuffer).Bytes()) }).
   585  		AddMethod("bytes", func(e *bas.Env) { e.A = bas.Bytes(e.A.Interface().(*internal.LimitedBuffer).Bytes()) }))
   586  
   587  	bas.AddTopFunc("buffer", func(e *bas.Env) {
   588  		b := &internal.LimitedBuffer{Limit: e.IntDefault(1, 0)}
   589  		bas.Write(b, e.Get(0))
   590  		e.A = bas.NewNativeWithMeta(b, bufferMeta).ToValue()
   591  	})
   592  }
   593  
   594  func minMax(e *bas.Env, max bool) {
   595  	v := e.Get(0)
   596  	for i := 1; i < e.Size(); i++ {
   597  		if x := e.Get(i); v.Less(x) == max {
   598  			v = x
   599  		}
   600  	}
   601  	e.A = v
   602  }
   603  
   604  var timeFormatMapping = map[interface{}]string{
   605  	"ansic": time.ANSIC, "ANSIC": time.ANSIC,
   606  	"unixdate": time.UnixDate, "UnixDate": time.UnixDate,
   607  	"rubydate": time.RubyDate, "RubyDate": time.RubyDate,
   608  	"rfc822": time.RFC822, "RFC822": time.RFC822,
   609  	"rfc822z": time.RFC822Z, "RFC822Z": time.RFC822Z,
   610  	"rfc850": time.RFC850, "RFC850": time.RFC850,
   611  	"rfc1123": time.RFC1123, "RFC1123": time.RFC1123,
   612  	"rfc1123z": time.RFC1123Z, "RFC1123Z": time.RFC1123Z,
   613  	"rfc3339": time.RFC3339, "RFC3339": time.RFC3339,
   614  	"rfc3339nano": time.RFC3339Nano, "RFC3339Nano": time.RFC3339Nano,
   615  	"kitchen": time.Kitchen, "Kitchen": time.Kitchen,
   616  	"stamp": time.Stamp, "Stamp": time.Stamp,
   617  	"stampmilli": time.StampMilli, "StampMilli": time.StampMilli,
   618  	"stampmicro": time.StampMicro, "StampMicro": time.StampMicro,
   619  	"stampnano": time.StampNano, "StampNano": time.StampNano,
   620  	'd': "02", 'D': "Mon", 'j': "2", 'l': "Monday", 'F': "January", 'z': "002", 'm': "01",
   621  	'M': "Jan", 'n': "1", 'Y': "2006", 'y': "06", 'a': "pm", 'A': "PM", 'g': "3", 'G': "15",
   622  	'h': "03", 'H': "15", 'i': "04", 's': "05", 'u': "05.000000", 'v': "05.000", 'O': "+0700",
   623  	'P': "-07:00", 'T': "MST",
   624  	'c': "2006-01-02T15:04:05-07:00",       //	ISO 860,
   625  	'r': "Mon, 02 Jan 2006 15:04:05 -0700", //	RFC 282,
   626  }
   627  
   628  func getTimeFormat(f string) string {
   629  	if tf, ok := timeFormatMapping[f]; ok {
   630  		return tf
   631  	}
   632  	buf := bytes.Buffer{}
   633  	for len(f) > 0 {
   634  		r, sz := utf8.DecodeRuneInString(f)
   635  		if sz == 0 {
   636  			break
   637  		}
   638  		if tf, ok := timeFormatMapping[r]; ok {
   639  			buf.WriteString(tf)
   640  		} else {
   641  			buf.WriteRune(r)
   642  		}
   643  		f = f[sz:]
   644  	}
   645  	return buf.String()
   646  }
   647  
   648  type env bas.Env
   649  
   650  func (e *env) valueOrError(v interface{}, err error) bas.Value {
   651  	if err != nil {
   652  		return bas.Error((*bas.Env)(e), err)
   653  	}
   654  	return bas.ValueOf(v)
   655  }