From cfff9ecbd76eba493691692fe12e6ce2db554f66 Mon Sep 17 00:00:00 2001 From: Dmitry Panov Date: Mon, 26 Jul 2021 13:22:28 +0100 Subject: [PATCH] Ensure ToPropertyKey happens earlier when assigning computed keys. Fixes #312. Signed-off-by: Gabri --- compiler_expr.go | 4 +++- compiler_test.go | 42 +++++++++++++++++++++++++++++++++++++++--- tc39_test.go | 1 + vm.go | 34 ++++++++++++++++++++++++++++++---- 4 files changed, 73 insertions(+), 8 deletions(-) diff --git a/compiler_expr.go b/compiler_expr.go index b997b9fe..248622c8 100644 --- a/compiler_expr.go +++ b/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) diff --git a/compiler_test.go b/compiler_test.go index c6ab798a..1f8bdda1 100644 --- a/compiler_test.go +++ b/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; diff --git a/tc39_test.go b/tc39_test.go index 334a02cf..61c772a3 100644 --- a/tc39_test.go +++ b/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", } ) diff --git a/vm.go b/vm.go index 3fa47571..172adc53 100644 --- a/vm.go +++ b/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