Skip to content

Commit

Permalink
Fixed typed arrays' defineProperty and indexing. Fixes dop251#308.
Browse files Browse the repository at this point in the history
Signed-off-by: Gabri <[email protected]>
  • Loading branch information
dop251 authored and Gabri3l committed Jan 24, 2022
1 parent cf26c0a commit fc7d812
Show file tree
Hide file tree
Showing 14 changed files with 530 additions and 189 deletions.
120 changes: 5 additions & 115 deletions array.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ func (a *arrayObject) getIdx(idx valueInt, receiver Value) Value {

func (a *arrayObject) getOwnPropStr(name unistring.String) Value {
if len(a.values) > 0 {
if i := strToIdx(name); i != math.MaxUint32 {
if i := strToArrayIdx(name); i != math.MaxUint32 {
if i < uint32(len(a.values)) {
return a.values[i]
}
Expand Down Expand Up @@ -264,7 +264,7 @@ func (a *arrayObject) _setOwnIdx(idx uint32, val Value, throw bool) bool {
}

func (a *arrayObject) setOwnStr(name unistring.String, val Value, throw bool) bool {
if idx := strToIdx(name); idx != math.MaxUint32 {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
return a._setOwnIdx(idx, val, throw)
} else {
if name == "length" {
Expand Down Expand Up @@ -325,7 +325,7 @@ func (a *arrayObject) ownKeys(all bool, accum []Value) []Value {
}

func (a *arrayObject) hasOwnPropertyStr(name unistring.String) bool {
if idx := strToIdx(name); idx != math.MaxUint32 {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
return idx < uint32(len(a.values)) && a.values[idx] != nil
} else {
return a.baseObject.hasOwnPropertyStr(name)
Expand Down Expand Up @@ -433,7 +433,7 @@ func (a *arrayObject) _defineIdxProperty(idx uint32, desc PropertyDescriptor, th
}

func (a *arrayObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
if idx := strToIdx(name); idx != math.MaxUint32 {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
return a._defineIdxProperty(idx, descr, throw)
}
if name == "length" {
Expand Down Expand Up @@ -467,7 +467,7 @@ func (a *arrayObject) _deleteIdxProp(idx uint32, throw bool) bool {
}

func (a *arrayObject) deleteStr(name unistring.String, throw bool) bool {
if idx := strToIdx(name); idx != math.MaxUint32 {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
return a._deleteIdxProp(idx, throw)
}
return a.baseObject.deleteStr(name, throw)
Expand Down Expand Up @@ -521,113 +521,3 @@ func toIdx(v valueInt) uint32 {
}
return math.MaxUint32
}

func strToIdx64(s unistring.String) int64 {
if s == "" {
return -1
}
l := len(s)
if s[0] == '0' {
if l == 1 {
return 0
}
return -1
}
var n int64
if l < 19 {
// guaranteed not to overflow
for i := 0; i < len(s); i++ {
c := s[i]
if c < '0' || c > '9' {
return -1
}
n = n*10 + int64(c-'0')
}
return n
}
if l > 19 {
// guaranteed to overflow
return -1
}
c18 := s[18]
if c18 < '0' || c18 > '9' {
return -1
}
for i := 0; i < 18; i++ {
c := s[i]
if c < '0' || c > '9' {
return -1
}
n = n*10 + int64(c-'0')
}
if n >= math.MaxInt64/10+1 {
return -1
}
n *= 10
n1 := n + int64(c18-'0')
if n1 < n {
return -1
}
return n1
}

func strToIdx(s unistring.String) uint32 {
if s == "" {
return math.MaxUint32
}
l := len(s)
if s[0] == '0' {
if l == 1 {
return 0
}
return math.MaxUint32
}
var n uint32
if l < 10 {
// guaranteed not to overflow
for i := 0; i < len(s); i++ {
c := s[i]
if c < '0' || c > '9' {
return math.MaxUint32
}
n = n*10 + uint32(c-'0')
}
return n
}
if l > 10 {
// guaranteed to overflow
return math.MaxUint32
}
c9 := s[9]
if c9 < '0' || c9 > '9' {
return math.MaxUint32
}
for i := 0; i < 9; i++ {
c := s[i]
if c < '0' || c > '9' {
return math.MaxUint32
}
n = n*10 + uint32(c-'0')
}
if n >= math.MaxUint32/10+1 {
return math.MaxUint32
}
n *= 10
n1 := n + uint32(c9-'0')
if n1 < n {
return math.MaxUint32
}

return n1
}

func strToGoIdx(s unistring.String) int {
if bits.UintSize == 64 {
return int(strToIdx64(s))
}
i := strToIdx(s)
if i >= math.MaxInt32 {
return -1
}
return int(i)
}
10 changes: 5 additions & 5 deletions array_sparse.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func (a *sparseArrayObject) getLengthProp() Value {
}

func (a *sparseArrayObject) getOwnPropStr(name unistring.String) Value {
if idx := strToIdx(name); idx != math.MaxUint32 {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
return a._getIdx(idx)
}
if name == "length" {
Expand Down Expand Up @@ -214,7 +214,7 @@ func (a *sparseArrayObject) _setOwnIdx(idx uint32, val Value, throw bool) bool {
}

func (a *sparseArrayObject) setOwnStr(name unistring.String, val Value, throw bool) bool {
if idx := strToIdx(name); idx != math.MaxUint32 {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
return a._setOwnIdx(idx, val, throw)
} else {
if name == "length" {
Expand Down Expand Up @@ -295,7 +295,7 @@ func (a *sparseArrayObject) setValues(values []Value, objCount int) {
}

func (a *sparseArrayObject) hasOwnPropertyStr(name unistring.String) bool {
if idx := strToIdx(name); idx != math.MaxUint32 {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
i := a.findIdx(idx)
return i < len(a.items) && a.items[i].idx == idx
} else {
Expand Down Expand Up @@ -372,7 +372,7 @@ func (a *sparseArrayObject) _defineIdxProperty(idx uint32, desc PropertyDescript
}

func (a *sparseArrayObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
if idx := strToIdx(name); idx != math.MaxUint32 {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
return a._defineIdxProperty(idx, descr, throw)
}
if name == "length" {
Expand Down Expand Up @@ -406,7 +406,7 @@ func (a *sparseArrayObject) _deleteIdxProp(idx uint32, throw bool) bool {
}

func (a *sparseArrayObject) deleteStr(name unistring.String, throw bool) bool {
if idx := strToIdx(name); idx != math.MaxUint32 {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
return a._deleteIdxProp(idx, throw)
}
return a.baseObject.deleteStr(name, throw)
Expand Down
13 changes: 13 additions & 0 deletions array_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ func TestArrayExportProps(t *testing.T) {
}
}

func TestArrayCanonicalIndex(t *testing.T) {
const SCRIPT = `
var a = [];
a["00"] = 1;
a["01"] = 2;
if (a[0] !== undefined) {
throw new Error("a[0]");
}
`

testScript1(SCRIPT, _undefined, t)
}

func BenchmarkArrayGetStr(b *testing.B) {
b.StopTimer()
r := New()
Expand Down
20 changes: 12 additions & 8 deletions builtin_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (h *nativeProxyHandler) preventExtensions(target *Object) (bool, bool) {

func (h *nativeProxyHandler) getOwnPropertyDescriptorStr(target *Object, prop unistring.String) (Value, bool) {
if trap := h.handler.GetOwnPropertyDescriptorIdx; trap != nil {
if idx, ok := strPropToInt(prop); ok {
if idx, ok := strToInt(prop); ok {
desc := trap(target, idx)
return desc.toValue(target.runtime), true
}
Expand Down Expand Up @@ -72,7 +72,7 @@ func (h *nativeProxyHandler) getOwnPropertyDescriptorSym(target *Object, prop *S

func (h *nativeProxyHandler) definePropertyStr(target *Object, prop unistring.String, desc PropertyDescriptor) (bool, bool) {
if trap := h.handler.DefinePropertyIdx; trap != nil {
if idx, ok := strPropToInt(prop); ok {
if idx, ok := strToInt(prop); ok {
return trap(target, idx, desc), true
}
}
Expand Down Expand Up @@ -101,7 +101,7 @@ func (h *nativeProxyHandler) definePropertySym(target *Object, prop *Symbol, des

func (h *nativeProxyHandler) hasStr(target *Object, prop unistring.String) (bool, bool) {
if trap := h.handler.HasIdx; trap != nil {
if idx, ok := strPropToInt(prop); ok {
if idx, ok := strToInt(prop); ok {
return trap(target, idx), true
}
}
Expand Down Expand Up @@ -130,7 +130,7 @@ func (h *nativeProxyHandler) hasSym(target *Object, prop *Symbol) (bool, bool) {

func (h *nativeProxyHandler) getStr(target *Object, prop unistring.String, receiver Value) (Value, bool) {
if trap := h.handler.GetIdx; trap != nil {
if idx, ok := strPropToInt(prop); ok {
if idx, ok := strToInt(prop); ok {
return trap(target, idx, receiver), true
}
}
Expand Down Expand Up @@ -159,7 +159,7 @@ func (h *nativeProxyHandler) getSym(target *Object, prop *Symbol, receiver Value

func (h *nativeProxyHandler) setStr(target *Object, prop unistring.String, value Value, receiver Value) (bool, bool) {
if trap := h.handler.SetIdx; trap != nil {
if idx, ok := strPropToInt(prop); ok {
if idx, ok := strToInt(prop); ok {
return trap(target, idx, value, receiver), true
}
}
Expand Down Expand Up @@ -188,7 +188,7 @@ func (h *nativeProxyHandler) setSym(target *Object, prop *Symbol, value Value, r

func (h *nativeProxyHandler) deleteStr(target *Object, prop unistring.String) (bool, bool) {
if trap := h.handler.DeletePropertyIdx; trap != nil {
if idx, ok := strPropToInt(prop); ok {
if idx, ok := strToInt(prop); ok {
return trap(target, idx), true
}
}
Expand Down Expand Up @@ -246,8 +246,12 @@ func (r *Runtime) newNativeProxyHandler(nativeHandler *ProxyTrapConfig) proxyHan

// ProxyTrapConfig provides a simplified Go-friendly API for implementing Proxy traps.
// If an *Idx trap is defined it gets called for integer property keys, including negative ones. Note that
// this also includes string property keys that can be parsed into an integer. This allows more efficient
// array operations.
// this only includes string property keys that represent a canonical integer
// (i.e. "0", "123", but not "00", "01", " 1" or "-0").
// For efficiency strings representing integers exceeding 2^53 are not checked to see if they are canonical,
// i.e. the *Idx traps will receive "9007199254740993" as well as "9007199254740994", even though the former is not
// a canonical representation in ECMAScript (Number("9007199254740993") === 9007199254740992).
// See https://262.ecma-international.org/#sec-canonicalnumericindexstring
// If an *Idx trap is not set, the corresponding string one is used.
type ProxyTrapConfig struct {
// A trap for Object.getPrototypeOf, Reflect.getPrototypeOf, __proto__, Object.prototype.isPrototypeOf, instanceof
Expand Down
20 changes: 19 additions & 1 deletion builtin_proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ func TestProxy_native_proxy_getOwnPropertyDescriptorIdx(t *testing.T) {
a := vm.NewArray()
proxy1 := vm.NewProxy(a, &ProxyTrapConfig{
GetOwnPropertyDescriptor: func(target *Object, prop string) PropertyDescriptor {
panic(vm.NewTypeError("GetOwnPropertyDescriptor was called"))
panic(vm.NewTypeError("GetOwnPropertyDescriptor was called for %q", prop))
},
GetOwnPropertyDescriptorIdx: func(target *Object, prop int) PropertyDescriptor {
if prop >= -1 && prop <= 1 {
Expand All @@ -352,8 +352,21 @@ func TestProxy_native_proxy_getOwnPropertyDescriptorIdx(t *testing.T) {
},
})

proxy3 := vm.NewProxy(a, &ProxyTrapConfig{
GetOwnPropertyDescriptor: func(target *Object, prop string) PropertyDescriptor {
return PropertyDescriptor{
Value: vm.ToValue(prop),
Configurable: FLAG_TRUE,
}
},
GetOwnPropertyDescriptorIdx: func(target *Object, prop int) PropertyDescriptor {
panic(vm.NewTypeError("GetOwnPropertyDescriptorIdx was called for %d", prop))
},
})

vm.Set("proxy1", proxy1)
vm.Set("proxy2", proxy2)
vm.Set("proxy3", proxy3)
_, err := vm.RunString(TESTLIBX + `
var desc;
for (var i = -1; i <= 1; i++) {
Expand All @@ -369,6 +382,11 @@ func TestProxy_native_proxy_getOwnPropertyDescriptorIdx(t *testing.T) {
desc = Object.getOwnPropertyDescriptor(proxy2, ""+i);
assert(deepEqual(desc, {value: ""+i, writable: false, enumerable: false, configurable: true}), "2. str "+i);
}
for (const prop of ["00", " 0", "-0", "01"]) {
desc = Object.getOwnPropertyDescriptor(proxy3, prop);
assert(deepEqual(desc, {value: prop, writable: false, enumerable: false, configurable: true}), "3. "+prop);
}
`)
if err != nil {
t.Fatal(err)
Expand Down
16 changes: 14 additions & 2 deletions object.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,18 @@ func (p *PropertyDescriptor) Empty() bool {
return *p == empty
}

func (p *PropertyDescriptor) IsAccessor() bool {
return p.Setter != nil || p.Getter != nil
}

func (p *PropertyDescriptor) IsData() bool {
return p.Value != nil || p.Writable != FLAG_NOT_SET
}

func (p *PropertyDescriptor) IsGeneric() bool {
return !p.IsAccessor() && !p.IsData()
}

func (p *PropertyDescriptor) toValue(r *Runtime) Value {
if p.jsDescriptor != nil {
return p.jsDescriptor
Expand Down Expand Up @@ -1121,9 +1133,9 @@ func (o *baseObject) fixPropOrder() {
names := o.propNames
for i := o.lastSortedPropLen; i < len(names); i++ {
name := names[i]
if idx := strToIdx(name); idx != math.MaxUint32 {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
k := sort.Search(o.idxPropCount, func(j int) bool {
return strToIdx(names[j]) >= idx
return strToArrayIdx(names[j]) >= idx
})
if k < i {
if namesMarkedForCopy(names) {
Expand Down
Loading

0 comments on commit fc7d812

Please sign in to comment.