diff --git a/build/build.go b/build/build.go index 7ec14dafe..754c7a876 100644 --- a/build/build.go +++ b/build/build.go @@ -26,6 +26,8 @@ import ( "github.com/neelance/sourcemap" "github.com/shurcooL/httpfs/vfsutil" "golang.org/x/tools/go/buildutil" + + "github.com/visualfc/fastmod" ) type ImportCError struct { @@ -125,10 +127,10 @@ func Import(path string, mode build.ImportMode, installSuffix string, buildTags wd = "" } bctx := NewBuildContext(installSuffix, buildTags) - return importWithSrcDir(*bctx, path, wd, mode, installSuffix) + return importWithSrcDir(*bctx, path, wd, mode, installSuffix, nil) } -func importWithSrcDir(bctx build.Context, path string, srcDir string, mode build.ImportMode, installSuffix string) (*PackageData, error) { +func importWithSrcDir(bctx build.Context, path string, srcDir string, mode build.ImportMode, installSuffix string, mod *fastmod.Package) (*PackageData, error) { // bctx is passed by value, so it can be modified here. var isVirtual bool switch path { @@ -154,7 +156,20 @@ func importWithSrcDir(bctx build.Context, path string, srcDir string, mode build mode |= build.IgnoreVendor isVirtual = true } - pkg, err := bctx.Import(path, srcDir, mode) + var pkg *build.Package + var err error + if mod != nil { + if _, dir, typ := mod.Lookup(path); typ != fastmod.PkgTypeNil { + srcDir = dir + pkg, err = bctx.ImportDir(srcDir, mode) + if err == nil { + pkg.ImportPath = path + } + } + } + if pkg == nil { + pkg, err = bctx.Import(path, srcDir, mode) //bctx.Import(path, srcDir, mode) + } if err != nil { return nil, err } @@ -471,11 +486,17 @@ type PackageData struct { type Session struct { options *Options bctx *build.Context + Mod *fastmod.Package Archives map[string]*compiler.Archive Types map[string]*types.Package Watcher *fsnotify.Watcher } +func (s *Session) LoadMod(dir string) (err error) { + s.Mod, err = fastmod.LoadPackage(dir, s.bctx) + return +} + func NewSession(options *Options) *Session { if options.GOROOT == "" { options.GOROOT = build.Default.GOROOT @@ -579,7 +600,7 @@ func (s *Session) BuildImportPath(path string) (*compiler.Archive, error) { } func (s *Session) buildImportPathWithSrcDir(path string, srcDir string) (*PackageData, *compiler.Archive, error) { - pkg, err := importWithSrcDir(*s.bctx, path, srcDir, 0, s.InstallSuffix()) + pkg, err := importWithSrcDir(*s.bctx, path, srcDir, 0, s.InstallSuffix(), s.Mod) if s.Watcher != nil && pkg != nil { // add watch even on error s.Watcher.Add(pkg.Dir) } @@ -715,7 +736,7 @@ func (s *Session) BuildPackage(pkg *PackageData) (*compiler.Archive, error) { } if s.options.Verbose { - fmt.Println(pkg.ImportPath) + fmt.Println(pkg.Dir) } s.Archives[pkg.ImportPath] = archive diff --git a/circle.yml b/circle.yml index 6df7246d6..a620afe4e 100644 --- a/circle.yml +++ b/circle.yml @@ -11,7 +11,7 @@ jobs: - checkout - run: git clone https://github.com/creationix/nvm $HOME/.nvm && cd $HOME/.nvm && git checkout v0.33.9 && echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV && echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV - run: nvm install 10.0.0 && nvm alias default 10.0.0 - - run: cd /usr/local && sudo rm -rf go && curl https://storage.googleapis.com/golang/go1.13beta1.linux-amd64.tar.gz | sudo tar -xz + - run: cd /usr/local && sudo rm -rf go && curl https://storage.googleapis.com/golang/go1.13.6.linux-amd64.tar.gz | sudo tar -xz - run: echo 'export PATH="$PATH:/usr/local/go/bin:$HOME/go/bin"' >> $BASH_ENV - run: go get -t -d -v ./... - run: go install -v diff --git a/compiler/natives/src/internal/reflectlite/reflect.go b/compiler/natives/src/internal/reflectlite/reflect.go new file mode 100644 index 000000000..06b9c34ee --- /dev/null +++ b/compiler/natives/src/internal/reflectlite/reflect.go @@ -0,0 +1,1572 @@ +// +build js + +package reflectlite + +import ( + "unsafe" + + "github.com/gopherjs/gopherjs/js" +) + +var initialized = false + +func init() { + // avoid dead code elimination + used := func(i interface{}) {} + used(rtype{}) + used(uncommonType{}) + used(method{}) + used(arrayType{}) + used(chanType{}) + used(funcType{}) + used(interfaceType{}) + used(mapType{}) + used(ptrType{}) + used(sliceType{}) + used(structType{}) + used(imethod{}) + used(structField{}) + + initialized = true + //uint8Type = TypeOf(uint8(0)).(*rtype) // set for real +} + +var ( + idJsType = "__jsType" + idReflectType = "__reflectType" + idKindType = "__kindType" + idRtype = "__rtype" +) + +func jsType(typ Type) *js.Object { + return js.InternalObject(typ).Get(idJsType) +} + +func reflectType(typ *js.Object) *rtype { + if typ.Get(idReflectType) == js.Undefined { + rt := &rtype{ + size: uintptr(typ.Get("size").Int()), + kind: uint8(typ.Get("kind").Int()), + str: newNameOff(newName(internalStr(typ.Get("string")), "", typ.Get("exported").Bool())), + } + js.InternalObject(rt).Set(idJsType, typ) + typ.Set(idReflectType, js.InternalObject(rt)) + + methodSet := js.Global.Call("$methodSet", typ) + if methodSet.Length() != 0 || typ.Get("named").Bool() { + rt.tflag |= tflagUncommon + if typ.Get("named").Bool() { + rt.tflag |= tflagNamed + } + var reflectMethods []method + for i := 0; i < methodSet.Length(); i++ { // Exported methods first. + m := methodSet.Index(i) + exported := internalStr(m.Get("pkg")) == "" + if !exported { + continue + } + reflectMethods = append(reflectMethods, method{ + name: newNameOff(newName(internalStr(m.Get("name")), "", exported)), + mtyp: newTypeOff(reflectType(m.Get("typ"))), + }) + } + xcount := uint16(len(reflectMethods)) + for i := 0; i < methodSet.Length(); i++ { // Unexported methods second. + m := methodSet.Index(i) + exported := internalStr(m.Get("pkg")) == "" + if exported { + continue + } + reflectMethods = append(reflectMethods, method{ + name: newNameOff(newName(internalStr(m.Get("name")), "", exported)), + mtyp: newTypeOff(reflectType(m.Get("typ"))), + }) + } + ut := &uncommonType{ + pkgPath: newNameOff(newName(internalStr(typ.Get("pkg")), "", false)), + mcount: uint16(methodSet.Length()), + xcount: xcount, + _methods: reflectMethods, + } + uncommonTypeMap[rt] = ut + js.InternalObject(ut).Set(idJsType, typ) + } + + switch rt.Kind() { + case Array: + setKindType(rt, &arrayType{ + elem: reflectType(typ.Get("elem")), + len: uintptr(typ.Get("len").Int()), + }) + case Chan: + dir := BothDir + if typ.Get("sendOnly").Bool() { + dir = SendDir + } + if typ.Get("recvOnly").Bool() { + dir = RecvDir + } + setKindType(rt, &chanType{ + elem: reflectType(typ.Get("elem")), + dir: uintptr(dir), + }) + case Func: + params := typ.Get("params") + in := make([]*rtype, params.Length()) + for i := range in { + in[i] = reflectType(params.Index(i)) + } + results := typ.Get("results") + out := make([]*rtype, results.Length()) + for i := range out { + out[i] = reflectType(results.Index(i)) + } + outCount := uint16(results.Length()) + if typ.Get("variadic").Bool() { + outCount |= 1 << 15 + } + setKindType(rt, &funcType{ + rtype: *rt, + inCount: uint16(params.Length()), + outCount: outCount, + _in: in, + _out: out, + }) + case Interface: + methods := typ.Get("methods") + imethods := make([]imethod, methods.Length()) + for i := range imethods { + m := methods.Index(i) + imethods[i] = imethod{ + name: newNameOff(newName(internalStr(m.Get("name")), "", internalStr(m.Get("pkg")) == "")), + typ: newTypeOff(reflectType(m.Get("typ"))), + } + } + setKindType(rt, &interfaceType{ + rtype: *rt, + pkgPath: newName(internalStr(typ.Get("pkg")), "", false), + methods: imethods, + }) + case Map: + setKindType(rt, &mapType{ + key: reflectType(typ.Get("key")), + elem: reflectType(typ.Get("elem")), + }) + case Ptr: + setKindType(rt, &ptrType{ + elem: reflectType(typ.Get("elem")), + }) + case Slice: + setKindType(rt, &sliceType{ + elem: reflectType(typ.Get("elem")), + }) + case Struct: + fields := typ.Get("fields") + reflectFields := make([]structField, fields.Length()) + for i := range reflectFields { + f := fields.Index(i) + offsetEmbed := uintptr(i) << 1 + if f.Get("embedded").Bool() { + offsetEmbed |= 1 + } + reflectFields[i] = structField{ + name: newName(internalStr(f.Get("name")), internalStr(f.Get("tag")), f.Get("exported").Bool()), + typ: reflectType(f.Get("typ")), + offsetEmbed: offsetEmbed, + } + } + setKindType(rt, &structType{ + rtype: *rt, + pkgPath: newName(internalStr(typ.Get("pkgPath")), "", false), + fields: reflectFields, + }) + } + } + + return (*rtype)(unsafe.Pointer(typ.Get(idReflectType).Unsafe())) +} + +func setKindType(rt *rtype, kindType interface{}) { + js.InternalObject(rt).Set(idKindType, js.InternalObject(kindType)) + js.InternalObject(kindType).Set(idRtype, js.InternalObject(rt)) +} + +type uncommonType struct { + pkgPath nameOff + mcount uint16 + xcount uint16 + moff uint32 + + _methods []method +} + +func (t *uncommonType) methods() []method { + return t._methods +} + +func (t *uncommonType) exportedMethods() []method { + return t._methods[:t.xcount:t.xcount] +} + +var uncommonTypeMap = make(map[*rtype]*uncommonType) + +func (t *rtype) uncommon() *uncommonType { + return uncommonTypeMap[t] +} + +type funcType struct { + rtype `reflect:"func"` + inCount uint16 + outCount uint16 + + _in []*rtype + _out []*rtype +} + +func (t *funcType) in() []*rtype { + return t._in +} + +func (t *funcType) out() []*rtype { + return t._out +} + +type name struct { + bytes *byte +} + +type nameData struct { + name string + tag string + exported bool +} + +var nameMap = make(map[*byte]*nameData) + +func (n name) name() (s string) { return nameMap[n.bytes].name } +func (n name) tag() (s string) { return nameMap[n.bytes].tag } +func (n name) pkgPath() string { return "" } +func (n name) isExported() bool { return nameMap[n.bytes].exported } + +func newName(n, tag string, exported bool) name { + b := new(byte) + nameMap[b] = &nameData{ + name: n, + tag: tag, + exported: exported, + } + return name{ + bytes: b, + } +} + +var nameOffList []name + +func (t *rtype) nameOff(off nameOff) name { + return nameOffList[int(off)] +} + +func newNameOff(n name) nameOff { + i := len(nameOffList) + nameOffList = append(nameOffList, n) + return nameOff(i) +} + +var typeOffList []*rtype + +func (t *rtype) typeOff(off typeOff) *rtype { + return typeOffList[int(off)] +} + +func newTypeOff(t *rtype) typeOff { + i := len(typeOffList) + typeOffList = append(typeOffList, t) + return typeOff(i) +} + +func internalStr(strObj *js.Object) string { + var c struct{ str string } + js.InternalObject(c).Set("str", strObj) // get string without internalizing + return c.str +} + +func isWrapped(typ Type) bool { + return jsType(typ).Get("wrapped").Bool() +} + +func copyStruct(dst, src *js.Object, typ Type) { + fields := jsType(typ).Get("fields") + for i := 0; i < fields.Length(); i++ { + prop := fields.Index(i).Get("prop").String() + dst.Set(prop, src.Get(prop)) + } +} + +func makeValue(t Type, v *js.Object, fl flag) Value { + rt := t.common() + if t.Kind() == Array || t.Kind() == Struct || t.Kind() == Ptr { + return Value{rt, unsafe.Pointer(v.Unsafe()), fl | flag(t.Kind())} + } + return Value{rt, unsafe.Pointer(js.Global.Call("$newDataPointer", v, jsType(rt.ptrTo())).Unsafe()), fl | flag(t.Kind()) | flagIndir} +} + +func MakeSlice(typ Type, len, cap int) Value { + if typ.Kind() != Slice { + panic("reflect.MakeSlice of non-slice type") + } + if len < 0 { + panic("reflect.MakeSlice: negative len") + } + if cap < 0 { + panic("reflect.MakeSlice: negative cap") + } + if len > cap { + panic("reflect.MakeSlice: len > cap") + } + + return makeValue(typ, js.Global.Call("$makeSlice", jsType(typ), len, cap, js.InternalObject(func() *js.Object { return jsType(typ.Elem()).Call("zero") })), 0) +} + +func TypeOf(i interface{}) Type { + if !initialized { // avoid error of uint8Type + return &rtype{} + } + if i == nil { + return nil + } + return reflectType(js.InternalObject(i).Get("constructor")) +} + +func ValueOf(i interface{}) Value { + if i == nil { + return Value{} + } + return makeValue(reflectType(js.InternalObject(i).Get("constructor")), js.InternalObject(i).Get("$val"), 0) +} + +func ArrayOf(count int, elem Type) Type { + return reflectType(js.Global.Call("$arrayType", jsType(elem), count)) +} + +// func ChanOf(dir ChanDir, t Type) Type { +// return reflectType(js.Global.Call("$chanType", jsType(t), dir == SendDir, dir == RecvDir)) +// } + +func FuncOf(in, out []Type, variadic bool) Type { + if variadic && (len(in) == 0 || in[len(in)-1].Kind() != Slice) { + panic("reflect.FuncOf: last arg of variadic func must be slice") + } + + jsIn := make([]*js.Object, len(in)) + for i, v := range in { + jsIn[i] = jsType(v) + } + jsOut := make([]*js.Object, len(out)) + for i, v := range out { + jsOut[i] = jsType(v) + } + return reflectType(js.Global.Call("$funcType", jsIn, jsOut, variadic)) +} + +func MapOf(key, elem Type) Type { + switch key.Kind() { + case Func, Map, Slice: + panic("reflect.MapOf: invalid key type " + key.String()) + } + + return reflectType(js.Global.Call("$mapType", jsType(key), jsType(elem))) +} + +func (t *rtype) ptrTo() *rtype { + return reflectType(js.Global.Call("$ptrType", jsType(t))) +} + +func SliceOf(t Type) Type { + return reflectType(js.Global.Call("$sliceType", jsType(t))) +} + +// func StructOf(fields []StructField) Type { +// jsFields := make([]*js.Object, len(fields)) +// fset := map[string]struct{}{} +// for i, f := range fields { +// if f.Type == nil { +// panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no type") +// } + +// name := f.Name +// if name == "" { +// // Embedded field +// if f.Type.Kind() == Ptr { +// // Embedded ** and *interface{} are illegal +// elem := f.Type.Elem() +// if k := elem.Kind(); k == Ptr || k == Interface { +// panic("reflect.StructOf: illegal anonymous field type " + f.Type.String()) +// } +// name = elem.String() +// } else { +// name = f.Type.String() +// } +// } + +// if _, dup := fset[name]; dup { +// panic("reflect.StructOf: duplicate field " + name) +// } +// fset[name] = struct{}{} + +// jsf := js.Global.Get("Object").New() +// jsf.Set("prop", name) +// jsf.Set("name", name) +// jsf.Set("exported", true) +// jsf.Set("typ", jsType(f.Type)) +// jsf.Set("tag", f.Tag) +// jsFields[i] = jsf +// } +// return reflectType(js.Global.Call("$structType", "", jsFields)) +// } + +func Zero(typ Type) Value { + return makeValue(typ, jsType(typ).Call("zero"), 0) +} + +func unsafe_New(typ *rtype) unsafe.Pointer { + switch typ.Kind() { + case Struct: + return unsafe.Pointer(jsType(typ).Get("ptr").New().Unsafe()) + case Array: + return unsafe.Pointer(jsType(typ).Call("zero").Unsafe()) + default: + return unsafe.Pointer(js.Global.Call("$newDataPointer", jsType(typ).Call("zero"), jsType(typ.ptrTo())).Unsafe()) + } +} + +func makeInt(f flag, bits uint64, t Type) Value { + typ := t.common() + ptr := unsafe_New(typ) + switch typ.Kind() { + case Int8: + *(*int8)(ptr) = int8(bits) + case Int16: + *(*int16)(ptr) = int16(bits) + case Int, Int32: + *(*int32)(ptr) = int32(bits) + case Int64: + *(*int64)(ptr) = int64(bits) + case Uint8: + *(*uint8)(ptr) = uint8(bits) + case Uint16: + *(*uint16)(ptr) = uint16(bits) + case Uint, Uint32, Uintptr: + *(*uint32)(ptr) = uint32(bits) + case Uint64: + *(*uint64)(ptr) = uint64(bits) + } + return Value{typ, ptr, f | flagIndir | flag(typ.Kind())} +} + +func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value { + if typ.Kind() != Func { + panic("reflect: call of MakeFunc with non-Func type") + } + + t := typ.common() + ftyp := (*funcType)(unsafe.Pointer(t)) + + fv := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { + args := make([]Value, ftyp.NumIn()) + for i := range args { + argType := ftyp.In(i).common() + args[i] = makeValue(argType, arguments[i], 0) + } + resultsSlice := fn(args) + switch ftyp.NumOut() { + case 0: + return nil + case 1: + return resultsSlice[0].object() + default: + results := js.Global.Get("Array").New(ftyp.NumOut()) + for i, r := range resultsSlice { + results.SetIndex(i, r.object()) + } + return results + } + }) + + return Value{t, unsafe.Pointer(fv.Unsafe()), flag(Func)} +} + +func typedmemmove(t *rtype, dst, src unsafe.Pointer) { + js.InternalObject(dst).Call("$set", js.InternalObject(src).Call("$get")) +} + +func loadScalar(p unsafe.Pointer, n uintptr) uintptr { + return js.InternalObject(p).Call("$get").Unsafe() +} + +func makechan(typ *rtype, size int) (ch unsafe.Pointer) { + ctyp := (*chanType)(unsafe.Pointer(typ)) + return unsafe.Pointer(js.Global.Get("$Chan").New(jsType(ctyp.elem), size).Unsafe()) +} + +func makemap(t *rtype, cap int) (m unsafe.Pointer) { + return unsafe.Pointer(js.Global.Get("Object").New().Unsafe()) +} + +func keyFor(t *rtype, key unsafe.Pointer) (*js.Object, string) { + kv := js.InternalObject(key) + if kv.Get("$get") != js.Undefined { + kv = kv.Call("$get") + } + k := jsType(t.Key()).Call("keyFor", kv).String() + return kv, k +} + +// func mapaccess(t *rtype, m, key unsafe.Pointer) unsafe.Pointer { +// _, k := keyFor(t, key) +// entry := js.InternalObject(m).Get(k) +// if entry == js.Undefined { +// return nil +// } +// return unsafe.Pointer(js.Global.Call("$newDataPointer", entry.Get("v"), jsType(PtrTo(t.Elem()))).Unsafe()) +// } + +// func mapassign(t *rtype, m, key, val unsafe.Pointer) { +// kv, k := keyFor(t, key) +// jsVal := js.InternalObject(val).Call("$get") +// et := t.Elem() +// if et.Kind() == Struct { +// newVal := jsType(et).Call("zero") +// copyStruct(newVal, jsVal, et) +// jsVal = newVal +// } +// entry := js.Global.Get("Object").New() +// entry.Set("k", kv) +// entry.Set("v", jsVal) +// js.InternalObject(m).Set(k, entry) +// } + +// func mapdelete(t *rtype, m unsafe.Pointer, key unsafe.Pointer) { +// _, k := keyFor(t, key) +// js.InternalObject(m).Delete(k) +// } + +// type mapIter struct { +// t Type +// m *js.Object +// keys *js.Object +// i int + +// // last is the last object the iterator indicates. If this object exists, the functions that return the +// // current key or value returns this object, regardless of the current iterator. It is because the current +// // iterator might be stale due to key deletion in a loop. +// last *js.Object +// } + +// func (iter *mapIter) skipUntilValidKey() { +// for iter.i < iter.keys.Length() { +// k := iter.keys.Index(iter.i) +// if iter.m.Get(k.String()) != js.Undefined { +// break +// } +// // The key is already deleted. Move on the next item. +// iter.i++ +// } +// } + +// func mapiterinit(t *rtype, m unsafe.Pointer) unsafe.Pointer { +// return unsafe.Pointer(&mapIter{t, js.InternalObject(m), js.Global.Call("$keys", js.InternalObject(m)), 0, nil}) +// } + +// func mapiterkey(it unsafe.Pointer) unsafe.Pointer { +// iter := (*mapIter)(it) +// var kv *js.Object +// if iter.last != nil { +// kv = iter.last +// } else { +// iter.skipUntilValidKey() +// if iter.i == iter.keys.Length() { +// return nil +// } +// k := iter.keys.Index(iter.i) +// kv = iter.m.Get(k.String()) + +// // Record the key-value pair for later accesses. +// iter.last = kv +// } +// return unsafe.Pointer(js.Global.Call("$newDataPointer", kv.Get("k"), jsType(PtrTo(iter.t.Key()))).Unsafe()) +// } + +// func mapitervalue(it unsafe.Pointer) unsafe.Pointer { +// iter := (*mapIter)(it) +// var kv *js.Object +// if iter.last != nil { +// kv = iter.last +// } else { +// iter.skipUntilValidKey() +// if iter.i == iter.keys.Length() { +// return nil +// } +// k := iter.keys.Index(iter.i) +// kv = iter.m.Get(k.String()) +// iter.last = kv +// } +// return unsafe.Pointer(js.Global.Call("$newDataPointer", kv.Get("v"), jsType(PtrTo(iter.t.Elem()))).Unsafe()) +// } + +// func mapiternext(it unsafe.Pointer) { +// iter := (*mapIter)(it) +// iter.last = nil +// iter.i++ +// } + +func maplen(m unsafe.Pointer) int { + return js.Global.Call("$keys", js.InternalObject(m)).Length() +} + +// func cvtDirect(v Value, typ Type) Value { +// var srcVal = v.object() +// if srcVal == jsType(v.typ).Get("nil") { +// return makeValue(typ, jsType(typ).Get("nil"), v.flag) +// } + +// var val *js.Object +// switch k := typ.Kind(); k { +// case Slice: +// slice := jsType(typ).New(srcVal.Get("$array")) +// slice.Set("$offset", srcVal.Get("$offset")) +// slice.Set("$length", srcVal.Get("$length")) +// slice.Set("$capacity", srcVal.Get("$capacity")) +// val = js.Global.Call("$newDataPointer", slice, jsType(PtrTo(typ))) +// case Ptr: +// if typ.Elem().Kind() == Struct { +// if typ.Elem() == v.typ.Elem() { +// val = srcVal +// break +// } +// val = jsType(typ).New() +// copyStruct(val, srcVal, typ.Elem()) +// break +// } +// val = jsType(typ).New(srcVal.Get("$get"), srcVal.Get("$set")) +// case Struct: +// val = jsType(typ).Get("ptr").New() +// copyStruct(val, srcVal, typ) +// case Array, Bool, Chan, Func, Interface, Map, String: +// val = js.InternalObject(v.ptr) +// default: +// panic(&ValueError{"reflect.Convert", k}) +// } +// return Value{typ.common(), unsafe.Pointer(val.Unsafe()), v.flag.ro() | v.flag&flagIndir | flag(typ.Kind())} +// } + +// func Copy(dst, src Value) int { +// dk := dst.kind() +// if dk != Array && dk != Slice { +// panic(&ValueError{"reflect.Copy", dk}) +// } +// if dk == Array { +// dst.mustBeAssignable() +// } +// dst.mustBeExported() + +// sk := src.kind() +// var stringCopy bool +// if sk != Array && sk != Slice { +// stringCopy = sk == String && dst.typ.Elem().Kind() == Uint8 +// if !stringCopy { +// panic(&ValueError{"reflect.Copy", sk}) +// } +// } +// src.mustBeExported() + +// if !stringCopy { +// typesMustMatch("reflect.Copy", dst.typ.Elem(), src.typ.Elem()) +// } + +// dstVal := dst.object() +// if dk == Array { +// dstVal = jsType(SliceOf(dst.typ.Elem())).New(dstVal) +// } + +// srcVal := src.object() +// if sk == Array { +// srcVal = jsType(SliceOf(src.typ.Elem())).New(srcVal) +// } + +// if stringCopy { +// return js.Global.Call("$copyString", dstVal, srcVal).Int() +// } +// return js.Global.Call("$copySlice", dstVal, srcVal).Int() +// } + +func methodReceiver(op string, v Value, i int) (_ *rtype, t *funcType, fn unsafe.Pointer) { + var prop string + if v.typ.Kind() == Interface { + tt := (*interfaceType)(unsafe.Pointer(v.typ)) + if i < 0 || i >= len(tt.methods) { + panic("reflect: internal error: invalid method index") + } + m := &tt.methods[i] + if !tt.nameOff(m.name).isExported() { + panic("reflect: " + op + " of unexported method") + } + t = (*funcType)(unsafe.Pointer(tt.typeOff(m.typ))) + prop = tt.nameOff(m.name).name() + } else { + ms := v.typ.exportedMethods() + if uint(i) >= uint(len(ms)) { + panic("reflect: internal error: invalid method index") + } + m := ms[i] + if !v.typ.nameOff(m.name).isExported() { + panic("reflect: " + op + " of unexported method") + } + t = (*funcType)(unsafe.Pointer(v.typ.typeOff(m.mtyp))) + prop = js.Global.Call("$methodSet", jsType(v.typ)).Index(i).Get("prop").String() + } + rcvr := v.object() + if isWrapped(v.typ) { + rcvr = jsType(v.typ).New(rcvr) + } + fn = unsafe.Pointer(rcvr.Get(prop).Unsafe()) + return +} + +func valueInterface(v Value) interface{} { + if v.flag == 0 { + panic(&ValueError{"reflect.Value.Interface", 0}) + } + // if safe && v.flag&flagRO != 0 { + // panic("reflect.Value.Interface: cannot return value obtained from unexported field or method") + // } + if v.flag&flagMethod != 0 { + v = makeMethodValue("Interface", v) + } + + if isWrapped(v.typ) { + return interface{}(unsafe.Pointer(jsType(v.typ).New(v.object()).Unsafe())) + } + return interface{}(unsafe.Pointer(v.object().Unsafe())) +} + +func ifaceE2I(t *rtype, src interface{}, dst unsafe.Pointer) { + js.InternalObject(dst).Call("$set", js.InternalObject(src)) +} + +func methodName() string { + return "?FIXME?" +} + +func makeMethodValue(op string, v Value) Value { + if v.flag&flagMethod == 0 { + panic("reflect: internal error: invalid use of makePartialFunc") + } + + _, _, fn := methodReceiver(op, v, int(v.flag)>>flagMethodShift) + rcvr := v.object() + if isWrapped(v.typ) { + rcvr = jsType(v.typ).New(rcvr) + } + fv := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { + return js.InternalObject(fn).Call("apply", rcvr, arguments) + }) + return Value{v.Type().common(), unsafe.Pointer(fv.Unsafe()), v.flag.ro() | flag(Func)} +} + +func (t *rtype) pointers() bool { + switch t.Kind() { + case Ptr, Map, Chan, Func, Struct, Array: + return true + default: + return false + } +} + +// TODO Comparable +// func (t *rtype) Comparable() bool { +// switch t.Kind() { +// case Func, Slice, Map: +// return false +// case Array: +// return t.Elem().Comparable() +// case Struct: +// for i := 0; i < t.NumField(); i++ { +// if !t.Field(i).Type.Comparable() { +// return false +// } +// } +// } +// return true +// } + +// func (t *rtype) Method(i int) (m Method) { +// if t.Kind() == Interface { +// tt := (*interfaceType)(unsafe.Pointer(t)) +// return tt.Method(i) +// } +// methods := t.exportedMethods() +// if i < 0 || i >= len(methods) { +// panic("reflect: Method index out of range") +// } +// p := methods[i] +// pname := t.nameOff(p.name) +// m.Name = pname.name() +// fl := flag(Func) +// mtyp := t.typeOff(p.mtyp) +// ft := (*funcType)(unsafe.Pointer(mtyp)) +// in := make([]Type, 0, 1+len(ft.in())) +// in = append(in, t) +// for _, arg := range ft.in() { +// in = append(in, arg) +// } +// out := make([]Type, 0, len(ft.out())) +// for _, ret := range ft.out() { +// out = append(out, ret) +// } +// mt := FuncOf(in, out, ft.IsVariadic()) +// m.Type = mt +// prop := js.Global.Call("$methodSet", js.InternalObject(t).Get(idJsType)).Index(i).Get("prop").String() +// fn := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { +// rcvr := arguments[0] +// return rcvr.Get(prop).Call("apply", rcvr, arguments[1:]) +// }) +// m.Func = Value{mt.(*rtype), unsafe.Pointer(fn.Unsafe()), fl} + +// m.Index = i +// return m +// } + +func (v Value) object() *js.Object { + if v.typ.Kind() == Array || v.typ.Kind() == Struct { + return js.InternalObject(v.ptr) + } + if v.flag&flagIndir != 0 { + val := js.InternalObject(v.ptr).Call("$get") + if val != js.Global.Get("$ifaceNil") && val.Get("constructor") != jsType(v.typ) { + switch v.typ.Kind() { + case Uint64, Int64: + val = jsType(v.typ).New(val.Get("$high"), val.Get("$low")) + case Complex64, Complex128: + val = jsType(v.typ).New(val.Get("$real"), val.Get("$imag")) + case Slice: + if val == val.Get("constructor").Get("nil") { + val = jsType(v.typ).Get("nil") + break + } + newVal := jsType(v.typ).New(val.Get("$array")) + newVal.Set("$offset", val.Get("$offset")) + newVal.Set("$length", val.Get("$length")) + newVal.Set("$capacity", val.Get("$capacity")) + val = newVal + } + } + return js.InternalObject(val.Unsafe()) + } + return js.InternalObject(v.ptr) +} + +func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value { + if v.flag&flagMethod != 0 { + v = makeMethodValue(context, v) + } + + switch { + case directlyAssignable(dst, v.typ): + // Overwrite type so that they match. + // Same memory layout, so no harm done. + fl := v.flag&(flagAddr|flagIndir) | v.flag.ro() + fl |= flag(dst.Kind()) + return Value{dst, v.ptr, fl} + + case implements(dst, v.typ): + if target == nil { + target = unsafe_New(dst) + } + // GopherJS: Skip the v.Kind() == Interface && v.IsNil() if statement + // from upstream. ifaceE2I below does not panic, and it needs + // to run, given its custom implementation. + x := valueInterface(v) + if dst.NumMethod() == 0 { + *(*interface{})(target) = x + } else { + ifaceE2I(dst, x, target) + } + return Value{dst, target, flagIndir | flag(Interface)} + } + + // Failed. + panic(context + ": value of type " + v.typ.String() + " is not assignable to type " + dst.String()) +} + +var callHelper = js.Global.Get("$call").Interface().(func(...interface{}) *js.Object) + +// func (v Value) call(op string, in []Value) []Value { +// var ( +// t *funcType +// fn unsafe.Pointer +// rcvr *js.Object +// ) +// if v.flag&flagMethod != 0 { +// _, t, fn = methodReceiver(op, v, int(v.flag)>>flagMethodShift) +// rcvr = v.object() +// if isWrapped(v.typ) { +// rcvr = jsType(v.typ).New(rcvr) +// } +// } else { +// t = (*funcType)(unsafe.Pointer(v.typ)) +// fn = unsafe.Pointer(v.object().Unsafe()) +// rcvr = js.Undefined +// } + +// if fn == nil { +// panic("reflect.Value.Call: call of nil function") +// } + +// isSlice := op == "CallSlice" +// n := t.NumIn() +// if isSlice { +// if !t.IsVariadic() { +// panic("reflect: CallSlice of non-variadic function") +// } +// if len(in) < n { +// panic("reflect: CallSlice with too few input arguments") +// } +// if len(in) > n { +// panic("reflect: CallSlice with too many input arguments") +// } +// } else { +// if t.IsVariadic() { +// n-- +// } +// if len(in) < n { +// panic("reflect: Call with too few input arguments") +// } +// if !t.IsVariadic() && len(in) > n { +// panic("reflect: Call with too many input arguments") +// } +// } +// for _, x := range in { +// if x.Kind() == Invalid { +// panic("reflect: " + op + " using zero Value argument") +// } +// } +// for i := 0; i < n; i++ { +// if xt, targ := in[i].Type(), t.In(i); !xt.AssignableTo(targ) { +// panic("reflect: " + op + " using " + xt.String() + " as type " + targ.String()) +// } +// } +// if !isSlice && t.IsVariadic() { +// // prepare slice for remaining values +// m := len(in) - n +// slice := MakeSlice(t.In(n), m, m) +// elem := t.In(n).Elem() +// for i := 0; i < m; i++ { +// x := in[n+i] +// if xt := x.Type(); !xt.AssignableTo(elem) { +// panic("reflect: cannot use " + xt.String() + " as type " + elem.String() + " in " + op) +// } +// slice.Index(i).Set(x) +// } +// origIn := in +// in = make([]Value, n+1) +// copy(in[:n], origIn) +// in[n] = slice +// } + +// nin := len(in) +// if nin != t.NumIn() { +// panic("reflect.Value.Call: wrong argument count") +// } +// nout := t.NumOut() + +// argsArray := js.Global.Get("Array").New(t.NumIn()) +// for i, arg := range in { +// argsArray.SetIndex(i, unwrapJsObject(t.In(i), arg.assignTo("reflect.Value.Call", t.In(i).common(), nil).object())) +// } +// results := callHelper(js.InternalObject(fn), rcvr, argsArray) + +// switch nout { +// case 0: +// return nil +// case 1: +// return []Value{makeValue(t.Out(0), wrapJsObject(t.Out(0), results), 0)} +// default: +// ret := make([]Value, nout) +// for i := range ret { +// ret[i] = makeValue(t.Out(i), wrapJsObject(t.Out(i), results.Index(i)), 0) +// } +// return ret +// } +// } + +func (v Value) Cap() int { + k := v.kind() + switch k { + case Array: + return v.typ.Len() + case Chan, Slice: + return v.object().Get("$capacity").Int() + } + panic(&ValueError{"reflect.Value.Cap", k}) +} + +var jsObjectPtr = reflectType(js.Global.Get("$jsObjectPtr")) + +func wrapJsObject(typ Type, val *js.Object) *js.Object { + if typ == jsObjectPtr { + return jsType(jsObjectPtr).New(val) + } + return val +} + +func unwrapJsObject(typ Type, val *js.Object) *js.Object { + if typ == jsObjectPtr { + return val.Get("object") + } + return val +} + +func (v Value) Elem() Value { + switch k := v.kind(); k { + case Interface: + val := v.object() + if val == js.Global.Get("$ifaceNil") { + return Value{} + } + typ := reflectType(val.Get("constructor")) + return makeValue(typ, val.Get("$val"), v.flag.ro()) + + case Ptr: + if v.IsNil() { + return Value{} + } + val := v.object() + tt := (*ptrType)(unsafe.Pointer(v.typ)) + fl := v.flag&flagRO | flagIndir | flagAddr + fl |= flag(tt.elem.Kind()) + return Value{tt.elem, unsafe.Pointer(wrapJsObject(tt.elem, val).Unsafe()), fl} + + default: + panic(&ValueError{"reflect.Value.Elem", k}) + } +} + +// func (v Value) Field(i int) Value { +// if v.kind() != Struct { +// panic(&ValueError{"reflect.Value.Field", v.kind()}) +// } +// tt := (*structType)(unsafe.Pointer(v.typ)) +// if uint(i) >= uint(len(tt.fields)) { +// panic("reflect: Field index out of range") +// } + +// prop := jsType(v.typ).Get("fields").Index(i).Get("prop").String() +// field := &tt.fields[i] +// typ := field.typ + +// fl := v.flag&(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind()) +// if !field.name.isExported() { +// if field.embedded() { +// fl |= flagEmbedRO +// } else { +// fl |= flagStickyRO +// } +// } + +// if tag := tt.fields[i].name.tag(); tag != "" && i != 0 { +// if jsTag := getJsTag(tag); jsTag != "" { +// for { +// v = v.Field(0) +// if v.typ == jsObjectPtr { +// o := v.object().Get("object") +// return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( +// js.InternalObject(func() *js.Object { return js.Global.Call("$internalize", o.Get(jsTag), jsType(typ)) }), +// js.InternalObject(func(x *js.Object) { o.Set(jsTag, js.Global.Call("$externalize", x, jsType(typ))) }), +// ).Unsafe()), fl} +// } +// if v.typ.Kind() == Ptr { +// v = v.Elem() +// } +// } +// } +// } + +// s := js.InternalObject(v.ptr) +// if fl&flagIndir != 0 && typ.Kind() != Array && typ.Kind() != Struct { +// return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( +// js.InternalObject(func() *js.Object { return wrapJsObject(typ, s.Get(prop)) }), +// js.InternalObject(func(x *js.Object) { s.Set(prop, unwrapJsObject(typ, x)) }), +// ).Unsafe()), fl} +// } +// return makeValue(typ, wrapJsObject(typ, s.Get(prop)), fl) +// } + +func getJsTag(tag string) string { + for tag != "" { + // skip leading space + i := 0 + for i < len(tag) && tag[i] == ' ' { + i++ + } + tag = tag[i:] + if tag == "" { + break + } + + // scan to colon. + // a space or a quote is a syntax error + i = 0 + for i < len(tag) && tag[i] != ' ' && tag[i] != ':' && tag[i] != '"' { + i++ + } + if i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { + break + } + name := string(tag[:i]) + tag = tag[i+1:] + + // scan quoted string to find value + i = 1 + for i < len(tag) && tag[i] != '"' { + if tag[i] == '\\' { + i++ + } + i++ + } + if i >= len(tag) { + break + } + qvalue := string(tag[:i+1]) + tag = tag[i+1:] + + if name == "js" { + value, _ := unquote(qvalue) + return value + } + } + return "" +} + +// func (v Value) Index(i int) Value { +// switch k := v.kind(); k { +// case Array: +// tt := (*arrayType)(unsafe.Pointer(v.typ)) +// if i < 0 || i > int(tt.len) { +// panic("reflect: array index out of range") +// } +// typ := tt.elem +// fl := v.flag&(flagIndir|flagAddr) | v.flag.ro() | flag(typ.Kind()) + +// a := js.InternalObject(v.ptr) +// if fl&flagIndir != 0 && typ.Kind() != Array && typ.Kind() != Struct { +// return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( +// js.InternalObject(func() *js.Object { return wrapJsObject(typ, a.Index(i)) }), +// js.InternalObject(func(x *js.Object) { a.SetIndex(i, unwrapJsObject(typ, x)) }), +// ).Unsafe()), fl} +// } +// return makeValue(typ, wrapJsObject(typ, a.Index(i)), fl) + +// case Slice: +// s := v.object() +// if i < 0 || i >= s.Get("$length").Int() { +// panic("reflect: slice index out of range") +// } +// tt := (*sliceType)(unsafe.Pointer(v.typ)) +// typ := tt.elem +// fl := flagAddr | flagIndir | v.flag.ro() | flag(typ.Kind()) + +// i += s.Get("$offset").Int() +// a := s.Get("$array") +// if fl&flagIndir != 0 && typ.Kind() != Array && typ.Kind() != Struct { +// return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( +// js.InternalObject(func() *js.Object { return wrapJsObject(typ, a.Index(i)) }), +// js.InternalObject(func(x *js.Object) { a.SetIndex(i, unwrapJsObject(typ, x)) }), +// ).Unsafe()), fl} +// } +// return makeValue(typ, wrapJsObject(typ, a.Index(i)), fl) + +// case String: +// str := *(*string)(v.ptr) +// if i < 0 || i >= len(str) { +// panic("reflect: string index out of range") +// } +// fl := v.flag.ro() | flag(Uint8) | flagIndir +// c := str[i] +// return Value{uint8Type, unsafe.Pointer(&c), fl} + +// default: +// panic(&ValueError{"reflect.Value.Index", k}) +// } +// } + +func (v Value) InterfaceData() [2]uintptr { + panic("InterfaceData is not supported by GopherJS") +} + +func (v Value) IsNil() bool { + switch k := v.kind(); k { + case Ptr, Slice: + return v.object() == jsType(v.typ).Get("nil") + case Chan: + return v.object() == js.Global.Get("$chanNil") + case Func: + return v.object() == js.Global.Get("$throwNilPointerError") + case Map: + return v.object() == js.InternalObject(false) + case Interface: + return v.object() == js.Global.Get("$ifaceNil") + case UnsafePointer: + return v.object().Unsafe() == 0 + default: + panic(&ValueError{"reflect.Value.IsNil", k}) + } +} + +func (v Value) Len() int { + switch k := v.kind(); k { + case Array, String: + return v.object().Length() + case Slice: + return v.object().Get("$length").Int() + case Chan: + return v.object().Get("$buffer").Get("length").Int() + case Map: + return js.Global.Call("$keys", v.object()).Length() + default: + panic(&ValueError{"reflect.Value.Len", k}) + } +} + +func (v Value) Pointer() uintptr { + switch k := v.kind(); k { + case Chan, Map, Ptr, UnsafePointer: + if v.IsNil() { + return 0 + } + return v.object().Unsafe() + case Func: + if v.IsNil() { + return 0 + } + return 1 + case Slice: + if v.IsNil() { + return 0 + } + return v.object().Get("$array").Unsafe() + default: + panic(&ValueError{"reflect.Value.Pointer", k}) + } +} + +func (v Value) Set(x Value) { + v.mustBeAssignable() + x.mustBeExported() + x = x.assignTo("reflect.Set", v.typ, nil) + if v.flag&flagIndir != 0 { + switch v.typ.Kind() { + case Array: + jsType(v.typ).Call("copy", js.InternalObject(v.ptr), js.InternalObject(x.ptr)) + case Interface: + js.InternalObject(v.ptr).Call("$set", js.InternalObject(valueInterface(x))) + case Struct: + copyStruct(js.InternalObject(v.ptr), js.InternalObject(x.ptr), v.typ) + default: + js.InternalObject(v.ptr).Call("$set", x.object()) + } + return + } + v.ptr = x.ptr +} + +func (v Value) SetBytes(x []byte) { + v.mustBeAssignable() + v.mustBe(Slice) + if v.typ.Elem().Kind() != Uint8 { + panic("reflect.Value.SetBytes of non-byte slice") + } + slice := js.InternalObject(x) + if v.typ.Name() != "" || v.typ.Elem().Name() != "" { + typedSlice := jsType(v.typ).New(slice.Get("$array")) + typedSlice.Set("$offset", slice.Get("$offset")) + typedSlice.Set("$length", slice.Get("$length")) + typedSlice.Set("$capacity", slice.Get("$capacity")) + slice = typedSlice + } + js.InternalObject(v.ptr).Call("$set", slice) +} + +func (v Value) SetCap(n int) { + v.mustBeAssignable() + v.mustBe(Slice) + s := js.InternalObject(v.ptr).Call("$get") + if n < s.Get("$length").Int() || n > s.Get("$capacity").Int() { + panic("reflect: slice capacity out of range in SetCap") + } + newSlice := jsType(v.typ).New(s.Get("$array")) + newSlice.Set("$offset", s.Get("$offset")) + newSlice.Set("$length", s.Get("$length")) + newSlice.Set("$capacity", n) + js.InternalObject(v.ptr).Call("$set", newSlice) +} + +func (v Value) SetLen(n int) { + v.mustBeAssignable() + v.mustBe(Slice) + s := js.InternalObject(v.ptr).Call("$get") + if n < 0 || n > s.Get("$capacity").Int() { + panic("reflect: slice length out of range in SetLen") + } + newSlice := jsType(v.typ).New(s.Get("$array")) + newSlice.Set("$offset", s.Get("$offset")) + newSlice.Set("$length", n) + newSlice.Set("$capacity", s.Get("$capacity")) + js.InternalObject(v.ptr).Call("$set", newSlice) +} + +func (v Value) Slice(i, j int) Value { + var ( + cap int + typ Type + s *js.Object + ) + switch kind := v.kind(); kind { + case Array: + if v.flag&flagAddr == 0 { + panic("reflect.Value.Slice: slice of unaddressable array") + } + tt := (*arrayType)(unsafe.Pointer(v.typ)) + cap = int(tt.len) + typ = SliceOf(tt.elem) + s = jsType(typ).New(v.object()) + + case Slice: + typ = v.typ + s = v.object() + cap = s.Get("$capacity").Int() + + case String: + str := *(*string)(v.ptr) + if i < 0 || j < i || j > len(str) { + panic("reflect.Value.Slice: string slice index out of bounds") + } + return ValueOf(str[i:j]) + + default: + panic(&ValueError{"reflect.Value.Slice", kind}) + } + + if i < 0 || j < i || j > cap { + panic("reflect.Value.Slice: slice index out of bounds") + } + + return makeValue(typ, js.Global.Call("$subslice", s, i, j), v.flag.ro()) +} + +func (v Value) Slice3(i, j, k int) Value { + var ( + cap int + typ Type + s *js.Object + ) + switch kind := v.kind(); kind { + case Array: + if v.flag&flagAddr == 0 { + panic("reflect.Value.Slice: slice of unaddressable array") + } + tt := (*arrayType)(unsafe.Pointer(v.typ)) + cap = int(tt.len) + typ = SliceOf(tt.elem) + s = jsType(typ).New(v.object()) + + case Slice: + typ = v.typ + s = v.object() + cap = s.Get("$capacity").Int() + + default: + panic(&ValueError{"reflect.Value.Slice3", kind}) + } + + if i < 0 || j < i || k < j || k > cap { + panic("reflect.Value.Slice3: slice index out of bounds") + } + + return makeValue(typ, js.Global.Call("$subslice", s, i, j, k), v.flag.ro()) +} + +func (v Value) Close() { + v.mustBe(Chan) + v.mustBeExported() + js.Global.Call("$close", v.object()) +} + +// TODO +func (v Value) mustBe(f Kind) { + +} + +// TODO +func (v Value) mustBeExported() { + +} + +var selectHelper = js.Global.Get("$select").Interface().(func(...interface{}) *js.Object) + +// func chanrecv(ch unsafe.Pointer, nb bool, val unsafe.Pointer) (selected, received bool) { +// comms := [][]*js.Object{{js.InternalObject(ch)}} +// if nb { +// comms = append(comms, []*js.Object{}) +// } +// selectRes := selectHelper(comms) +// if nb && selectRes.Index(0).Int() == 1 { +// return false, false +// } +// recvRes := selectRes.Index(1) +// js.InternalObject(val).Call("$set", recvRes.Index(0)) +// return true, recvRes.Index(1).Bool() +// } + +// func chansend(ch unsafe.Pointer, val unsafe.Pointer, nb bool) bool { +// comms := [][]*js.Object{{js.InternalObject(ch), js.InternalObject(val).Call("$get")}} +// if nb { +// comms = append(comms, []*js.Object{}) +// } +// selectRes := selectHelper(comms) +// if nb && selectRes.Index(0).Int() == 1 { +// return false +// } +// return true +// } + +// func rselect(rselects []runtimeSelect) (chosen int, recvOK bool) { +// comms := make([][]*js.Object, len(rselects)) +// for i, s := range rselects { +// switch SelectDir(s.dir) { +// case SelectDefault: +// comms[i] = []*js.Object{} +// case SelectRecv: +// ch := js.Global.Get("$chanNil") +// if js.InternalObject(s.ch) != js.InternalObject(0) { +// ch = js.InternalObject(s.ch) +// } +// comms[i] = []*js.Object{ch} +// case SelectSend: +// ch := js.Global.Get("$chanNil") +// var val *js.Object +// if js.InternalObject(s.ch) != js.InternalObject(0) { +// ch = js.InternalObject(s.ch) +// val = js.InternalObject(s.val).Call("$get") +// } +// comms[i] = []*js.Object{ch, val} +// } +// } +// selectRes := selectHelper(comms) +// c := selectRes.Index(0).Int() +// if SelectDir(rselects[c].dir) == SelectRecv { +// recvRes := selectRes.Index(1) +// js.InternalObject(rselects[c].val).Call("$set", recvRes.Index(0)) +// return c, recvRes.Index(1).Bool() +// } +// return c, false +// } + +// func DeepEqual(a1, a2 interface{}) bool { +// i1 := js.InternalObject(a1) +// i2 := js.InternalObject(a2) +// if i1 == i2 { +// return true +// } +// if i1 == nil || i2 == nil || i1.Get("constructor") != i2.Get("constructor") { +// return false +// } +// return deepValueEqualJs(ValueOf(a1), ValueOf(a2), nil) +// } + +// func deepValueEqualJs(v1, v2 Value, visited [][2]unsafe.Pointer) bool { +// if !v1.IsValid() || !v2.IsValid() { +// return !v1.IsValid() && !v2.IsValid() +// } +// if v1.Type() != v2.Type() { +// return false +// } +// if v1.Type() == jsObjectPtr { +// return unwrapJsObject(jsObjectPtr, v1.object()) == unwrapJsObject(jsObjectPtr, v2.object()) +// } + +// switch v1.Kind() { +// case Array, Map, Slice, Struct: +// for _, entry := range visited { +// if v1.ptr == entry[0] && v2.ptr == entry[1] { +// return true +// } +// } +// visited = append(visited, [2]unsafe.Pointer{v1.ptr, v2.ptr}) +// } + +// switch v1.Kind() { +// case Array, Slice: +// if v1.Kind() == Slice { +// if v1.IsNil() != v2.IsNil() { +// return false +// } +// if v1.object() == v2.object() { +// return true +// } +// } +// var n = v1.Len() +// if n != v2.Len() { +// return false +// } +// for i := 0; i < n; i++ { +// if !deepValueEqualJs(v1.Index(i), v2.Index(i), visited) { +// return false +// } +// } +// return true +// case Interface: +// if v1.IsNil() || v2.IsNil() { +// return v1.IsNil() && v2.IsNil() +// } +// return deepValueEqualJs(v1.Elem(), v2.Elem(), visited) +// case Ptr: +// return deepValueEqualJs(v1.Elem(), v2.Elem(), visited) +// case Struct: +// //TODO +// var n = v1.typ.NumField() +// for i := 0; i < n; i++ { +// if !deepValueEqualJs(v1.Field(i), v2.Field(i), visited) { +// return false +// } +// } +// return true +// case Map: +// if v1.IsNil() != v2.IsNil() { +// return false +// } +// if v1.object() == v2.object() { +// return true +// } +// // TODO +// // tt := (*mapType)(unsafe.Pointer(v1.typ)) + +// // var keys = v1.MapKeys() +// // if len(keys) != v2.Len() { +// // return false +// // } +// // for _, k := range keys { +// // val1 := v1.MapIndex(k) +// // val2 := v2.MapIndex(k) +// // if !val1.IsValid() || !val2.IsValid() || !deepValueEqualJs(val1, val2, visited) { +// // return false +// // } +// // } +// return true +// case Func: +// return v1.IsNil() && v2.IsNil() +// case UnsafePointer: +// return v1.object() == v2.object() +// } + +// return js.Global.Call("$interfaceIsEqual", js.InternalObject(valueInterface(v1)), js.InternalObject(valueInterface(v2))).Bool() +// } diff --git a/compiler/natives/src/internal/reflectlite/swapper.go b/compiler/natives/src/internal/reflectlite/swapper.go new file mode 100644 index 000000000..7e61d254a --- /dev/null +++ b/compiler/natives/src/internal/reflectlite/swapper.go @@ -0,0 +1,36 @@ +// +build js + +package reflectlite + +import "github.com/gopherjs/gopherjs/js" + +func Swapper(slice interface{}) func(i, j int) { + v := ValueOf(slice) + if v.Kind() != Slice { + panic(&ValueError{Method: "Swapper", Kind: v.Kind()}) + } + // Fast path for slices of size 0 and 1. Nothing to swap. + vLen := uint(v.Len()) + switch vLen { + case 0: + return func(i, j int) { panic("reflect: slice index out of range") } + case 1: + return func(i, j int) { + if i != 0 || j != 0 { + panic("reflect: slice index out of range") + } + } + } + a := js.InternalObject(slice).Get("$array") + off := js.InternalObject(slice).Get("$offset").Int() + return func(i, j int) { + if uint(i) >= vLen || uint(j) >= vLen { + panic("reflect: slice index out of range") + } + i += off + j += off + tmp := a.Index(i) + a.SetIndex(i, a.Index(j)) + a.SetIndex(j, tmp) + } +} diff --git a/compiler/natives/src/internal/reflectlite/utils.go b/compiler/natives/src/internal/reflectlite/utils.go new file mode 100644 index 000000000..2ed49658a --- /dev/null +++ b/compiler/natives/src/internal/reflectlite/utils.go @@ -0,0 +1,34 @@ +package reflectlite + +type ChanDir int + +const ( + RecvDir ChanDir = 1 << iota // <-chan + SendDir // chan<- + BothDir = RecvDir | SendDir // chan +) + +type errorString struct { + s string +} + +func (e *errorString) Error() string { + return e.s +} + +var ( + ErrSyntax = &errorString{"invalid syntax"} +) + +func unquote(s string) (string, error) { + if len(s) < 2 { + return s, nil + } + if s[0] == '\'' || s[0] == '"' { + if s[len(s)-1] == s[0] { + return s[1 : len(s)-1], nil + } + return "", ErrSyntax + } + return s, nil +} diff --git a/compiler/natives/src/reflect/reflect.go b/compiler/natives/src/reflect/reflect.go index 3d0cc4e0f..57774cd42 100644 --- a/compiler/natives/src/reflect/reflect.go +++ b/compiler/natives/src/reflect/reflect.go @@ -279,6 +279,15 @@ func newTypeOff(t *rtype) typeOff { return typeOff(i) } +// addReflectOff adds a pointer to the reflection lookup map in the runtime. +// It returns a new ID that can be used as a typeOff or textOff, and will +// be resolved correctly. Implemented in the runtime package. +func addReflectOff(ptr unsafe.Pointer) int32 { + i := len(typeOffList) + typeOffList = append(typeOffList, (*rtype)(ptr)) + return int32(i) +} + func internalStr(strObj *js.Object) string { var c struct{ str string } js.InternalObject(c).Set("str", strObj) // get string without internalizing @@ -591,7 +600,7 @@ func mapiterkey(it unsafe.Pointer) unsafe.Pointer { return unsafe.Pointer(js.Global.Call("$newDataPointer", kv.Get("k"), jsType(PtrTo(iter.t.Key()))).Unsafe()) } -func mapitervalue(it unsafe.Pointer) unsafe.Pointer { +func mapiterelem(it unsafe.Pointer) unsafe.Pointer { iter := (*mapIter)(it) var kv *js.Object if iter.last != nil { diff --git a/compiler/natives/src/syscall/js/go113_js.go b/compiler/natives/src/syscall/js/go113_js.go new file mode 100644 index 000000000..7a2230d7b --- /dev/null +++ b/compiler/natives/src/syscall/js/go113_js.go @@ -0,0 +1,36 @@ +// +build js + +package js + +// CopyBytesToGo copies bytes from the Uint8Array src to dst. +// It returns the number of bytes copied, which will be the minimum of the lengths of src and dst. +// CopyBytesToGo panics if src is not an Uint8Array. +func CopyBytesToGo(dst []byte, src Value) int { + return copy(dst, src.internal().Interface().([]byte)) +} + +// func CopyBytesToGo(dst []byte, src Value) int { +// s := make([]byte, src.Get("byteLength").Int()) +// a := TypedArrayOf(s) +// a.Call("set", src) +// a.Release() +// return copy(dst, s) +// } + +// CopyBytesToJS copies bytes from src to the Uint8Array dst. +// It returns the number of bytes copied, which will be the minimum of the lengths of src and dst. +// CopyBytesToJS panics if dst is not an Uint8Array. +func CopyBytesToJS(dst Value, src []byte) int { + return copy(dst.internal().Interface().([]byte), src) +} + +// func CopyBytesToJS(dst Value, src []byte) int { +// n := dst.Get("byteLength").Int() +// if n > len(src) { +// n = len(src) +// } +// a := TypedArrayOf(src[:n]) +// dst.Call("set", a) +// a.Release() +// return n +// } diff --git a/compiler/natives/src/syscall/js/js.go b/compiler/natives/src/syscall/js/js.go index 81af995b7..a3db7c4a6 100644 --- a/compiler/natives/src/syscall/js/js.go +++ b/compiler/natives/src/syscall/js/js.go @@ -194,11 +194,21 @@ func (v Value) Float() float64 { return v.internal().Float() } +func (t Type) isObject() bool { + return t == TypeObject || t == TypeFunction +} + func (v Value) Get(p string) Value { + if vType := v.Type(); !vType.isObject() { + panic(&ValueError{"Value.Get", vType}) + } return objectToValue(v.internal().Get(p)) } func (v Value) Index(i int) Value { + if vType := v.Type(); !vType.isObject() { + panic(&ValueError{"Value.Index", vType}) + } return objectToValue(v.internal().Index(i)) } @@ -228,20 +238,67 @@ func (v Value) Length() int { return v.internal().Length() } +func (v Value) newWrap(args ...interface{}) (obj *js.Object, err error) { + defer func() { + ret := recover() + if r, ok := ret.(*js.Error); ok { + err = r + } + }() + obj = v.internal().New(convertArgs(args...)...) + return +} + func (v Value) New(args ...interface{}) Value { - return objectToValue(v.internal().New(convertArgs(args...)...)) + obj, err := v.newWrap(args...) + if err != nil { + if vType := v.Type(); vType != TypeFunction { // check here to avoid overhead in success case + panic(&ValueError{"Value.Invoke", vType}) + } + panic(Error{objectToValue(obj)}) + } + return objectToValue(obj) } func (v Value) Set(p string, x interface{}) { + if vType := v.Type(); !vType.isObject() { + panic(&ValueError{"Value.Set", vType}) + } v.internal().Set(p, convertArgs(x)[0]) } func (v Value) SetIndex(i int, x interface{}) { + if vType := v.Type(); !vType.isObject() { + panic(&ValueError{"Value.SetIndex", vType}) + } v.internal().SetIndex(i, convertArgs(x)[0]) } +// func (v Value) String() string { +// return v.internal().String() +// } + func (v Value) String() string { - return v.internal().String() + switch v.Type() { + case TypeString: + return v.internal().String() + case TypeUndefined: + return "" + case TypeNull: + return "" + case TypeBoolean: + return "" + case TypeNumber: + return "" + case TypeSymbol: + return "" + case TypeObject: + return "" + case TypeFunction: + return "" + default: + panic("bad type") + } } func (v Value) Truthy() bool { diff --git a/go.mod b/go.mod new file mode 100644 index 000000000..9d8f67f82 --- /dev/null +++ b/go.mod @@ -0,0 +1,18 @@ +module github.com/gopherjs/gopherjs + +go 1.13 + +require ( + github.com/fsnotify/fsnotify v1.4.7 + github.com/kisielk/gotool v1.0.0 + github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86 + github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab + github.com/shurcooL/go v0.0.0-20191216061654-b114cc39af9f + github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 + github.com/spf13/cobra v0.0.5 + github.com/spf13/pflag v1.0.5 + github.com/visualfc/fastmod v1.0.0 + golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d + golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 + golang.org/x/tools v0.0.0-20200131143746-097c1f2eed26 +) diff --git a/go.sum b/go.sum new file mode 100644 index 000000000..9463e11cf --- /dev/null +++ b/go.sum @@ -0,0 +1,69 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de h1:F7WD09S8QB4LrkEpka0dFPLSotH11HRpCsLIbIcJ7sU= +github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86 h1:D6paGObi5Wud7xg83MaEFyjxQB1W5bz5d0IFppr+ymk= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab h1:eFXv9Nu1lGbrNbj619aWwZfVF5HBrm9Plte8aNptuTI= +github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/shurcooL/go v0.0.0-20191216061654-b114cc39af9f h1:gvOuVMIvQ0Ca/afBH/YBfp5f2RWb92DbAI5EjwoFLuc= +github.com/shurcooL/go v0.0.0-20191216061654-b114cc39af9f/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/visualfc/fastmod v1.0.0 h1:3UcYFM+mzdk5AeKF7itO7D7rEv9Vkk0f2HOZi6KCMh8= +github.com/visualfc/fastmod v1.0.0/go.mod h1:MjR7AyXKzcr5S9uEeJPI4xID37/q88a8FSBS/2H8AFc= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200117160349-530e935923ad h1:Jh8cai0fqIK+f6nG0UgPW5wFk8wmiMhM3AyciDBdtQg= +golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U= +golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20200125223703-d33eef8e6825 h1:aNQeSIHKi0RWpKA5NO0CqyLjx6Beh5l0LLUEnndEjz0= +golang.org/x/tools v0.0.0-20200125223703-d33eef8e6825/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200131143746-097c1f2eed26 h1:Xm3yOuwULU693JtNquv+un7Gw/4duNW4Ybem7SaAthY= +golang.org/x/tools v0.0.0-20200131143746-097c1f2eed26/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/tool.go b/tool.go index 4c580a139..3a73ecd71 100644 --- a/tool.go +++ b/tool.go @@ -123,7 +123,6 @@ func main() { // Expand import path patterns. patternContext := gbuild.NewBuildContext("", options.BuildTags) pkgs := (&gotool.Context{BuildContext: *patternContext}).ImportPaths(args) - for _, pkgPath := range pkgs { if s.Watcher != nil { pkg, err := gbuild.NewBuildContext(s.InstallSuffix(), options.BuildTags).Import(pkgPath, "", build.FindOnly) @@ -136,6 +135,8 @@ func main() { if err != nil { return err } + s.LoadMod(pkg.Dir) + archive, err := s.BuildPackage(pkg) if err != nil { return err @@ -196,6 +197,7 @@ func main() { if err != nil { return err } + s.LoadMod(pkg.Dir) archive, err := s.BuildPackage(pkg) if err != nil { @@ -331,6 +333,7 @@ func main() { continue } s := gbuild.NewSession(options) + s.LoadMod(pkg.Dir) tests := &testFuncs{BuildContext: s.BuildContext(), Package: pkg.Package} collectTests := func(testPkg *gbuild.PackageData, testPkgName string, needVar *bool) error { @@ -584,6 +587,8 @@ func (fs serveCommandFileSystem) Open(requestName string) (http.File, error) { switch { case isPkg: + s.LoadMod(pkg.Dir) + buf := new(bytes.Buffer) browserErrors := new(bytes.Buffer) err := func() error {