Bläddra i källkod

Fixed the handling of Symbol properties in destructuring assignments. Fixes #312.

Dmitry Panov 4 år sedan
förälder
incheckning
a88d78e02a
6 ändrade filer med 80 tillägg och 23 borttagningar
  1. 20 0
      builtin_proxy_test.go
  2. 26 10
      destruct.go
  3. 18 1
      proxy.go
  4. 12 0
      runtime_test.go
  5. 1 0
      tc39_test.go
  6. 3 12
      vm.go

+ 20 - 0
builtin_proxy_test.go

@@ -1260,3 +1260,23 @@ func TestProxy_proxy_createTargetNotCallable(t *testing.T) {
 
 	testScript1(TESTLIB+SCRIPT, _undefined, t)
 }
+
+func TestProxyEnumerableSymbols(t *testing.T) {
+	const SCRIPT = `
+	var getOwnKeys = [];
+	var ownKeysResult = [Symbol(), "foo", "0"];
+	var proxy = new Proxy({}, {
+	  getOwnPropertyDescriptor: function(_target, key) {
+		getOwnKeys.push(key);
+	  },
+	  ownKeys: function() {
+		return ownKeysResult;
+	  },
+	});
+
+	let {...$} = proxy;
+	compareArray(getOwnKeys, ownKeysResult);
+	`
+
+	testScript1(TESTLIB+SCRIPT, valueTrue, t)
+}

+ 26 - 10
destruct.go

