Browse Source

Ensure ToPropertyKey happens earlier when assigning computed keys. Fixes #312.

Dmitry Panov 4 years ago
parent
commit
b7029a5638
4 changed files with 73 additions and 8 deletions
  1. 3 1
      compiler_expr.go
  2. 39 3
      compiler_test.go
  3. 1 0
      tc39_test.go
  4. 30 4
      vm.go

+ 3 - 1
compiler_expr.go

@@ -1682,6 +1682,7 @@ func (e *compiledObjectLiteral) emitGetter(putOnStack bool) {
 				}
 			}
 			if computed {
+				e.c.emit(_toPropertyKey{})
 				valueExpr.emitGetter(true)
 				switch prop.Kind {
 				case ast.PropertyKindValue, ast.PropertyKindMethod:
@@ -2071,6 +2072,7 @@ func (c *compiler) emitObjectPattern(pattern *ast.ObjectPattern, emitAssign func
 		case *ast.PropertyKeyed:
 			c.emit(dup)
 			c.compileExpression(prop.Key).emitGetter(true)
+			c.emit(_toPropertyKey{})
 			var target ast.Expression
 			var initializer ast.Expression
 			if e, ok := prop.Value.(*ast.AssignExpression); ok {
@@ -2080,7 +2082,7 @@ func (c *compiler) emitObjectPattern(pattern *ast.ObjectPattern, emitAssign func
 				target = prop.Value
 			}
 			c.emitAssign(target, c.compilePatternInitExpr(func() {
-				c.emit(getElem)
+				c.emit(getKey)
 			}, initializer, prop.Idx0()), emitAssign)
 		default:
 			c.throwSyntaxError(int(prop.Idx0()-1), "Unsupported AssignmentProperty type: %T", prop)

+ 39 - 3
compiler_test.go

@@ -3588,12 +3588,22 @@ func TestObjectAssignmentPatternEvalOrder(t *testing.T) {
 	
 	function prop1() {
 		trace += "prop1(),"
-		return "a";
+		return {
+			toString: function() {
+				trace += "prop1-to-string(),";
+				return "a";
+			}
+		}
 	}
 	
 	function prop2() {
 		trace += "prop2(),";
-		return "b";
+		return {
+			toString: function() {
+				trace += "prop2-to-string(),";
+				return "b";
+			}
+		}
 	}
 	
 	function target() {
@@ -3609,7 +3619,7 @@ func TestObjectAssignmentPatternEvalOrder(t *testing.T) {
 	}
 	trace;
 	`
-	testScript1(SCRIPT, asciiString("src(),prop1(),target(),get a,prop2(),"), t)
+	testScript1(SCRIPT, asciiString("src(),prop1(),prop1-to-string(),target(),get a,prop2(),prop2-to-string(),"), t)
 }
 
 func TestArrayAssignmentPatternEvalOrder(t *testing.T) {
@@ -3733,6 +3743,32 @@ func TestObjLiteralComputedKeys(t *testing.T) {
 	testScript1(SCRIPT, _undefined, t)
 }
 
+func TestObjLiteralComputedKeysEvalOrder(t *testing.T) {
+	const SCRIPT = `
+	let trace = [];
+	function key() {
+		trace.push("key");
+		return {
+			toString: function() {
+				trace.push("key-toString");
+				return "key";
+			}
+		}
+	}
+	function val() {
+		trace.push("val");
+		return "val";
+	}
+	
+	const _ = {
+		[key()]: val(),
+	}
+	
+	trace.join(",");
+	`
+	testScript1(SCRIPT, asciiString("key,key-toString,val"), t)
+}
+
 func TestArrayAssignPattern(t *testing.T) {
 	const SCRIPT = `
 	let a, b;

+ 1 - 0
tc39_test.go

@@ -424,6 +424,7 @@ var (
 		"sec-evaldeclarationinstantiation",
 		"sec-integer-indexed-exotic-objects-defineownproperty-p-desc",
 		"sec-destructuring-binding-patterns",
+		"sec-runtime-semantics-keyeddestructuringassignmentevaluation",
 	}
 )
 

+ 30 - 4
vm.go

@@ -1234,6 +1234,14 @@ func (j jump) exec(vm *vm) {
 	vm.pc += int(j)
 }
 
+type _toPropertyKey struct{}
+
+func (_toPropertyKey) exec(vm *vm) {
+	p := vm.sp - 1
+	vm.stack[p] = toPropertyKey(vm.stack[p])
+	vm.pc++
+}
+
 type _getElemRef struct{}
 
 var getElemRef _getElemRef
@@ -1287,7 +1295,7 @@ var setElem1 _setElem1
 
 func (_setElem1) exec(vm *vm) {
 	obj := vm.stack[vm.sp-3].ToObject(vm.r)
-	propName := toPropertyKey(vm.stack[vm.sp-2])
+	propName := vm.stack[vm.sp-2]
 	val := vm.stack[vm.sp-1]
 
 	obj.setOwn(propName, val, true)
@@ -1302,7 +1310,7 @@ var setElem1Named _setElem1Named
 
 func (_setElem1Named) exec(vm *vm) {
 	obj := vm.stack[vm.sp-3].ToObject(vm.r)
-	propName := toPropertyKey(vm.stack[vm.sp-2])
+	propName := vm.stack[vm.sp-2]
 	val := vm.stack[vm.sp-1]
 	vm.r.toObject(val).self.defineOwnPropertyStr("name", PropertyDescriptor{
 		Value:        propName,
@@ -1547,7 +1555,7 @@ var setPropGetter1 _setPropGetter1
 
 func (s _setPropGetter1) exec(vm *vm) {
 	obj := vm.r.toObject(vm.stack[vm.sp-3])
-	propName := toPropertyKey(vm.stack[vm.sp-2])
+	propName := vm.stack[vm.sp-2]
 	val := vm.stack[vm.sp-1]
 	vm.r.toObject(val).self.defineOwnPropertyStr("name", PropertyDescriptor{
 		Value:        asciiString("get ").concat(stringValueFromRaw(val.string())),
@@ -1572,7 +1580,7 @@ var setPropSetter1 _setPropSetter1
 
 func (s _setPropSetter1) exec(vm *vm) {
 	obj := vm.r.toObject(vm.stack[vm.sp-3])
-	propName := toPropertyKey(vm.stack[vm.sp-2])
+	propName := vm.stack[vm.sp-2]
 	val := vm.stack[vm.sp-1]
 
 	vm.r.toObject(val).self.defineOwnPropertyStr("name", PropertyDescriptor{
@@ -1641,6 +1649,24 @@ func (_getElem) exec(vm *vm) {
 	vm.pc++
 }
 
+type _getKey struct{}
+
+var getKey _getKey
+
+func (_getKey) exec(vm *vm) {
+	v := vm.stack[vm.sp-2]
+	obj := v.baseObject(vm.r)
+	propName := vm.stack[vm.sp-1]
+	if obj == nil {
+		panic(vm.r.NewTypeError("Cannot read property '%s' of undefined", propName.String()))
+	}
+
+	vm.stack[vm.sp-2] = nilSafe(obj.get(propName, v))
+
+	vm.sp--
+	vm.pc++
+}
+
 type _getElemCallee struct{}
 
 var getElemCallee _getElemCallee