Browse Source

Support for assignment patterns in for-in and for-of loops. Support for spread elements in call arguments. Fixed various issues related to destructuring assignments. See #305.

Dmitry Panov 4 years ago
parent
commit
7ad1be9aa9
10 changed files with 259 additions and 36 deletions
  1. 105 29
      compiler_expr.go
  2. 6 1
      compiler_stmt.go
  3. 21 0
      compiler_test.go
  4. 19 4
      parser/expression.go
  5. 1 0
      parser/parser_test.go
  6. 5 1
      parser/statement.go
  7. 10 1
      runtime.go
  8. 19 0
      tc39_test.go
  9. 2 0
      token/token_const.go
  10. 71 0
      vm.go

+ 105 - 29
compiler_expr.go

@@ -33,6 +33,12 @@ type compiledCallExpr struct {
 	baseCompiledExpr
 	baseCompiledExpr
 	args   []compiledExpr
 	args   []compiledExpr
 	callee compiledExpr
 	callee compiledExpr
+
+	isVariadic bool
+}
+
+type compiledNewExpr struct {
+	compiledCallExpr
 }
 }
 
 
 type compiledObjectLiteral struct {
 type compiledObjectLiteral struct {
@@ -124,12 +130,6 @@ type compiledThisExpr struct {
 	baseCompiledExpr
 	baseCompiledExpr
 }
 }
 
 
-type compiledNewExpr struct {
-	baseCompiledExpr
-	callee compiledExpr
-	args   []compiledExpr
-}
-
 type compiledNewTarget struct {
 type compiledNewTarget struct {
 	baseCompiledExpr
 	baseCompiledExpr
 }
 }
@@ -176,6 +176,11 @@ type defaultDeleteExpr struct {
 	expr compiledExpr
 	expr compiledExpr
 }
 }
 
 