@@ -8,7 +8,7 @@ import (
 type destructKeyedSource struct {
 	r        *Runtime
 	wrapped  Value
-	usedKeys map[unistring.String]struct{}
+	usedKeys map[Value]struct{}
 }
 
 func newDestructKeyedSource(r *Runtime, wrapped Value) *destructKeyedSource {
@@ -29,9 +29,9 @@ func (d *destructKeyedSource) w() objectImpl {
 	return d.wrapped.ToObject(d.r).self
 }
 
-func (d *destructKeyedSource) recordKey(key unistring.String) {
+func (d *destructKeyedSource) recordKey(key Value) {
 	if d.usedKeys == nil {
-		d.usedKeys = make(map[unistring.String]struct{})
+		d.usedKeys = make(map[Value]struct{})
 	}
 	d.usedKeys[key] = struct{}{}
 }
@@ -53,30 +53,32 @@ func (d *destructKeyedSource) className() string {
 }
 
 func (d *destructKeyedSource) getStr(p unistring.String, receiver Value) Value {
-	d.recordKey(p)
+	d.recordKey(stringValueFromRaw(p))
 	return d.w().getStr(p, receiver)
 }
 
 func (d *destructKeyedSource) getIdx(p valueInt, receiver Value) Value {
-	d.recordKey(p.string())
+	d.recordKey(p.toString())
 	return d.w().getIdx(p, receiver)
 }
 
 func (d *destructKeyedSource) getSym(p *Symbol, receiver Value) Value {
+	d.recordKey(p)
 	return d.w().getSym(p, receiver)
 }
 
 func (d *destructKeyedSource) getOwnPropStr(u unistring.String) Value {
-	d.recordKey(u)
+	d.recordKey(stringValueFromRaw(u))
 	return d.w().getOwnPropStr(u)
 }
 
 func (d *destructKeyedSource) getOwnPropIdx(v valueInt) Value {
-	d.recordKey(v.string())
+	d.recordKey(v.toString())
 	return d.w().getOwnPropIdx(v)
 }
 
 func (d *destructKeyedSource) getOwnPropSym(symbol *Symbol) Value {
+	d.recordKey(symbol)
 	return d.w().getOwnPropSym(symbol)
 }
 
@@ -204,7 +206,7 @@ func (i *destructKeyedSourceIter) next() (propIterItem, iterNextFunc) {
 			return item, nil
 		}
 		i.wrapped = next
-		if _, exists := i.d.usedKeys[item.name]; !exists {
+		if _, exists := i.d.usedKeys[stringValueFromRaw(item.name)]; !exists {
 			return item, i.next
 		}
 	}
@@ -244,12 +246,26 @@ func (d *destructKeyedSource) ownKeys(all bool, accum []Value) []Value {
 	return accum
 }
 
+func (d *destructKeyedSource) filterUsedKeys(keys []Value) []Value {
+	k := 0
+	for i, key := range keys {
+		if _, exists := d.usedKeys[key]; exists {
+			continue
+		}
+		if k != i {
+			keys[k] = key
+		}
+		k++
+	}
+	return keys[:k]
+}
+
 func (d *destructKeyedSource) ownSymbols(all bool, accum []Value) []Value {
-	return d.w().ownSymbols(all, accum)
+	return d.filterUsedKeys(d.w().ownSymbols(all, accum))
 }
 
 func (d *destructKeyedSource) ownPropertyKeys(all bool, accum []Value) []Value {
-	return d.ownSymbols(all, d.ownKeys(all, accum))
+	return d.filterUsedKeys(d.w().ownPropertyKeys(all, accum))
 }
 
 func (d *destructKeyedSource) _putProp(name unistring.String, value Value, writable, enumerable, configurable bool) Value {

+ 18 - 1
proxy.go

@@ -774,6 +774,23 @@ func (p *proxyObject) deleteSym(s *Symbol, throw bool) bool {
 
 func (p *proxyObject) ownPropertyKeys(all bool, _ []Value) []Value {
 	if v, ok := p.proxyOwnKeys(); ok {
+		if !all {
+			k := 0
+			for i, key := range v {
+				prop := p.val.getOwnProp(key)
+				if prop == nil {
+					continue
+				}
+				if prop, ok := prop.(*valueProperty); ok && !prop.enumerable {
+					continue
+				}
+				if k != i {
+					v[k] = v[i]
+				}
+				k++
+			}
+			v = v[:k]
+		}
 		return v
 	}
 	return p.target.self.ownPropertyKeys(all, nil)
@@ -992,7 +1009,7 @@ func (p *proxyObject) ownKeys(all bool, _ []Value) []Value { // we can assume ac
 
 func (p *proxyObject) ownSymbols(all bool, accum []Value) []Value {
 	if vals, ok := p.proxyOwnKeys(); ok {
-		res := p.filterKeys(vals, true, true)
+		res := p.filterKeys(vals, all, true)
 		if accum == nil {
 			return res
 		}

+ 12 - 0
runtime_test.go

@@ -2182,6 +2182,18 @@ func TestStrToInt32(t *testing.T) {
 	}
 }
 
+func TestDestructSymbol(t *testing.T) {
+	const SCRIPT = `
+	var S = Symbol("S");
+	var s, rest;
+
+	({[S]: s, ...rest} = {[S]: true, test: 1});
+	assert.sameValue(s, true, "S");
+	assert(deepEqual(rest, {test: 1}), "rest");
+	`
+	testScript1(TESTLIBX+SCRIPT, _undefined, t)
+}
+
 /*
 func TestArrayConcatSparse(t *testing.T) {
 function foo(a,b,c)

+ 1 - 0
tc39_test.go

@@ -423,6 +423,7 @@ var (
 		"sec-functiondeclarations-in-ifstatement-statement-clauses",
 		"sec-evaldeclarationinstantiation",
 		"sec-integer-indexed-exotic-objects-defineownproperty-p-desc",
+		"sec-destructuring-binding-patterns",
 	}
 )
 

+ 3 - 12
vm.go

@@ -3789,18 +3789,9 @@ func (r *Runtime) copyDataProperties(target, source Value) {
 		return
 	}
 	sourceObj := source.ToObject(r)
-	iter := &enumerableIter{
-		wrapped: sourceObj.self.enumerateOwnKeys(),
-	}
-
-	for item, next := iter.next(); next != nil; item, next = next() {
-		v := nilSafe(sourceObj.self.getStr(item.name, nil))
-		createDataPropertyOrThrow(targetObj, stringValueFromRaw(item.name), v)
-	}
-
-	for _, sym := range sourceObj.self.ownSymbols(false, nil) {
-		v := nilSafe(sourceObj.self.getSym(sym.(*Symbol), nil))
-		createDataPropertyOrThrow(targetObj, sym, v)
+	for _, key := range sourceObj.self.ownPropertyKeys(false, nil) {
+		v := nilSafe(sourceObj.get(key, nil))
+		createDataPropertyOrThrow(targetObj, key, v)
 	}
 }