diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index b8b9369f37..f782ce0974 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -70,7 +70,7 @@ const ( ) func structfieldSize() int { return 3 * Widthptr } // Sizeof(runtime.structfield{}) -func imethodSize() int { return 2 * Widthptr } // Sizeof(runtime.imethod{}) +func imethodSize() int { return 4 + 4 } // Sizeof(runtime.imethod{}) func uncommonSize(t *Type) int { // Sizeof(runtime.uncommontype{}) if t.Sym == nil && len(methods(t)) == 0 { return 0 @@ -647,13 +647,11 @@ func dextratypeData(s *Sym, ot int, t *Type) int { pkg = a.pkg } nsym := dname(a.name, "", pkg, exported) - ot = dsymptrLSym(lsym, ot, nsym, 0) + + ot = dsymptrOffLSym(lsym, ot, nsym, 0) ot = dmethodptrOffLSym(lsym, ot, Linksym(dtypesym(a.mtype))) ot = dmethodptrOffLSym(lsym, ot, Linksym(a.isym)) ot = dmethodptrOffLSym(lsym, ot, Linksym(a.tsym)) - if Widthptr == 8 { - ot = duintxxLSym(lsym, ot, 0, 4) // pad to reflect.method size - } } return ot } @@ -1226,6 +1224,7 @@ ok: dataAdd := imethodSize() * n ot = dextratype(s, ot, t, dataAdd) + lsym := Linksym(s) for _, a := range m { // ../../../../runtime/type.go:/imethod exported := exportname(a.name) @@ -1234,8 +1233,9 @@ ok: pkg = a.pkg } nsym := dname(a.name, "", pkg, exported) - ot = dsymptrLSym(Linksym(s), ot, nsym, 0) - ot = dsymptr(s, ot, dtypesym(a.type_), 0) + + ot = dsymptrOffLSym(lsym, ot, nsym, 0) + ot = dsymptrOffLSym(lsym, ot, Linksym(dtypesym(a.type_)), 0) } // ../../../../runtime/type.go:/mapType diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go index 4725b91d01..5eb20c2fb2 100644 --- a/src/cmd/link/internal/ld/decodesym.go +++ b/src/cmd/link/internal/ld/decodesym.go @@ -262,8 +262,9 @@ const ( ) // decode_methodsig decodes an array of method signature information. -// Each element of the array is size bytes. The first word is a -// reflect.name for the name, the second word is a *rtype for the funcType. +// Each element of the array is size bytes. The first 4 bytes is a +// nameOff for the method name, and the next 4 bytes is a typeOff for +// the function type. // // Conveniently this is the layout of both runtime.method and runtime.imethod. func decode_methodsig(s *LSym, off, size, count int) []methodsig { @@ -271,7 +272,7 @@ func decode_methodsig(s *LSym, off, size, count int) []methodsig { var methods []methodsig for i := 0; i < count; i++ { buf.WriteString(decodetype_name(s, off)) - mtypSym := decode_reloc_sym(s, int32(off+SysArch.PtrSize)) + mtypSym := decode_reloc_sym(s, int32(off+4)) buf.WriteRune('(') inCount := decodetype_funcincount(mtypSym) @@ -311,7 +312,7 @@ func decodetype_ifacemethods(s *LSym) []methodsig { } off := int(r.Add) // array of reflect.imethod values numMethods := int(decodetype_ifacemethodcount(s)) - sizeofIMethod := 2 * SysArch.PtrSize + sizeofIMethod := 4 + 4 return decode_methodsig(s, off, sizeofIMethod, numMethods) } @@ -343,12 +344,7 @@ func decodetype_methods(s *LSym) []methodsig { mcount := int(decode_inuxi(s.P[off+SysArch.PtrSize:], 2)) moff := int(decode_inuxi(s.P[off+SysArch.PtrSize+2:], 2)) - off += moff // offset to array of reflect.method values - var sizeofMethod int // sizeof reflect.method in program - if SysArch.PtrSize == 4 { - sizeofMethod = 4 * SysArch.PtrSize - } else { - sizeofMethod = 3 * SysArch.PtrSize - } + off += moff // offset to array of reflect.method values + const sizeofMethod = 4 * 4 // sizeof reflect.method in program return decode_methodsig(s, off, sizeofMethod, mcount) } diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index 96e8de5030..1f07a4eb77 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -427,7 +427,7 @@ func symtab() { if !DynlinkingGo() { s.Attr |= AttrHidden } - if UseRelro() && len(s.R) > 0 { + if UseRelro() { s.Type = obj.STYPERELRO s.Outer = symtyperel } else { diff --git a/src/reflect/export_test.go b/src/reflect/export_test.go index 2769e0db40..f527434f0d 100644 --- a/src/reflect/export_test.go +++ b/src/reflect/export_test.go @@ -50,7 +50,8 @@ func TypeLinks() []string { for i, offs := range offset { rodata := sections[i] for _, off := range offs { - r = append(r, rtypeOff(rodata, off).string) + typ := (*rtype)(resolveTypeOff(unsafe.Pointer(rodata), off)) + r = append(r, typ.string) } } return r @@ -91,10 +92,11 @@ func FirstMethodNameBytes(t Type) *byte { panic("type has no methods") } m := ut.methods()[0] - if *m.name.data(0)&(1<<2) == 0 { + mname := t.(*rtype).nameOff(m.name) + if *mname.data(0)&(1<<2) == 0 { panic("method name does not have pkgPath *string") } - return m.name.bytes + return mname.bytes } type OtherPkgFields struct { diff --git a/src/reflect/type.go b/src/reflect/type.go index b8c778cc2b..0cae69a79c 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -288,7 +288,7 @@ type typeAlg struct { // Method on non-interface type type method struct { - name name // name of method + name nameOff // name of method mtyp typeOff // method type (without receiver) ifn textOff // fn used in interface call (one-word receiver) tfn textOff // fn used for normal method call @@ -347,8 +347,8 @@ type funcType struct { // imethod represents a method on an interface type type imethod struct { - name name // name of method - typ *rtype // .(*FuncType) underneath + name nameOff // name of method + typ typeOff // .(*FuncType) underneath } // interfaceType represents an interface type. @@ -424,19 +424,19 @@ type name struct { bytes *byte } -func (n *name) data(off int) *byte { +func (n name) data(off int) *byte { return (*byte)(add(unsafe.Pointer(n.bytes), uintptr(off))) } -func (n *name) isExported() bool { +func (n name) isExported() bool { return (*n.bytes)&(1<<0) != 0 } -func (n *name) nameLen() int { +func (n name) nameLen() int { return int(uint16(*n.data(1))<<8 | uint16(*n.data(2))) } -func (n *name) tagLen() int { +func (n name) tagLen() int { if *n.data(0)&(1<<1) == 0 { return 0 } @@ -444,7 +444,7 @@ func (n *name) tagLen() int { return int(uint16(*n.data(off))<<8 | uint16(*n.data(off + 1))) } -func (n *name) name() (s string) { +func (n name) name() (s string) { if n.bytes == nil { return "" } @@ -458,7 +458,7 @@ func (n *name) name() (s string) { return s } -func (n *name) tag() (s string) { +func (n name) tag() (s string) { tl := n.tagLen() if tl == 0 { return "" @@ -470,7 +470,7 @@ func (n *name) tag() (s string) { return s } -func (n *name) pkgPath() string { +func (n name) pkgPath() string { if n.bytes == nil || *n.data(0)&(1<<2) == 0 { return "" } @@ -480,7 +480,7 @@ func (n *name) pkgPath() string { } var nameOff int32 copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.data(off)))[:]) - pkgPathName := name{(*byte)(resolveTypeOff(unsafe.Pointer(n), nameOff))} + pkgPathName := name{(*byte)(resolveTypeOff(unsafe.Pointer(n.bytes), nameOff))} return pkgPathName.name() } @@ -605,6 +605,11 @@ func (t *uncommonType) PkgPath() string { return t.pkgPath.name() } +// resolveNameOff resolves a name offset from a base pointer. +// The (*rtype).nameOff method is a convenience wrapper for this function. +// Implemented in the runtime package. +func resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer + // resolveTypeOff resolves an *rtype offset from a base type. // The (*rtype).typeOff method is a convenience wrapper for this function. // Implemented in the runtime package. @@ -620,6 +625,12 @@ func resolveTextOff(rtype unsafe.Pointer, off int32) unsafe.Pointer // be resolved correctly. Implemented in the runtime package. func addReflectOff(ptr unsafe.Pointer) int32 +// resolveReflectType adds a name to the reflection lookup map in the runtime. +// It returns a new nameOff that can be used to refer to the pointer. +func resolveReflectName(n name) nameOff { + return nameOff(addReflectOff(unsafe.Pointer(n.bytes))) +} + // resolveReflectType adds a *rtype to the reflection lookup map in the runtime. // It returns a new typeOff that can be used to refer to the pointer. func resolveReflectType(t *rtype) typeOff { @@ -633,9 +644,17 @@ func resolveReflectText(ptr unsafe.Pointer) textOff { return textOff(addReflectOff(ptr)) } +type nameOff int32 // offset to a name type typeOff int32 // offset to an *rtype type textOff int32 // offset from top of text section +func (t *rtype) nameOff(off nameOff) name { + if off == 0 { + return name{} + } + return name{(*byte)(resolveNameOff(unsafe.Pointer(t), int32(off)))} +} + func (t *rtype) typeOff(off typeOff) *rtype { if off == 0 { return nil @@ -753,10 +772,11 @@ func (t *rtype) Method(i int) (m Method) { panic("reflect: Method index out of range") } p := ut.methods()[i] - m.Name = p.name.name() + pname := t.nameOff(p.name) + m.Name = pname.name() fl := flag(Func) - if !p.name.isExported() { - m.PkgPath = p.name.pkgPath() + if !pname.isExported() { + m.PkgPath = pname.pkgPath() if m.PkgPath == "" { m.PkgPath = ut.pkgPath.name() } @@ -796,7 +816,8 @@ func (t *rtype) MethodByName(name string) (m Method, ok bool) { utmethods := ut.methods() for i := 0; i < int(ut.mcount); i++ { p := utmethods[i] - if p.name.name() == name { + pname := t.nameOff(p.name) + if pname.name() == name { return t.Method(i), true } } @@ -1005,14 +1026,15 @@ func (t *interfaceType) Method(i int) (m Method) { return } p := &t.methods[i] - m.Name = p.name.name() - if !p.name.isExported() { - m.PkgPath = p.name.pkgPath() + pname := t.nameOff(p.name) + m.Name = pname.name() + if !pname.isExported() { + m.PkgPath = pname.pkgPath() if m.PkgPath == "" { m.PkgPath = t.pkgPath.name() } } - m.Type = toType(p.typ) + m.Type = toType(t.typeOff(p.typ)) m.Index = i return } @@ -1028,7 +1050,7 @@ func (t *interfaceType) MethodByName(name string) (m Method, ok bool) { var p *imethod for i := range t.methods { p = &t.methods[i] - if p.name.name() == name { + if t.nameOff(p.name).name() == name { return t.Method(i), true } } @@ -1468,7 +1490,7 @@ func implements(T, V *rtype) bool { for j := 0; j < len(v.methods); j++ { tm := &t.methods[i] vm := &v.methods[j] - if vm.name.name() == tm.name.name() && vm.typ == tm.typ { + if V.nameOff(vm.name).name() == t.nameOff(tm.name).name() && V.typeOff(vm.typ) == t.typeOff(tm.typ) { if i++; i >= len(t.methods) { return true } @@ -1486,7 +1508,7 @@ func implements(T, V *rtype) bool { for j := 0; j < int(v.mcount); j++ { tm := &t.methods[i] vm := vmethods[j] - if vm.name.name() == tm.name.name() && V.typeOff(vm.mtyp) == tm.typ { + if V.nameOff(vm.name).name() == t.nameOff(tm.name).name() && V.typeOff(vm.mtyp) == t.typeOff(tm.typ) { if i++; i >= len(t.methods) { return true } @@ -2327,12 +2349,13 @@ func StructOf(fields []StructField) Type { case Interface: ift := (*interfaceType)(unsafe.Pointer(ft)) for im, m := range ift.methods { - if m.name.pkgPath() != "" { + if ift.nameOff(m.name).pkgPath() != "" { // TODO(sbinet) panic("reflect: embedded interface with unexported method(s) not implemented") } var ( + mtyp = ift.typeOff(m.typ) ifield = i imethod = im ifn Value @@ -2340,7 +2363,7 @@ func StructOf(fields []StructField) Type { ) if ft.kind&kindDirectIface != 0 { - tfn = MakeFunc(m.typ, func(in []Value) []Value { + tfn = MakeFunc(mtyp, func(in []Value) []Value { var args []Value var recv = in[0] if len(in) > 1 { @@ -2348,7 +2371,7 @@ func StructOf(fields []StructField) Type { } return recv.Field(ifield).Method(imethod).Call(args) }) - ifn = MakeFunc(m.typ, func(in []Value) []Value { + ifn = MakeFunc(mtyp, func(in []Value) []Value { var args []Value var recv = in[0] if len(in) > 1 { @@ -2357,7 +2380,7 @@ func StructOf(fields []StructField) Type { return recv.Field(ifield).Method(imethod).Call(args) }) } else { - tfn = MakeFunc(m.typ, func(in []Value) []Value { + tfn = MakeFunc(mtyp, func(in []Value) []Value { var args []Value var recv = in[0] if len(in) > 1 { @@ -2365,7 +2388,7 @@ func StructOf(fields []StructField) Type { } return recv.Field(ifield).Method(imethod).Call(args) }) - ifn = MakeFunc(m.typ, func(in []Value) []Value { + ifn = MakeFunc(mtyp, func(in []Value) []Value { var args []Value var recv = Indirect(in[0]) if len(in) > 1 { @@ -2376,8 +2399,8 @@ func StructOf(fields []StructField) Type { } methods = append(methods, method{ - name: m.name, - mtyp: resolveReflectType(m.typ), + name: resolveReflectName(ift.nameOff(m.name)), + mtyp: resolveReflectType(mtyp), ifn: resolveReflectText(unsafe.Pointer(&ifn)), tfn: resolveReflectText(unsafe.Pointer(&tfn)), }) @@ -2386,12 +2409,13 @@ func StructOf(fields []StructField) Type { ptr := (*ptrType)(unsafe.Pointer(ft)) if unt := ptr.uncommon(); unt != nil { for _, m := range unt.methods() { - if m.name.pkgPath() != "" { + mname := ptr.nameOff(m.name) + if mname.pkgPath() != "" { // TODO(sbinet) panic("reflect: embedded interface with unexported method(s) not implemented") } methods = append(methods, method{ - name: m.name, + name: resolveReflectName(mname), mtyp: resolveReflectType(ptr.typeOff(m.mtyp)), ifn: resolveReflectText(ptr.textOff(m.ifn)), tfn: resolveReflectText(ptr.textOff(m.tfn)), @@ -2400,12 +2424,13 @@ func StructOf(fields []StructField) Type { } if unt := ptr.elem.uncommon(); unt != nil { for _, m := range unt.methods() { - if m.name.pkgPath() != "" { + mname := ptr.nameOff(m.name) + if mname.pkgPath() != "" { // TODO(sbinet) panic("reflect: embedded interface with unexported method(s) not implemented") } methods = append(methods, method{ - name: m.name, + name: resolveReflectName(mname), mtyp: resolveReflectType(ptr.elem.typeOff(m.mtyp)), ifn: resolveReflectText(ptr.elem.textOff(m.ifn)), tfn: resolveReflectText(ptr.elem.textOff(m.tfn)), @@ -2415,12 +2440,13 @@ func StructOf(fields []StructField) Type { default: if unt := ft.uncommon(); unt != nil { for _, m := range unt.methods() { - if m.name.pkgPath() != "" { + mname := ft.nameOff(m.name) + if mname.pkgPath() != "" { // TODO(sbinet) panic("reflect: embedded interface with unexported method(s) not implemented") } methods = append(methods, method{ - name: m.name, + name: resolveReflectName(mname), mtyp: resolveReflectType(ft.typeOff(m.mtyp)), ifn: resolveReflectText(ft.textOff(m.ifn)), tfn: resolveReflectText(ft.textOff(m.tfn)), diff --git a/src/reflect/value.go b/src/reflect/value.go index d4d317436a..e6b846e5d1 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -553,7 +553,7 @@ func methodReceiver(op string, v Value, methodIndex int) (rcvrtype, t *rtype, fn panic("reflect: internal error: invalid method index") } m := &tt.methods[i] - if !m.name.isExported() { + if !tt.nameOff(m.name).isExported() { panic("reflect: " + op + " of unexported method") } iface := (*nonEmptyInterface)(v.ptr) @@ -562,7 +562,7 @@ func methodReceiver(op string, v Value, methodIndex int) (rcvrtype, t *rtype, fn } rcvrtype = iface.itab.typ fn = unsafe.Pointer(&iface.itab.fun[i]) - t = m.typ + t = tt.typeOff(m.typ) } else { rcvrtype = v.typ ut := v.typ.uncommon() @@ -570,7 +570,7 @@ func methodReceiver(op string, v Value, methodIndex int) (rcvrtype, t *rtype, fn panic("reflect: internal error: invalid method index") } m := ut.methods()[i] - if !m.name.isExported() { + if !v.typ.nameOff(m.name).isExported() { panic("reflect: " + op + " of unexported method") } ifn := v.typ.textOff(m.ifn) @@ -1684,7 +1684,7 @@ func (v Value) Type() Type { panic("reflect: internal error: invalid method index") } m := &tt.methods[i] - return m.typ + return v.typ.typeOff(m.typ) } // Method on concrete type. ut := v.typ.uncommon() diff --git a/src/runtime/iface.go b/src/runtime/iface.go index 84f0ee8f0c..8f179bac80 100644 --- a/src/runtime/iface.go +++ b/src/runtime/iface.go @@ -37,7 +37,8 @@ func getitab(inter *interfacetype, typ *_type, canfail bool) *itab { if canfail { return nil } - panic(&TypeAssertionError{"", typ._string, inter.typ._string, inter.mhdr[0].name.name()}) + name := inter.typ.nameOff(inter.mhdr[0].name) + panic(&TypeAssertionError{"", typ._string, inter.typ._string, name.name()}) } h := itabhash(inter, typ) @@ -98,20 +99,22 @@ func additab(m *itab, locked, canfail bool) { j := 0 for k := 0; k < ni; k++ { i := &inter.mhdr[k] - iname := i.name.name() - itype := i._type - ipkg := i.name.pkgPath() + itype := inter.typ.typeOff(i.ityp) + name := inter.typ.nameOff(i.name) + iname := name.name() + ipkg := name.pkgPath() if ipkg == "" { ipkg = inter.pkgpath.name() } for ; j < nt; j++ { t := &xmhdr[j] - if typ.typeOff(t.mtyp) == itype && t.name.name() == iname { - pkgPath := t.name.pkgPath() + tname := typ.nameOff(t.name) + if typ.typeOff(t.mtyp) == itype && tname.name() == iname { + pkgPath := tname.pkgPath() if pkgPath == "" { pkgPath = x.pkgpath.name() } - if t.name.isExported() || pkgPath == ipkg { + if tname.isExported() || pkgPath == ipkg { if m != nil { ifn := typ.textOff(t.ifn) *(*unsafe.Pointer)(add(unsafe.Pointer(&m.fun[0]), uintptr(k)*sys.PtrSize)) = ifn diff --git a/src/runtime/runtime1.go b/src/runtime/runtime1.go index 02aeedaf75..9089383904 100644 --- a/src/runtime/runtime1.go +++ b/src/runtime/runtime1.go @@ -487,6 +487,12 @@ func reflect_typelinks() ([]unsafe.Pointer, [][]int32) { return sections, ret } +// reflect_resolveNameOff resolves a name offset from a base pointer. +//go:linkname reflect_resolveNameOff reflect.resolveNameOff +func reflect_resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer { + return unsafe.Pointer(resolveNameOff(ptrInModule, nameOff(off)).bytes) +} + // reflect_resolveTypeOff resolves an *rtype offset from a base type. //go:linkname reflect_resolveTypeOff reflect.resolveTypeOff func reflect_resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer { diff --git a/src/runtime/type.go b/src/runtime/type.go index 711753bab5..31f7ff81b8 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -161,11 +161,17 @@ func resolveNameOff(ptrInModule unsafe.Pointer, off nameOff) name { } } if md == nil { - println("runtime: nameOff", hex(off), "base", hex(base), "not in ranges:") - for next := &firstmoduledata; next != nil; next = next.next { - println("\ttypes", hex(next.types), "etypes", hex(next.etypes)) + lock(&reflectOffs.lock) + res, found := reflectOffs.m[int32(off)] + unlock(&reflectOffs.lock) + if !found { + println("runtime: nameOff", hex(off), "base", hex(base), "not in ranges:") + for next := &firstmoduledata; next != nil; next = next.next { + println("\ttypes", hex(next.types), "etypes", hex(next.etypes)) + } + throw("runtime: name offset base pointer out of range") } - throw("runtime: name offset base pointer out of range") + return name{(*byte)(res)} } res := md.types + uintptr(off) if res > md.etypes { @@ -175,6 +181,10 @@ func resolveNameOff(ptrInModule unsafe.Pointer, off nameOff) name { return name{(*byte)(unsafe.Pointer(res))} } +func (t *_type) nameOff(off nameOff) name { + return resolveNameOff(unsafe.Pointer(t), off) +} + func (t *_type) typeOff(off typeOff) *_type { if off == 0 { return nil @@ -269,7 +279,7 @@ type typeOff int32 type textOff int32 type method struct { - name name + name nameOff mtyp typeOff ifn textOff tfn textOff @@ -282,8 +292,8 @@ type uncommontype struct { } type imethod struct { - name name - _type *_type + name nameOff + ityp typeOff } type interfacetype struct { @@ -354,19 +364,19 @@ type name struct { bytes *byte } -func (n *name) data(off int) *byte { +func (n name) data(off int) *byte { return (*byte)(add(unsafe.Pointer(n.bytes), uintptr(off))) } -func (n *name) isExported() bool { +func (n name) isExported() bool { return (*n.bytes)&(1<<0) != 0 } -func (n *name) nameLen() int { +func (n name) nameLen() int { return int(uint16(*n.data(1))<<8 | uint16(*n.data(2))) } -func (n *name) tagLen() int { +func (n name) tagLen() int { if *n.data(0)&(1<<1) == 0 { return 0 } @@ -374,7 +384,7 @@ func (n *name) tagLen() int { return int(uint16(*n.data(off))<<8 | uint16(*n.data(off + 1))) } -func (n *name) name() (s string) { +func (n name) name() (s string) { if n.bytes == nil { return "" } @@ -388,7 +398,7 @@ func (n *name) name() (s string) { return s } -func (n *name) tag() (s string) { +func (n name) tag() (s string) { tl := n.tagLen() if tl == 0 { return "" @@ -400,7 +410,7 @@ func (n *name) tag() (s string) { return s } -func (n *name) pkgPath() string { +func (n name) pkgPath() string { if n.bytes == nil || *n.data(0)&(1<<2) == 0 { return "" } @@ -545,13 +555,15 @@ func typesEqual(t, v *_type) bool { for i := range it.mhdr { tm := &it.mhdr[i] vm := &iv.mhdr[i] - if tm.name.name() != vm.name.name() { + tname := it.typ.nameOff(tm.name) + vname := iv.typ.nameOff(vm.name) + if tname.name() != vname.name() { return false } - if tm.name.pkgPath() != vm.name.pkgPath() { + if tname.pkgPath() != vname.pkgPath() { return false } - if !typesEqual(tm._type, vm._type) { + if !typesEqual(it.typ.typeOff(tm.ityp), iv.typ.typeOff(vm.ityp)) { return false } }