+type compiledSpreadCallArgument struct {
+	baseCompiledExpr
+	expr compiledExpr
+}
+
 func (e *defaultDeleteExpr) emitGetter(putOnStack bool) {
 func (e *defaultDeleteExpr) emitGetter(putOnStack bool) {
 	e.expr.emitGetter(false)
 	e.expr.emitGetter(false)
 	if putOnStack {
 	if putOnStack {
@@ -935,18 +940,20 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 	funcs := e.c.extractFunctions(body)
 	funcs := e.c.extractFunctions(body)
 	s := e.c.scope
 	s := e.c.scope
 	var calleeBinding *binding
 	var calleeBinding *binding
-	if e.isExpr && e.expr.Name != nil {
-		if b, created := s.bindNameLexical(e.expr.Name.Name, false, 0); created {
-			b.isConst = true
-			calleeBinding = b
-		}
-	}
 	preambleLen := 4 // enter, boxThis, createArgs, set
 	preambleLen := 4 // enter, boxThis, createArgs, set
 	e.c.p.code = make([]instruction, preambleLen, 8)
 	e.c.p.code = make([]instruction, preambleLen, 8)
 
 
-	if calleeBinding != nil {
-		e.c.emit(loadCallee)
-		calleeBinding.emitInit()
+	if hasPatterns || hasInits {
+		if e.isExpr && e.expr.Name != nil {
+			if b, created := s.bindNameLexical(e.expr.Name.Name, false, 0); created {
+				b.isConst = true
+				calleeBinding = b
+			}
+		}
+		if calleeBinding != nil {
+			e.c.emit(loadCallee)
+			calleeBinding.emitInit()
+		}
 	}
 	}
 
 
 	emitArgsRestMark := -1
 	emitArgsRestMark := -1
@@ -1031,6 +1038,16 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 		e.c.compileDeclList(e.expr.DeclarationList, true)
 		e.c.compileDeclList(e.expr.DeclarationList, true)
 		e.c.createFunctionBindings(funcs)
 		e.c.createFunctionBindings(funcs)
 		e.c.compileLexicalDeclarations(body, true)
 		e.c.compileLexicalDeclarations(body, true)
+		if e.isExpr && e.expr.Name != nil {
+			if b, created := s.bindNameLexical(e.expr.Name.Name, false, 0); created {
+				b.isConst = true
+				calleeBinding = b
+			}
+		}
+		if calleeBinding != nil {
+			e.c.emit(loadCallee)
+			calleeBinding.emitInit()
+		}
 	}
 	}
 
 
 	e.c.compileFunctions(funcs)
 	e.c.compileFunctions(funcs)
@@ -1194,25 +1211,45 @@ func (e *compiledThisExpr) emitGetter(putOnStack bool) {
 }
 }
 
 
 func (e *compiledNewExpr) emitGetter(putOnStack bool) {
 func (e *compiledNewExpr) emitGetter(putOnStack bool) {
+	if e.isVariadic {
+		e.c.emit(startVariadic)
+	}
 	e.callee.emitGetter(true)
 	e.callee.emitGetter(true)
 	for _, expr := range e.args {
 	for _, expr := range e.args {
 		expr.emitGetter(true)
 		expr.emitGetter(true)
 	}
 	}
 	e.addSrcMap()
 	e.addSrcMap()
-	e.c.emit(_new(len(e.args)))
+	if e.isVariadic {
+		e.c.emit(newVariadic, endVariadic)
+	} else {
+		e.c.emit(_new(len(e.args)))
+	}
 	if !putOnStack {
 	if !putOnStack {
 		e.c.emit(pop)
 		e.c.emit(pop)
 	}
 	}
 }
 }
 
 
-func (c *compiler) compileNewExpression(v *ast.NewExpression) compiledExpr {
-	args := make([]compiledExpr, len(v.ArgumentList))
-	for i, expr := range v.ArgumentList {
-		args[i] = c.compileExpression(expr)
+func (c *compiler) compileCallArgs(list []ast.Expression) (args []compiledExpr, isVariadic bool) {
+	args = make([]compiledExpr, len(list))
+	for i, argExpr := range list {
+		if spread, ok := argExpr.(*ast.SpreadElement); ok {
+			args[i] = c.compileSpreadCallArgument(spread)
+			isVariadic = true
+		} else {
+			args[i] = c.compileExpression(argExpr)
+		}
 	}
 	}
+	return
+}
+
+func (c *compiler) compileNewExpression(v *ast.NewExpression) compiledExpr {
+	args, isVariadic := c.compileCallArgs(v.ArgumentList)
 	r := &compiledNewExpr{
 	r := &compiledNewExpr{
-		callee: c.compileExpression(v.Callee),
-		args:   args,
+		compiledCallExpr: compiledCallExpr{
+			callee:     c.compileExpression(v.Callee),
+			args:       args,
+			isVariadic: isVariadic,
+		},
 	}
 	}
 	r.init(c, v.Idx0())
 	r.init(c, v.Idx0())
 	return r
 	return r
@@ -1765,6 +1802,9 @@ func (c *compiler) compileRegexpLiteral(v *ast.RegExpLiteral) compiledExpr {
 
 
 func (e *compiledCallExpr) emitGetter(putOnStack bool) {
 func (e *compiledCallExpr) emitGetter(putOnStack bool) {
 	var calleeName unistring.String
 	var calleeName unistring.String
+	if e.isVariadic {
+		e.c.emit(startVariadic)
+	}
 	switch callee := e.callee.(type) {
 	switch callee := e.callee.(type) {
 	case *compiledDotExpr:
 	case *compiledDotExpr:
 		callee.left.emitGetter(true)
 		callee.left.emitGetter(true)
@@ -1805,14 +1845,28 @@ func (e *compiledCallExpr) emitGetter(putOnStack bool) {
 		}
 		}
 
 
 		if e.c.scope.strict {
 		if e.c.scope.strict {
-			e.c.emit(callEvalStrict(len(e.args)))
+			if e.isVariadic {
+				e.c.emit(callEvalVariadicStrict)
+			} else {
+				e.c.emit(callEvalStrict(len(e.args)))
+			}
 		} else {
 		} else {
-			e.c.emit(callEval(len(e.args)))
+			if e.isVariadic {
+				e.c.emit(callEvalVariadic)
+			} else {
+				e.c.emit(callEval(len(e.args)))
+			}
 		}
 		}
 	} else {
 	} else {
-		e.c.emit(call(len(e.args)))
+		if e.isVariadic {
+			e.c.emit(callVariadic)
+		} else {
+			e.c.emit(call(len(e.args)))
+		}
+	}
+	if e.isVariadic {
+		e.c.emit(endVariadic)
 	}
 	}
-
 	if !putOnStack {
 	if !putOnStack {
 		e.c.emit(pop)
 		e.c.emit(pop)
 	}
 	}
@@ -1826,16 +1880,31 @@ func (e *compiledCallExpr) deleteExpr() compiledExpr {
 	return r
 	return r
 }
 }
 
 
+func (c *compiler) compileSpreadCallArgument(spread *ast.SpreadElement) compiledExpr {
+	r := &compiledSpreadCallArgument{
+		expr: c.compileExpression(spread.Expression),
+	}
+	r.init(c, spread.Idx0())
+	return r
+}
+
 func (c *compiler) compileCallExpression(v *ast.CallExpression) compiledExpr {
 func (c *compiler) compileCallExpression(v *ast.CallExpression) compiledExpr {
 
 
 	args := make([]compiledExpr, len(v.ArgumentList))
 	args := make([]compiledExpr, len(v.ArgumentList))
+	isVariadic := false
 	for i, argExpr := range v.ArgumentList {
 	for i, argExpr := range v.ArgumentList {
-		args[i] = c.compileExpression(argExpr)
+		if spread, ok := argExpr.(*ast.SpreadElement); ok {
+			args[i] = c.compileSpreadCallArgument(spread)
+			isVariadic = true
+		} else {
+			args[i] = c.compileExpression(argExpr)
+		}
 	}
 	}
 
 
 	r := &compiledCallExpr{
 	r := &compiledCallExpr{
-		args:   args,
-		callee: c.compileExpression(v.Callee),
+		args:       args,
+		callee:     c.compileExpression(v.Callee),
+		isVariadic: isVariadic,
 	}
 	}
 	r.init(c, v.LeftParenthesis)
 	r.init(c, v.LeftParenthesis)
 	return r
 	return r
@@ -2178,3 +2247,10 @@ func (c *compiler) compileNamedEmitterExpr(namedEmitter func(unistring.String),
 	r.init(c, idx)
 	r.init(c, idx)
 	return r
 	return r
 }
 }
+
+func (e *compiledSpreadCallArgument) emitGetter(putOnStack bool) {
+	e.expr.emitGetter(putOnStack)
+	if putOnStack {
+		e.c.emit(pushSpread)
+	}
+}

+ 6 - 1
compiler_stmt.go

@@ -410,7 +410,7 @@ func (c *compiler) compileLabeledForInOfStatement(into ast.ForInto, source ast.E
 	c.compileExpression(source).emitGetter(true)
 	c.compileExpression(source).emitGetter(true)
 	if enterPos != -1 {
 	if enterPos != -1 {
 		s := c.scope
 		s := c.scope
-		used := len(c.block.breaks) > 0
+		used := len(c.block.breaks) > 0 || s.isDynamic()
 		if !used {
 		if !used {
 			for _, b := range s.bindings {
 			for _, b := range s.bindings {
 				if b.useCount() > 0 {
 				if b.useCount() > 0 {
@@ -420,6 +420,11 @@ func (c *compiler) compileLabeledForInOfStatement(into ast.ForInto, source ast.E
 			}
 			}
 		}
 		}
 		if used {
 		if used {
+			// We need the stack untouched because it contains the source.
+			// This is not the most optimal way, but it's an edge case, hopefully quite rare.
+			for _, b := range s.bindings {
+				b.moveToStash()
+			}
 			enter := &enterBlock{}
 			enter := &enterBlock{}
 			c.p.code[enterPos] = enter
 			c.p.code[enterPos] = enter
 			c.leaveScopeBlock(enter)
 			c.leaveScopeBlock(enter)

+ 21 - 0
compiler_test.go

@@ -3960,6 +3960,27 @@ func TestDefParamsStackPtr(t *testing.T) {
 	testScript1(SCRIPT, _undefined, t)
 	testScript1(SCRIPT, _undefined, t)
 }
 }
 
 
+func TestNestedVariadicCalls(t *testing.T) {
+	const SCRIPT = `
+	function f() {
+		return Array.prototype.join.call(arguments, ",");
+	}
+	f(...[1], "a", f(...[2]));
+	`
+	testScript1(SCRIPT, asciiString("1,a,2"), t)
+}
+
+func TestVariadicNew(t *testing.T) {
+	const SCRIPT = `
+	function C() {
+		this.res = Array.prototype.join.call(arguments, ",");
+	}
+	var c = new C(...[1], "a", new C(...[2]).res);
+	c.res;
+	`
+	testScript1(SCRIPT, asciiString("1,a,2"), t)
+}
+
 /*
 /*
 func TestBabel(t *testing.T) {
 func TestBabel(t *testing.T) {
 	src, err := ioutil.ReadFile("babel7.js")
 	src, err := ioutil.ReadFile("babel7.js")

+ 19 - 4
parser/expression.go

@@ -295,8 +295,11 @@ func (self *_parser) parseObjectProperty() ast.Property {
 		case literal == "get" && self.token != token.COLON:
 		case literal == "get" && self.token != token.COLON:
 			idx := self.idx
 			idx := self.idx
 			_, value, _ := self.parseObjectPropertyKey()
 			_, value, _ := self.parseObjectPropertyKey()
+			idx1 := self.idx
 			parameterList := self.parseFunctionParameterList()
 			parameterList := self.parseFunctionParameterList()
-
+			if len(parameterList.List) > 0 || parameterList.Rest != nil {
+				self.error(idx1, "Getter must not have any formal parameters.")
+			}
 			node := &ast.FunctionLiteral{
 			node := &ast.FunctionLiteral{
 				Function:      idx,
 				Function:      idx,
 				ParameterList: parameterList,
 				ParameterList: parameterList,
@@ -392,7 +395,16 @@ func (self *_parser) parseArgumentList() (argumentList []ast.Expression, idx0, i
 	idx0 = self.expect(token.LEFT_PARENTHESIS)
 	idx0 = self.expect(token.LEFT_PARENTHESIS)
 	if self.token != token.RIGHT_PARENTHESIS {
 	if self.token != token.RIGHT_PARENTHESIS {
 		for {
 		for {
-			argumentList = append(argumentList, self.parseAssignmentExpression())
+			var item ast.Expression
+			if self.token == token.ELLIPSIS {
+				self.next()
+				item = &ast.SpreadElement{
+					Expression: self.parseAssignmentExpression(),
+				}
+			} else {
+				item = self.parseAssignmentExpression()
+			}
+			argumentList = append(argumentList, item)
 			if self.token != token.COMMA {
 			if self.token != token.COMMA {
 				break
 				break
 			}
 			}
@@ -818,8 +830,11 @@ func (self *_parser) parseConditionlExpression() ast.Expression {
 }
 }
 
 
 func (self *_parser) parseAssignmentExpression() ast.Expression {
 func (self *_parser) parseAssignmentExpression() ast.Expression {
+	parenthesis := false
 	if self.token == token.LET {
 	if self.token == token.LET {
 		self.token = token.IDENTIFIER
 		self.token = token.IDENTIFIER
+	} else if self.token == token.LEFT_PARENTHESIS {
+		parenthesis = true
 	}
 	}
 	left := self.parseConditionlExpression()
 	left := self.parseConditionlExpression()
 	var operator token.Token
 	var operator token.Token
@@ -858,12 +873,12 @@ func (self *_parser) parseAssignmentExpression() ast.Expression {
 		case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression:
 		case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression:
 			ok = true
 			ok = true
 		case *ast.ArrayLiteral:
 		case *ast.ArrayLiteral:
-			if operator == token.ASSIGN {
+			if !parenthesis && operator == token.ASSIGN {
 				left = self.reinterpretAsArrayAssignmentPattern(l)
 				left = self.reinterpretAsArrayAssignmentPattern(l)
 				ok = true
 				ok = true
 			}
 			}
 		case *ast.ObjectLiteral:
 		case *ast.ObjectLiteral:
-			if operator == token.ASSIGN {
+			if !parenthesis && operator == token.ASSIGN {
 				left = self.reinterpretAsObjectAssignmentPattern(l)
 				left = self.reinterpretAsObjectAssignmentPattern(l)
 				ok = true
 				ok = true
 			}
 			}

+ 1 - 0
parser/parser_test.go

@@ -485,6 +485,7 @@ func TestParserErr(t *testing.T) {
 			test(`abc.yield = 1`, nil)
 			test(`abc.yield = 1`, nil)
 			test(`var yield;`, nil)
 			test(`var yield;`, nil)
 		}
 		}
+		test(`0, { get a(param = null) {} };`, "(anonymous): Line 1:11 Getter must not have any formal parameters.")
 	})
 	})
 }
 }
 
 

+ 5 - 1
parser/statement.go

@@ -497,9 +497,13 @@ func (self *_parser) parseForOrForInStatement() ast.Statement {
 				forOf = true
 				forOf = true
 			}
 			}
 			if forIn || forOf {
 			if forIn || forOf {
-				switch expr.(type) {
+				switch e := expr.(type) {
 				case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression, *ast.Binding:
 				case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression, *ast.Binding:
 					// These are all acceptable
 					// These are all acceptable
+				case *ast.ObjectLiteral:
+					expr = self.reinterpretAsObjectAssignmentPattern(e)
+				case *ast.ArrayLiteral:
+					expr = self.reinterpretAsArrayAssignmentPattern(e)
 				default:
 				default:
 					self.error(idx, "Invalid left-hand side in for-in or for-of")
 					self.error(idx, "Invalid left-hand side in for-in or for-of")
 					self.nextStatement()
 					self.nextStatement()

+ 10 - 1
runtime.go

@@ -772,7 +772,16 @@ func (r *Runtime) builtin_thrower(FunctionCall) Value {
 func (r *Runtime) eval(srcVal valueString, direct, strict bool, this Value) Value {
 func (r *Runtime) eval(srcVal valueString, direct, strict bool, this Value) Value {
 	src := escapeInvalidUtf16(srcVal)
 	src := escapeInvalidUtf16(srcVal)
 	vm := r.vm
 	vm := r.vm
-	p, err := r.compile("<eval>", src, strict, true, !direct || vm.stash == &r.global.stash)
+	inGlobal := true
+	if direct {
+		for s := vm.stash; s != nil; s = s.outer {
+			if s.variable {
+				inGlobal = false
+				break
+			}
+		}
+	}
+	p, err := r.compile("<eval>", src, strict, true, inGlobal)
 	if err != nil {
 	if err != nil {
 		panic(err)
 		panic(err)
 	}
 	}

+ 19 - 0
tc39_test.go

@@ -177,6 +177,12 @@ var (
 		"test/language/expressions/arrow-function/scope-paramsbody-var-open.js":                                      true,
 		"test/language/expressions/arrow-function/scope-paramsbody-var-open.js":                                      true,
 		"test/language/expressions/arrow-function/scope-paramsbody-var-close.js":                                     true,
 		"test/language/expressions/arrow-function/scope-paramsbody-var-close.js":                                     true,
 		"test/language/expressions/arrow-function/scope-body-lex-distinct.js":                                        true,
 		"test/language/expressions/arrow-function/scope-body-lex-distinct.js":                                        true,
+		"test/language/statements/for-of/dstr/var-ary-ptrn-elem-id-init-fn-name-class.js":                            true,
+		"test/language/statements/for-of/dstr/var-obj-ptrn-id-init-fn-name-class.js":                                 true,
+		"test/language/statements/for-of/dstr/const-obj-ptrn-id-init-fn-name-class.js":                               true,
+		"test/language/statements/for-of/dstr/let-obj-ptrn-id-init-fn-name-class.js":                                 true,
+		"test/language/statements/for-of/dstr/const-ary-ptrn-elem-id-init-fn-name-class.js":                          true,
+		"test/language/statements/for-of/dstr/let-ary-ptrn-elem-id-init-fn-name-class.js":                            true,
 
 
 		// arrow-function
 		// arrow-function
 		"test/built-ins/Object/prototype/toString/proxy-function.js":                                    true,
 		"test/built-ins/Object/prototype/toString/proxy-function.js":                                    true,
@@ -223,6 +229,17 @@ var (
 		"test/language/statements/function/dstr/obj-ptrn-id-init-fn-name-arrow.js":                      true,
 		"test/language/statements/function/dstr/obj-ptrn-id-init-fn-name-arrow.js":                      true,
 		"test/language/statements/function/dstr/ary-ptrn-elem-id-init-fn-name-arrow.js":                 true,
 		"test/language/statements/function/dstr/ary-ptrn-elem-id-init-fn-name-arrow.js":                 true,
 		"test/language/statements/function/dstr/dflt-obj-ptrn-id-init-fn-name-arrow.js":                 true,
 		"test/language/statements/function/dstr/dflt-obj-ptrn-id-init-fn-name-arrow.js":                 true,
+		"test/language/statements/for-of/dstr/var-obj-ptrn-id-init-fn-name-arrow.js":                    true,
+		"test/language/statements/for-of/dstr/var-ary-ptrn-elem-id-init-fn-name-arrow.js":               true,
+		"test/language/statements/for-of/dstr/obj-prop-elem-init-fn-name-arrow.js":                      true,
+		"test/language/statements/for-of/dstr/obj-rest-order.js":                                        true,
+		"test/language/statements/for-of/dstr/obj-id-init-fn-name-arrow.js":                             true,
+		"test/language/statements/for-of/dstr/let-ary-ptrn-elem-id-init-fn-name-arrow.js":               true,
+		"test/language/statements/for-of/dstr/const-obj-ptrn-id-init-fn-name-arrow.js":                  true,
+		"test/language/statements/for-of/dstr/const-ary-ptrn-elem-id-init-fn-name-arrow.js":             true,
+		"test/language/statements/for-of/dstr/let-obj-ptrn-id-init-fn-name-arrow.js":                    true,
+		"test/language/statements/for-of/dstr/array-elem-init-fn-name-arrow.js":                         true,
+		"test/language/expressions/call/spread-obj-spread-order.js":                                     true,
 
 
 		// template strings
 		// template strings
 		"test/built-ins/String/raw/zero-literal-segments.js":                                           true,
 		"test/built-ins/String/raw/zero-literal-segments.js":                                           true,
@@ -371,6 +388,7 @@ var (
 		"sec-proxy-*",
 		"sec-proxy-*",
 		"sec-for-statement-*",
 		"sec-for-statement-*",
 		"sec-for-in-and-for-of-statements",
 		"sec-for-in-and-for-of-statements",
+		"sec-for-in-and-for-of-statements-*",
 		"sec-do-while-statement",
 		"sec-do-while-statement",
 		"sec-if-statement",
 		"sec-if-statement",
 		"sec-while-statement",
 		"sec-while-statement",
@@ -382,6 +400,7 @@ var (
 		"sec-arguments-exotic-objects-defineownproperty-p-desc",
 		"sec-arguments-exotic-objects-defineownproperty-p-desc",
 		"sec-other-properties-of-the-global-object-globalthis",
 		"sec-other-properties-of-the-global-object-globalthis",
 		"sec-variable-statement-runtime-semantics-evaluation",
 		"sec-variable-statement-runtime-semantics-evaluation",
+		"sec-function-calls-runtime-semantics-evaluation",
 		"sec-function-definitions",
 		"sec-function-definitions",
 		"sec-function-definitions-runtime-semantics-evaluation",
 		"sec-function-definitions-runtime-semantics-evaluation",
 		"sec-function-definitions-runtime-semantics-instantiatefunctionobject",
 		"sec-function-definitions-runtime-semantics-instantiatefunctionobject",

+ 2 - 0
token/token_const.go

@@ -172,6 +172,8 @@ var token2string = [...]string{
 	SEMICOLON:                   ";",
 	SEMICOLON:                   ";",
 	COLON:                       ":",
 	COLON:                       ":",
 	QUESTION_MARK:               "?",
 	QUESTION_MARK:               "?",
+	ARROW:                       "=>",
+	ELLIPSIS:                    "...",
 	IF:                          "if",
 	IF:                          "if",
 	IN:                          "in",
 	IN:                          "in",
 	OF:                          "of",
 	OF:                          "of",

+ 71 - 0
vm.go

@@ -1742,6 +1742,19 @@ func (_pushArraySpread) exec(vm *vm) {
 	vm.pc++
 	vm.pc++
 }
 }
 
 
+type _pushSpread struct{}
+
+var pushSpread _pushSpread
+
+func (_pushSpread) exec(vm *vm) {
+	vm.sp--
+	obj := vm.stack[vm.sp]
+	vm.r.iterate(vm.r.getIterator(obj, nil), func(val Value) {
+		vm.push(val)
+	})
+	vm.pc++
+}
+
 type _newArrayFromIter struct{}
 type _newArrayFromIter struct{}
 
 
 var newArrayFromIter _newArrayFromIter
 var newArrayFromIter _newArrayFromIter
@@ -2476,6 +2489,22 @@ func (numargs callEvalStrict) exec(vm *vm) {
 	vm.callEval(int(numargs), true)
 	vm.callEval(int(numargs), true)
 }
 }
 
 
+type _callEvalVariadic struct{}
+
+var callEvalVariadic _callEvalVariadic
+
+func (_callEvalVariadic) exec(vm *vm) {
+	vm.callEval(vm.sp-vm.sb-2, false)
+}
+
+type _callEvalVariadicStrict struct{}
+
+var callEvalVariadicStrict _callEvalVariadicStrict
+
+func (_callEvalVariadicStrict) exec(vm *vm) {
+	vm.callEval(vm.sp-vm.sb-2, true)
+}
+
 type _boxThis struct{}
 type _boxThis struct{}
 
 
 var boxThis _boxThis
 var boxThis _boxThis
@@ -2490,6 +2519,35 @@ func (_boxThis) exec(vm *vm) {
 	vm.pc++
 	vm.pc++
 }
 }
 
 
+type _startVariadic struct{}
+
+var startVariadic _startVariadic
+
+func (_startVariadic) exec(vm *vm) {
+	vm.push(valueInt(vm.sb))
+	vm.sb = vm.sp
+	vm.pc++
+}
+
+type _callVariadic struct{}
+
+var callVariadic _callVariadic
+
+func (_callVariadic) exec(vm *vm) {
+	call(vm.sp - vm.sb - 2).exec(vm)
+}
+
+type _endVariadic struct{}
+
+var endVariadic _endVariadic
+
+func (_endVariadic) exec(vm *vm) {
+	vm.sp--
+	vm.sb = int(vm.stack[vm.sp-1].(valueInt))
+	vm.stack[vm.sp-1] = vm.stack[vm.sp]
+	vm.pc++
+}
+
 type call uint32
 type call uint32
 
 
 func (numargs call) exec(vm *vm) {
 func (numargs call) exec(vm *vm) {
@@ -3417,6 +3475,14 @@ func (_throw) exec(vm *vm) {
 	panic(vm.stack[vm.sp-1])
 	panic(vm.stack[vm.sp-1])
 }
 }
 
 
+type _newVariadic struct{}
+
+var newVariadic _newVariadic
+
+func (_newVariadic) exec(vm *vm) {
+	_new(vm.sp - vm.sb - 1).exec(vm)
+}
+
 type _new uint32
 type _new uint32
 
 
 func (n _new) exec(vm *vm) {
 func (n _new) exec(vm *vm) {
@@ -3720,6 +3786,11 @@ func (r *Runtime) copyDataProperties(target, source Value) {
 		v := nilSafe(sourceObj.self.getStr(item.name, nil))
 		v := nilSafe(sourceObj.self.getStr(item.name, nil))
 		createDataPropertyOrThrow(targetObj, stringValueFromRaw(item.name), v)
 		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)
+	}
 }
 }
 
 
 type _copySpread struct{}
 type _copySpread struct{}