Procházet zdrojové kódy

Arrow function (#319)

* Implemented arrow functions. See #304.

* Define the name property for anonymous functions (including arrow functions)

* Treat "arguments" as a lexical binding to match the latest specification
Dmitry Panov před 4 roky
rodič
revize
32956a348b
14 změnil soubory, kde provedl 465 přidání a 141 odebrání
  1. 26 0
      ast/node.go
  2. 4 0
      builtin_function.go
  3. 5 1
      compiler.go
  4. 83 37
      compiler_expr.go
  5. 17 0
      compiler_test.go
  6. 35 13
      func.go
  7. 131 7
      parser/expression.go
  8. 32 1
      parser/lexer.go
  9. 4 0
      parser/parser_test.go
  10. 20 12
      parser/statement.go
  11. 39 6
      runtime.go
  12. 8 0
      runtime_test.go
  13. 34 60
      tc39_test.go
  14. 27 4
      vm.go

+ 26 - 0
ast/node.go

@@ -128,6 +128,23 @@ type (
 		DeclarationList []*VariableDeclaration
 	}
 
+	ConciseBody interface {
+		Node
+		_conciseBody()
+	}
+
+	ExpressionBody struct {
+		Expression Expression
+	}
+
+	ArrowFunctionLiteral struct {
+		Start           file.Idx
+		ParameterList   *ParameterList
+		Body            ConciseBody
+		Source          string
+		DeclarationList []*VariableDeclaration
+	}
+
 	Identifier struct {
 		Name unistring.String
 		Idx  file.Idx
@@ -238,6 +255,7 @@ func (*CallExpression) _expressionNode()        {}
 func (*ConditionalExpression) _expressionNode() {}
 func (*DotExpression) _expressionNode()         {}
 func (*FunctionLiteral) _expressionNode()       {}
+func (*ArrowFunctionLiteral) _expressionNode()  {}
 func (*Identifier) _expressionNode()            {}
 func (*NewExpression) _expressionNode()         {}
 func (*NullLiteral) _expressionNode()           {}
@@ -497,6 +515,9 @@ func (*SpreadElement) _property() {}
 
 func (*Identifier) _bindingTarget() {}
 
+func (*BlockStatement) _conciseBody() {}
+func (*ExpressionBody) _conciseBody() {}
+
 // ==== //
 // Node //
 // ==== //
@@ -525,6 +546,7 @@ func (self *CallExpression) Idx0() file.Idx        { return self.Callee.Idx0() }
 func (self *ConditionalExpression) Idx0() file.Idx { return self.Test.Idx0() }
 func (self *DotExpression) Idx0() file.Idx         { return self.Left.Idx0() }
 func (self *FunctionLiteral) Idx0() file.Idx       { return self.Function }
+func (self *ArrowFunctionLiteral) Idx0() file.Idx  { return self.Start }
 func (self *Identifier) Idx0() file.Idx            { return self.Idx }
 func (self *NewExpression) Idx0() file.Idx         { return self.New }
 func (self *NullLiteral) Idx0() file.Idx           { return self.Idx }
@@ -566,6 +588,7 @@ func (self *Binding) Idx0() file.Idx             { return self.Target.Idx0() }
 func (self *ForLoopInitializerVarDeclList) Idx0() file.Idx { return self.List[0].Idx0() }
 func (self *PropertyShort) Idx0() file.Idx                 { return self.Name.Idx }
 func (self *PropertyKeyed) Idx0() file.Idx                 { return self.Key.Idx0() }
+func (self *ExpressionBody) Idx0() file.Idx                { return self.Expression.Idx0() }
 
 // ==== //
 // Idx1 //
@@ -582,6 +605,7 @@ func (self *CallExpression) Idx1() file.Idx        { return self.RightParenthesi
 func (self *ConditionalExpression) Idx1() file.Idx { return self.Test.Idx1() }
 func (self *DotExpression) Idx1() file.Idx         { return self.Identifier.Idx1() }
 func (self *FunctionLiteral) Idx1() file.Idx       { return self.Body.Idx1() }
+func (self *ArrowFunctionLiteral) Idx1() file.Idx  { return self.Body.Idx1() }
 func (self *Identifier) Idx1() file.Idx            { return file.Idx(int(self.Idx) + len(self.Name)) }
 func (self *NewExpression) Idx1() file.Idx         { return self.RightParenthesis + 1 }
 func (self *NullLiteral) Idx1() file.Idx           { return file.Idx(int(self.Idx) + 4) } // "null"
@@ -656,3 +680,5 @@ func (self *PropertyShort) Idx1() file.Idx {
 }
 
 func (self *PropertyKeyed) Idx1() file.Idx { return self.Value.Idx1() }
+
+func (self *ExpressionBody) Idx1() file.Idx { return self.Expression.Idx1() }

+ 4 - 0
builtin_function.go

@@ -33,6 +33,8 @@ repeat:
 	switch f := obj.self.(type) {
 	case *funcObject:
 		return newStringValue(f.src)
+	case *arrowFuncObject:
+		return newStringValue(f.src)
 	case *nativeFuncObject:
 		return newStringValue(fmt.Sprintf("function %s() { [native code] }", nilSafe(f.getStr("name", nil)).toString()))
 	case *boundFuncObject:
@@ -46,6 +48,8 @@ repeat:
 		switch c := f.target.self.(type) {
 		case *funcObject:
 			name = c.src
+		case *arrowFuncObject:
+			name = c.src
 		case *nativeFuncObject:
 			name = nilSafe(f.getStr("name", nil)).toString().String()
 		case *boundFuncObject:

+ 5 - 1
compiler.go

@@ -248,6 +248,8 @@ type scope struct {
 
 	// is a function or a top-level lexical environment
 	function bool
+	// is an arrow function's top-level lexical environment (functions only)
+	arrow bool
 	// is a variable environment, i.e. the target for dynamically created var bindings
 	variable bool
 	// a function scope that has at least one direct eval() and non-strict, so the variables can be added dynamically
@@ -365,6 +367,8 @@ func (p *Program) _dumpCode(indent string, logger func(format string, args ...in
 		logger("%s %d: %T(%v)", indent, pc, ins, ins)
 		if f, ok := ins.(*newFunc); ok {
 			f.prg._dumpCode(indent+">", logger)
+		} else if f, ok := ins.(*newArrowFunc); ok {
+			f.prg._dumpCode(indent+">", logger)
 		}
 	}
 }
@@ -395,7 +399,7 @@ func (s *scope) lookupName(name unistring.String) (binding *binding, noDynamics
 				return
 			}
 		}
-		if name == "arguments" && curScope.function {
+		if name == "arguments" && curScope.function && !curScope.arrow {
 			curScope.argsNeeded = true
 			binding, _ = curScope.bindName(name)
 			return

+ 83 - 37
compiler_expr.go

@@ -115,10 +115,15 @@ type compiledIdentifierExpr struct {
 
 type compiledFunctionLiteral struct {
 	baseCompiledExpr
-	expr    *ast.FunctionLiteral
-	lhsName unistring.String
-	strict  *ast.StringLiteral
-	isExpr  bool
+	name            *ast.Identifier
+	parameterList   *ast.ParameterList
+	body            []ast.Statement
+	source          string
+	declarationList []*ast.VariableDeclaration
+	lhsName         unistring.String
+	strict          *ast.StringLiteral
+	isExpr          bool
+	isArrow         bool
 }
 
 type compiledBracketExpr struct {
@@ -225,6 +230,8 @@ func (c *compiler) compileExpression(v ast.Expression) compiledExpr {
 		return c.compileConditionalExpression(v)
 	case *ast.FunctionLiteral:
 		return c.compileFunctionLiteral(v, true)
+	case *ast.ArrowFunctionLiteral:
+		return c.compileArrowFunctionLiteral(v)
 	case *ast.DotExpression:
 		r := &compiledDotExpr{
 			left: c.compileExpression(v.Left),
@@ -740,7 +747,7 @@ func (e *compiledAssignExpr) emitGetter(putOnStack bool) {
 	switch e.operator {
 	case token.ASSIGN:
 		if fn, ok := e.right.(*compiledFunctionLiteral); ok {
-			if fn.expr.Name == nil {
+			if fn.name == nil {
 				if id, ok := e.left.(*compiledIdentifierExpr); ok {
 					fn.lhsName = id.name
 				}
@@ -847,11 +854,13 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 		src: e.c.p.src,
 	}
 	e.c.newScope()
-	e.c.scope.function = true
+	s := e.c.scope
+	s.function = true
+	s.arrow = e.isArrow
 
 	var name unistring.String
-	if e.expr.Name != nil {
-		name = e.expr.Name.Name
+	if e.name != nil {
+		name = e.name.Name
 	} else {
 		name = e.lhsName
 	}
@@ -868,8 +877,8 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 		typ: blockScope,
 	}
 
-	if !e.c.scope.strict {
-		e.c.scope.strict = e.strict != nil
+	if !s.strict {
+		s.strict = e.strict != nil
 	}
 
 	hasPatterns := false
@@ -877,12 +886,12 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 	firstDupIdx := -1
 	length := 0
 
-	if e.expr.ParameterList.Rest != nil {
+	if e.parameterList.Rest != nil {
 		hasPatterns = true // strictly speaking not, but we need to activate all the checks
 	}
 
 	// First, make sure that the first bindings correspond to the formal parameters
-	for _, item := range e.expr.ParameterList.List {
+	for _, item := range e.parameterList.List {
 		switch tgt := item.Target.(type) {
 		case *ast.Identifier:
 			offset := int(tgt.Idx) - 1
@@ -892,7 +901,7 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 			}
 			b.isArg = true
 		case ast.Pattern:
-			b := e.c.scope.addBinding(int(item.Idx0()) - 1)
+			b := s.addBinding(int(item.Idx0()) - 1)
 			b.isArg = true
 			hasPatterns = true
 		default:
@@ -902,7 +911,7 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 		if item.Initializer != nil {
 			hasInits = true
 		}
-		if hasPatterns || hasInits {
+		if hasPatterns || hasInits || e.isArrow {
 			if firstDupIdx >= 0 {
 				e.c.throwSyntaxError(firstDupIdx, "Duplicate parameter name not allowed in this context")
 				return
@@ -919,7 +928,7 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 
 	// create pattern bindings
 	if hasPatterns {
-		for _, item := range e.expr.ParameterList.List {
+		for _, item := range e.parameterList.List {
 			switch tgt := item.Target.(type) {
 			case *ast.Identifier:
 				// we already created those in the previous loop, skipping
@@ -927,17 +936,16 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 				e.c.compileParameterPatternBinding(tgt)
 			}
 		}
-		if rest := e.expr.ParameterList.Rest; rest != nil {
+		if rest := e.parameterList.Rest; rest != nil {
 			e.c.compileParameterPatternBinding(rest)
 		}
 	}
 
-	paramsCount := len(e.expr.ParameterList.List)
+	paramsCount := len(e.parameterList.List)
 
-	e.c.scope.numArgs = paramsCount
-	body := e.expr.Body.List
+	s.numArgs = paramsCount
+	body := e.body
 	funcs := e.c.extractFunctions(body)
-	s := e.c.scope
 	var calleeBinding *binding
 	preambleLen := 4 // enter, boxThis, createArgs, set
 	e.c.p.code = make([]instruction, preambleLen, 8)
@@ -947,8 +955,8 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 	enterFunc2Mark := -1
 
 	if hasPatterns || hasInits {
-		if e.isExpr && e.expr.Name != nil {
-			if b, created := s.bindNameLexical(e.expr.Name.Name, false, 0); created {
+		if e.isExpr && e.name != nil {
+			if b, created := s.bindNameLexical(e.name.Name, false, 0); created {
 				b.isConst = true
 				calleeBinding = b
 			}
@@ -957,7 +965,7 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 			e.c.emit(loadCallee)
 			calleeBinding.emitInit()
 		}
-		for i, item := range e.expr.ParameterList.List {
+		for i, item := range e.parameterList.List {
 			if pattern, ok := item.Target.(ast.Pattern); ok {
 				i := i
 				e.c.compilePatternInitExpr(func() {
@@ -996,7 +1004,7 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 				}
 			}
 		}
-		if rest := e.expr.ParameterList.Rest; rest != nil {
+		if rest := e.parameterList.Rest; rest != nil {
 			e.c.emitAssign(rest, e.c.compileEmitterExpr(
 				func() {
 					emitArgsRestMark = len(e.c.p.code)
@@ -1019,7 +1027,7 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 		varScope.variable = true
 		enterFunc2Mark = len(e.c.p.code)
 		e.c.emit(nil)
-		e.c.compileDeclList(e.expr.DeclarationList, true)
+		e.c.compileDeclList(e.declarationList, false)
 		e.c.createFunctionBindings(funcs)
 		e.c.compileLexicalDeclarationsFuncBody(body, calleeBinding)
 		for _, b := range varScope.bindings {
@@ -1036,11 +1044,11 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 		for _, b := range s.bindings[:paramsCount] {
 			b.isVar = true
 		}
-		e.c.compileDeclList(e.expr.DeclarationList, true)
+		e.c.compileDeclList(e.declarationList, true)
 		e.c.createFunctionBindings(funcs)
 		e.c.compileLexicalDeclarations(body, true)
-		if e.isExpr && e.expr.Name != nil {
-			if b, created := s.bindNameLexical(e.expr.Name.Name, false, 0); created {
+		if e.isExpr && e.name != nil {
+			if b, created := s.bindNameLexical(e.name.Name, false, 0); created {
 				b.isConst = true
 				calleeBinding = b
 			}
@@ -1082,7 +1090,7 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 			if s.strict {
 				b.isConst = true
 			} else {
-				b.isVar = true
+				b.isVar = e.c.scope.function
 			}
 			pos := preambleLen - 2
 			delta += 2
@@ -1179,7 +1187,11 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 	}
 	e.c.popScope()
 	e.c.p = savedPrg
-	e.c.emit(&newFunc{prg: p, length: uint32(length), name: name, srcStart: uint32(e.expr.Idx0() - 1), srcEnd: uint32(e.expr.Idx1() - 1), strict: strict})
+	if e.isArrow {
+		e.c.emit(&newArrowFunc{newFunc: newFunc{prg: p, length: uint32(length), name: name, source: e.source, strict: strict}})
+	} else {
+		e.c.emit(&newFunc{prg: p, length: uint32(length), name: name, source: e.source, strict: strict})
+	}
 	if !putOnStack {
 		e.c.emit(pop)
 	}
@@ -1191,9 +1203,42 @@ func (c *compiler) compileFunctionLiteral(v *ast.FunctionLiteral, isExpr bool) *
 		c.checkIdentifierLName(v.Name.Name, int(v.Name.Idx)-1)
 	}
 	r := &compiledFunctionLiteral{
-		expr:   v,
-		isExpr: isExpr,
-		strict: strictBody,
+		name:            v.Name,
+		parameterList:   v.ParameterList,
+		body:            v.Body.List,
+		source:          v.Source,
+		declarationList: v.DeclarationList,
+		isExpr:          isExpr,
+		strict:          strictBody,
+	}
+	r.init(c, v.Idx0())
+	return r
+}
+
+func (c *compiler) compileArrowFunctionLiteral(v *ast.ArrowFunctionLiteral) *compiledFunctionLiteral {
+	var strictBody *ast.StringLiteral
+	var body []ast.Statement
+	switch b := v.Body.(type) {
+	case *ast.BlockStatement:
+		strictBody = c.isStrictStatement(b)
+		body = b.List
+	case *ast.ExpressionBody:
+		body = []ast.Statement{
+			&ast.ReturnStatement{
+				Argument: b.Expression,
+			},
+		}
+	default:
+		c.throwSyntaxError(int(b.Idx0())-1, "Unsupported ConciseBody type: %T", b)
+	}
+	r := &compiledFunctionLiteral{
+		parameterList:   v.ParameterList,
+		body:            body,
+		source:          v.Source,
+		declarationList: v.DeclarationList,
+		isExpr:          true,
+		isArrow:         true,
+		strict:          strictBody,
 	}
 	r.init(c, v.Idx0())
 	return r
@@ -1207,7 +1252,9 @@ func (e *compiledThisExpr) emitGetter(putOnStack bool) {
 		}
 
 		if scope != nil {
-			scope.thisNeeded = true
+			if !scope.arrow {
+				scope.thisNeeded = true
+			}
 			e.c.emit(loadStack(0))
 		} else {
 			e.c.emit(loadGlobalObject)
@@ -1671,12 +1718,11 @@ func (e *compiledObjectLiteral) emitGetter(putOnStack bool) {
 			default:
 				keyExpr.emitGetter(true)
 				computed = true
-				//e.c.throwSyntaxError(e.offset, "non-literal properties in object literal are not supported yet")
 			}
 			valueExpr := e.c.compileExpression(prop.Value)
 			var anonFn *compiledFunctionLiteral
 			if fn, ok := valueExpr.(*compiledFunctionLiteral); ok {
-				if fn.expr.Name == nil {
+				if fn.name == nil {
 					anonFn = fn
 					fn.lhsName = key
 				}
@@ -1837,7 +1883,7 @@ func (e *compiledCallExpr) emitGetter(putOnStack bool) {
 	if calleeName == "eval" {
 		foundFunc, foundVar := false, false
 		for sc := e.c.scope; sc != nil; sc = sc.outer {
-			if !foundFunc && sc.function {
+			if !foundFunc && sc.function && !sc.arrow {
 				foundFunc = true
 				sc.thisNeeded, sc.argsNeeded = true, true
 			}

+ 17 - 0
compiler_test.go

@@ -1692,6 +1692,23 @@ func TestArgumentsRedeclareInEval(t *testing.T) {
 	testScript1(TESTLIB+SCRIPT, _undefined, t)
 }
 
+func TestArgumentsRedeclareArrow(t *testing.T) {
+	const SCRIPT = `
+	const oldArguments = globalThis.arguments;
+	let count = 0;
+	const f = (p = eval("var arguments = 'param'"), q = () => arguments) => {
+	  var arguments = "local";
+	  assert.sameValue(arguments, "local", "arguments");
+	  assert.sameValue(q(), "param", "q");
+	  count++;
+	}
+	f();
+	assert.sameValue(count, 1);
+	assert.sameValue(globalThis.arguments, oldArguments, "globalThis.arguments unchanged");
+	`
+	testScript1(TESTLIB+SCRIPT, _undefined, t)
+}
+
 func TestEvalParamWithDef(t *testing.T) {
 	const SCRIPT = `
 	function f(param = 0) {

+ 35 - 13
func.go

@@ -12,12 +12,23 @@ type baseFuncObject struct {
 	lenProp valueProperty
 }
 
-type funcObject struct {
+type baseJsFuncObject struct {
 	baseFuncObject
 
-	stash *stash
-	prg   *Program
-	src   string
+	stash  *stash
+	prg    *Program
+	src    string
+	strict bool
+}
+
+type funcObject struct {
+	baseJsFuncObject
+}
+
+type arrowFuncObject struct {
+	baseJsFuncObject
+	this      Value
+	newTarget Value
 }
 
 type nativeFuncObject struct {
@@ -129,18 +140,18 @@ func (f *funcObject) Call(call FunctionCall) Value {
 	return f.call(call, nil)
 }
 
-func (f *funcObject) call(call FunctionCall, newTarget Value) Value {
+func (f *arrowFuncObject) Call(call FunctionCall) Value {
+	return f._call(call, f.newTarget, f.this)
+}
+
+func (f *baseJsFuncObject) _call(call FunctionCall, newTarget, this Value) Value {
 	vm := f.val.runtime.vm
 	pc := vm.pc
 
 	vm.stack.expand(vm.sp + len(call.Arguments) + 1)
 	vm.stack[vm.sp] = f.val
 	vm.sp++
-	if call.This != nil {
-		vm.stack[vm.sp] = call.This
-	} else {
-		vm.stack[vm.sp] = _undefined
-	}
+	vm.stack[vm.sp] = this
 	vm.sp++
 	for _, arg := range call.Arguments {
 		if arg != nil {
@@ -162,6 +173,11 @@ func (f *funcObject) call(call FunctionCall, newTarget Value) Value {
 	vm.pc = pc
 	vm.halt = false
 	return vm.pop()
+
+}
+
+func (f *funcObject) call(call FunctionCall, newTarget Value) Value {
+	return f._call(call, newTarget, nilSafe(call.This))
 }
 
 func (f *funcObject) export(*objectExportCtx) interface{} {
@@ -180,12 +196,18 @@ func (f *funcObject) assertConstructor() func(args []Value, newTarget *Object) *
 	return f.construct
 }
 
+func (f *arrowFuncObject) exportType() reflect.Type {
+	return reflect.TypeOf(f.Call)
+}
+
+func (f *arrowFuncObject) assertCallable() (func(FunctionCall) Value, bool) {
+	return f.Call, true
+}
+
 func (f *baseFuncObject) init(name unistring.String, length int) {
 	f.baseObject.init()
 
-	if name != "" {
-		f._putProp("name", stringValueFromRaw(name), false, false, true)
-	}
+	f._putProp("name", stringValueFromRaw(name), false, false, true)
 
 	f.lenProp.configurable = true
 	f.lenProp.value = valueInt(length)

+ 131 - 7
parser/expression.go

@@ -85,10 +85,11 @@ func (self *_parser) parsePrimaryExpression() ast.Expression {
 	case token.LEFT_BRACKET:
 		return self.parseArrayLiteral()
 	case token.LEFT_PARENTHESIS:
-		self.expect(token.LEFT_PARENTHESIS)
+		/*self.expect(token.LEFT_PARENTHESIS)
 		expression := self.parseExpression()
 		self.expect(token.RIGHT_PARENTHESIS)
-		return expression
+		return expression*/
+		return self.parseParenthesisedExpression()
 	case token.THIS:
 		self.next()
 		return &ast.ThisExpression{
@@ -103,6 +104,76 @@ func (self *_parser) parsePrimaryExpression() ast.Expression {
 	return &ast.BadExpression{From: idx, To: self.idx}
 }
 
+func (self *_parser) reinterpretSequenceAsArrowFuncParams(seq *ast.SequenceExpression) *ast.ParameterList {
+	firstRestIdx := -1
+	params := make([]*ast.Binding, 0, len(seq.Sequence))
+	for i, item := range seq.Sequence {
+		if _, ok := item.(*ast.SpreadElement); ok {
+			if firstRestIdx == -1 {
+				firstRestIdx = i
+				continue
+			}
+		}
+		if firstRestIdx != -1 {
+			self.error(seq.Sequence[firstRestIdx].Idx0(), "Rest parameter must be last formal parameter")
+			return &ast.ParameterList{}
+		}
+		params = append(params, self.reinterpretAsBinding(item))
+	}
+	var rest ast.Expression
+	if firstRestIdx != -1 {
+		rest = self.reinterpretAsBindingRestElement(seq.Sequence[firstRestIdx])
+	}
+	return &ast.ParameterList{
+		List: params,
+		Rest: rest,
+	}
+}
+
+func (self *_parser) parseParenthesisedExpression() ast.Expression {
+	opening := self.idx
+	self.expect(token.LEFT_PARENTHESIS)
+	var list []ast.Expression
+	if self.token != token.RIGHT_PARENTHESIS {
+		for {
+			if self.token == token.ELLIPSIS {
+				start := self.idx
+				self.errorUnexpectedToken(token.ELLIPSIS)
+				self.next()
+				expr := self.parseAssignmentExpression()
+				list = append(list, &ast.BadExpression{
+					From: start,
+					To:   expr.Idx1(),
+				})
+			} else {
+				list = append(list, self.parseAssignmentExpression())
+			}
+			if self.token != token.COMMA {
+				break
+			}
+			self.next()
+			if self.token == token.RIGHT_PARENTHESIS {
+				self.errorUnexpectedToken(token.RIGHT_PARENTHESIS)
+				break
+			}
+		}
+	}
+	self.expect(token.RIGHT_PARENTHESIS)
+	if len(list) == 1 && len(self.errors) == 0 {
+		return list[0]
+	}
+	if len(list) == 0 {
+		self.errorUnexpectedToken(token.RIGHT_PARENTHESIS)
+		return &ast.BadExpression{
+			From: opening,
+			To:   self.idx,
+		}
+	}
+	return &ast.SequenceExpression{
+		Sequence: list,
+	}
+}
+
 func (self *_parser) parseRegExpLiteral() *ast.RegExpLiteral {
 
 	offset := self.chrOffset - 1 // Opening slash already gotten
@@ -271,7 +342,7 @@ func (self *_parser) parseObjectProperty() ast.Property {
 				Function:      idx,
 				ParameterList: parameterList,
 			}
-			self.parseFunctionBlock(node)
+			node.Body, node.DeclarationList = self.parseFunctionBlock()
 
 			return &ast.PropertyKeyed{
 				Key:   value,
@@ -307,7 +378,7 @@ func (self *_parser) parseObjectProperty() ast.Property {
 				Function:      idx,
 				ParameterList: parameterList,
 			}
-			self.parseFunctionBlock(node)
+			node.Body, node.DeclarationList = self.parseFunctionBlock()
 			return &ast.PropertyKeyed{
 				Key:   value,
 				Kind:  ast.PropertyKindGet,
@@ -323,7 +394,7 @@ func (self *_parser) parseObjectProperty() ast.Property {
 				ParameterList: parameterList,
 			}
 
-			self.parseFunctionBlock(node)
+			node.Body, node.DeclarationList = self.parseFunctionBlock()
 
 			return &ast.PropertyKeyed{
 				Key:   value,
@@ -815,7 +886,7 @@ func (self *_parser) parseLogicalOrExpression() ast.Expression {
 	return left
 }
 
-func (self *_parser) parseConditionlExpression() ast.Expression {
+func (self *_parser) parseConditionalExpression() ast.Expression {
 	left := self.parseLogicalOrExpression()
 
 	if self.token == token.QUESTION_MARK {
@@ -833,13 +904,16 @@ func (self *_parser) parseConditionlExpression() ast.Expression {
 }
 
 func (self *_parser) parseAssignmentExpression() ast.Expression {
+	start := self.idx
 	parenthesis := false
+	var state parserState
 	if self.token == token.LET {
 		self.token = token.IDENTIFIER
 	} else if self.token == token.LEFT_PARENTHESIS {
+		self.mark(&state)
 		parenthesis = true
 	}
-	left := self.parseConditionlExpression()
+	left := self.parseConditionalExpression()
 	var operator token.Token
 	switch self.token {
 	case token.ASSIGN:
@@ -866,6 +940,35 @@ func (self *_parser) parseAssignmentExpression() ast.Expression {
 		operator = token.SHIFT_RIGHT
 	case token.UNSIGNED_SHIFT_RIGHT_ASSIGN:
 		operator = token.UNSIGNED_SHIFT_RIGHT
+	case token.ARROW:
+		var paramList *ast.ParameterList
+		if id, ok := left.(*ast.Identifier); ok {
+			paramList = &ast.ParameterList{
+				Opening: id.Idx,
+				Closing: id.Idx1(),
+				List: []*ast.Binding{{
+					Target: id,
+				}},
+			}
+		} else if parenthesis {
+			if seq, ok := left.(*ast.SequenceExpression); ok && len(self.errors) == 0 {
+				paramList = self.reinterpretSequenceAsArrowFuncParams(seq)
+			} else {
+				self.restore(&state)
+				paramList = self.parseFunctionParameterList()
+			}
+		} else {
+			self.error(left.Idx0(), "Malformed arrow function parameter list")
+			return &ast.BadExpression{From: left.Idx0(), To: left.Idx1()}
+		}
+		self.expect(token.ARROW)
+		node := &ast.ArrowFunctionLiteral{
+			Start:         start,
+			ParameterList: paramList,
+		}
+		node.Body, node.DeclarationList = self.parseArrowFunctionBody()
+		node.Source = self.slice(node.Start, node.Body.Idx1())
+		return node
 	}
 
 	if operator != 0 {
@@ -1111,6 +1214,27 @@ func (self *_parser) reinterpretAsBindingElement(expr ast.Expression) ast.Expres
 	}
 }
 
+func (self *_parser) reinterpretAsBinding(expr ast.Expression) *ast.Binding {
+	switch expr := expr.(type) {
+	case *ast.AssignExpression:
+		if expr.Operator == token.ASSIGN {
+			return &ast.Binding{
+				Target:      self.reinterpretAsDestructBindingTarget(expr.Left),
+				Initializer: expr.Right,
+			}
+		} else {
+			self.error(expr.Idx0(), "Invalid destructuring assignment target")
+			return &ast.Binding{
+				Target: &ast.BadExpression{From: expr.Idx0(), To: expr.Idx1()},
+			}
+		}
+	default:
+		return &ast.Binding{
+			Target: self.reinterpretAsDestructBindingTarget(expr),
+		}
+	}
+}
+
 func (self *_parser) reinterpretAsDestructAssignTarget(item ast.Expression) ast.Expression {
 	switch item := item.(type) {
 	case nil:

+ 32 - 1
parser/lexer.go

@@ -184,6 +184,33 @@ func isId(tkn token.Token) bool {
 	return false
 }
 
+type parserState struct {
+	tok                                token.Token
+	literal                            string
+	parsedLiteral                      unistring.String
+	implicitSemicolon, insertSemicolon bool
+	chr                                rune
+	chrOffset, offset                  int
+	errorCount                         int
+}
+
+func (self *_parser) mark(state *parserState) *parserState {
+	if state == nil {
+		state = &parserState{}
+	}
+	state.tok, state.literal, state.parsedLiteral, state.implicitSemicolon, state.insertSemicolon, state.chr, state.chrOffset, state.offset =
+		self.token, self.literal, self.parsedLiteral, self.implicitSemicolon, self.insertSemicolon, self.chr, self.chrOffset, self.offset
+
+	state.errorCount = len(self.errors)
+	return state
+}
+
+func (self *_parser) restore(state *parserState) {
+	self.token, self.literal, self.parsedLiteral, self.implicitSemicolon, self.insertSemicolon, self.chr, self.chrOffset, self.offset =
+		state.tok, state.literal, state.parsedLiteral, state.implicitSemicolon, state.insertSemicolon, state.chr, state.chrOffset, state.offset
+	self.errors = self.errors[:state.errorCount]
+}
+
 func (self *_parser) peek() token.Token {
 	implicitSemicolon, insertSemicolon, chr, chrOffset, offset := self.implicitSemicolon, self.insertSemicolon, self.chr, self.chrOffset, self.offset
 	tok, _, _, _ := self.scan()
@@ -363,7 +390,11 @@ func (self *_parser) scan() (tkn token.Token, literal string, parsedLiteral unis
 			case '=':
 				if self.chr == '>' {
 					self.read()
-					tkn = token.ARROW
+					if self.implicitSemicolon {
+						tkn = token.ILLEGAL
+					} else {
+						tkn = token.ARROW
+					}
 				} else {
 					tkn = self.switch2(token.ASSIGN, token.EQUAL)
 					if tkn == token.EQUAL && self.chr == '=' {

+ 4 - 0
parser/parser_test.go

@@ -881,6 +881,10 @@ func TestParser(t *testing.T) {
 
 		test(`[a, b] = [1, 2]`, nil)
 		test(`({"a b": {}} = {})`, nil)
+
+		test(`ref = (a, b = 39,) => {
+		};`, nil)
+		test(`(a,) => {}`, nil)
 	})
 }
 

+ 20 - 12
parser/statement.go

@@ -194,24 +194,32 @@ func (self *_parser) parseFunction(declaration bool) *ast.FunctionLiteral {
 	}
 	node.Name = name
 	node.ParameterList = self.parseFunctionParameterList()
-	self.parseFunctionBlock(node)
+	node.Body, node.DeclarationList = self.parseFunctionBlock()
 	node.Source = self.slice(node.Idx0(), node.Idx1())
 
 	return node
 }
 
-func (self *_parser) parseFunctionBlock(node *ast.FunctionLiteral) {
-	{
-		self.openScope()
-		inFunction := self.scope.inFunction
-		self.scope.inFunction = true
-		defer func() {
-			self.scope.inFunction = inFunction
-			self.closeScope()
-		}()
-		node.Body = self.parseBlockStatement()
-		node.DeclarationList = self.scope.declarationList
+func (self *_parser) parseFunctionBlock() (body *ast.BlockStatement, declarationList []*ast.VariableDeclaration) {
+	self.openScope()
+	inFunction := self.scope.inFunction
+	self.scope.inFunction = true
+	defer func() {
+		self.scope.inFunction = inFunction
+		self.closeScope()
+	}()
+	body = self.parseBlockStatement()
+	declarationList = self.scope.declarationList
+	return
+}
+
+func (self *_parser) parseArrowFunctionBody() (ast.ConciseBody, []*ast.VariableDeclaration) {
+	if self.token == token.LEFT_BRACE {
+		return self.parseFunctionBlock()
 	}
+	return &ast.ExpressionBody{
+		Expression: self.parseAssignmentExpression(),
+	}, nil
 }
 
 func (self *_parser) parseDebuggerStatement() ast.Statement {

+ 39 - 6
runtime.go

@@ -352,9 +352,11 @@ func (r *Runtime) init() {
 	}
 	r.vm.init()
 
-	r.global.FunctionPrototype = r.newNativeFunc(func(FunctionCall) Value {
+	funcProto := r.newNativeFunc(func(FunctionCall) Value {
 		return _undefined
 	}, nil, " ", nil, 0)
+	r.global.FunctionPrototype = funcProto
+	funcProtoObj := funcProto.self.(*nativeFuncObject)
 
 	r.global.IteratorPrototype = r.newLazyObject(r.createIterProto)
 
@@ -391,6 +393,9 @@ func (r *Runtime) init() {
 		setterFunc: r.global.thrower,
 		accessor:   true,
 	}
+
+	funcProtoObj._put("caller", r.global.throwerProperty)
+	funcProtoObj._put("arguments", r.global.throwerProperty)
 }
 
 func (r *Runtime) typeErrorResult(throw bool, args ...interface{}) {
@@ -487,13 +492,35 @@ func (r *Runtime) newFunc(name unistring.String, len int, strict bool) (f *funcO
 	f.class = classFunction
 	f.val = v
 	f.extensible = true
+	f.strict = strict
 	v.self = f
 	f.prototype = r.global.FunctionPrototype
 	f.init(name, len)
-	if strict {
-		f._put("caller", r.global.throwerProperty)
-		f._put("arguments", r.global.throwerProperty)
+	return
+}
+
+func (r *Runtime) newArrowFunc(name unistring.String, len int, strict bool) (f *arrowFuncObject) {
+	v := &Object{runtime: r}
+
+	f = &arrowFuncObject{}
+	f.class = classFunction
+	f.val = v
+	f.extensible = true
+	f.strict = strict
+
+	vm := r.vm
+	var this Value
+	if vm.sb >= 0 {
+		this = vm.stack[vm.sb]
+	} else {
+		this = vm.r.globalObject
 	}
+
+	f.this = this
+	f.newTarget = vm.newTarget
+	v.self = f
+	f.prototype = r.global.FunctionPrototype
+	f.init(name, len)
 	return
 }
 
@@ -764,8 +791,14 @@ func (r *Runtime) throw(e Value) {
 	panic(e)
 }
 
-func (r *Runtime) builtin_thrower(FunctionCall) Value {
-	r.typeErrorResult(true, "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them")
+func (r *Runtime) builtin_thrower(call FunctionCall) Value {
+	obj := r.toObject(call.This)
+	strict := true
+	switch fn := obj.self.(type) {
+	case *funcObject:
+		strict = fn.strict
+	}
+	r.typeErrorResult(strict, "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them")
 	return nil
 }
 

+ 8 - 0
runtime_test.go

@@ -2254,6 +2254,14 @@ func TestCoverFuncName(t *testing.T) {
 	testScript1(TESTLIB+SCRIPT, _undefined, t)
 }
 
+func TestAnonFuncName(t *testing.T) {
+	const SCRIPT = `
+	const d = Object.getOwnPropertyDescriptor((function() {}), 'name');
+	d !== undefined && d.value === '';
+	`
+	testScript1(SCRIPT, valueTrue, t)
+}
+
 /*
 func TestArrayConcatSparse(t *testing.T) {
 function foo(a,b,c)

+ 34 - 60
tc39_test.go

@@ -55,6 +55,8 @@ var (
 		"test/language/expressions/arrow-function/scope-param-rest-elem-var-close.js": true,
 		"test/language/expressions/arrow-function/scope-param-elem-var-close.js":      true,
 		"test/language/expressions/arrow-function/scope-param-elem-var-open.js":       true,
+		"test/language/function-code/each-param-has-own-scope.js":                     true,
+		"test/language/function-code/each-param-has-own-non-shared-eval-scope.js":     true,
 
 		// These tests are out of date (fixed in https://github.com/tc39/test262/commit/7d998a098e5420cb4b6ee4a05eb8c386d750c596)
 		"test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/key-is-numericindex.js":                   true,
@@ -75,6 +77,10 @@ var (
 		// 167e596a649ede35df11d03cb3c093941c9cf396
 		"test/built-ins/TypedArrayConstructors/internals/Set/detached-buffer.js": true,
 
+		// 59a1a016b7cf5cf43f66b274c7d1db4ec6066935
+		"test/language/expressions/function/name.js":                 true,
+		"test/built-ins/Proxy/revocable/revocation-function-name.js": true,
+
 		"test/built-ins/Date/prototype/toISOString/15.9.5.43-0-8.js":  true, // timezone
 		"test/built-ins/Date/prototype/toISOString/15.9.5.43-0-9.js":  true, // timezone
 		"test/built-ins/Date/prototype/toISOString/15.9.5.43-0-10.js": true, // timezone
@@ -204,65 +210,22 @@ var (
 		"test/language/statements/for-of/dstr/let-ary-ptrn-elem-id-init-fn-name-class.js":                            true,
 		"test/language/statements/try/dstr/obj-ptrn-id-init-fn-name-class.js":                                        true,
 		"test/language/statements/try/dstr/ary-ptrn-elem-id-init-fn-name-class.js":                                   true,
-
-		// arrow-function
-		"test/built-ins/Object/prototype/toString/proxy-function.js":                                    true,
-		"test/built-ins/Array/prototype/pop/throws-with-string-receiver.js":                             true,
-		"test/built-ins/Array/prototype/push/throws-with-string-receiver.js":                            true,
-		"test/built-ins/Array/prototype/shift/throws-with-string-receiver.js":                           true,
-		"test/built-ins/Array/prototype/unshift/throws-with-string-receiver.js":                         true,
-		"test/built-ins/Date/prototype/toString/non-date-receiver.js":                                   true,
-		"test/built-ins/Number/prototype/toExponential/range.js":                                        true,
-		"test/built-ins/Number/prototype/toFixed/range.js":                                              true,
-		"test/built-ins/Number/prototype/toPrecision/range.js":                                          true,
-		"test/built-ins/TypedArray/prototype/sort/stability.js":                                         true,
-		"test/built-ins/RegExp/named-groups/functional-replace-global.js":                               true,
-		"test/built-ins/RegExp/named-groups/functional-replace-non-global.js":                           true,
-		"test/built-ins/Array/prototype/sort/stability-513-elements.js":                                 true,
-		"test/built-ins/Array/prototype/sort/stability-5-elements.js":                                   true,
-		"test/built-ins/Array/prototype/sort/stability-2048-elements.js":                                true,
-		"test/built-ins/Array/prototype/sort/stability-11-elements.js":                                  true,
-		"test/language/statements/variable/fn-name-arrow.js":                                            true,
-		"test/language/statements/let/fn-name-arrow.js":                                                 true,
-		"test/language/statements/const/fn-name-arrow.js":                                               true,
-		"test/built-ins/Proxy/getPrototypeOf/instanceof-target-not-extensible-not-same-proto-throws.js": true,
-		"test/language/statements/let/dstr/obj-ptrn-id-init-fn-name-arrow.js":                           true,
-		"test/language/statements/let/dstr/ary-ptrn-elem-id-init-fn-name-arrow.js":                      true,
-		"test/language/statements/for/dstr/var-obj-ptrn-id-init-fn-name-arrow.js":                       true,
-		"test/language/statements/for/dstr/var-ary-ptrn-elem-id-init-fn-name-arrow.js":                  true,
-		"test/language/statements/for/dstr/let-ary-ptrn-elem-id-init-fn-name-arrow.js":                  true,
-		"test/language/statements/for/dstr/let-obj-ptrn-id-init-fn-name-arrow.js":                       true,
-		"test/language/statements/const/dstr/ary-ptrn-elem-id-init-fn-name-arrow.js":                    true,
-		"test/language/statements/for/dstr/const-obj-ptrn-id-init-fn-name-arrow.js":                     true,
-		"test/language/statements/const/dstr/obj-ptrn-id-init-fn-name-arrow.js":                         true,
-		"test/language/statements/for/dstr/const-ary-ptrn-elem-id-init-fn-name-arrow.js":                true,
-		"test/language/statements/variable/dstr/obj-ptrn-id-init-fn-name-arrow.js":                      true,
-		"test/language/statements/variable/dstr/ary-ptrn-elem-id-init-fn-name-arrow.js":                 true,
-		"test/language/expressions/assignment/dstr/obj-prop-elem-init-fn-name-arrow.js":                 true,
-		"test/language/expressions/assignment/dstr/obj-id-init-fn-name-arrow.js":                        true,
-		"test/language/expressions/assignment/dstr/obj-rest-order.js":                                   true,
-		"test/language/expressions/assignment/dstr/array-elem-init-fn-name-arrow.js":                    true,
-		"test/language/expressions/function/dstr/obj-ptrn-id-init-fn-name-arrow.js":                     true,
-		"test/language/expressions/function/dstr/dflt-obj-ptrn-id-init-fn-name-arrow.js":                true,
-		"test/language/expressions/function/dstr/dflt-ary-ptrn-elem-id-init-fn-name-arrow.js":           true,
-		"test/language/expressions/function/dstr/ary-ptrn-elem-id-init-fn-name-arrow.js":                true,
-		"test/language/statements/function/dstr/dflt-ary-ptrn-elem-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/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,
-		"test/language/statements/try/dstr/obj-ptrn-id-init-fn-name-arrow.js":                           true,
-		"test/language/statements/try/dstr/ary-ptrn-elem-id-init-fn-name-arrow.js":                      true,
+		"test/language/expressions/arrow-function/dstr/ary-ptrn-elem-id-init-fn-name-class.js":                       true,
+		"test/language/expressions/arrow-function/dstr/dflt-obj-ptrn-id-init-fn-name-class.js":                       true,
+		"test/language/expressions/arrow-function/dstr/obj-ptrn-id-init-fn-name-class.js":                            true,
+		"test/language/expressions/arrow-function/dstr/dflt-ary-ptrn-elem-id-init-fn-name-class.js":                  true,
+		"test/language/statements/class/static-method-length-dflt.js":                                                true,
+		"test/language/statements/class/setter-length-dflt.js":                                                       true,
+		"test/language/statements/class/restricted-properties.js":                                                    true,
+		"test/language/statements/class/method-length-dflt.js":                                                       true,
+		"test/language/statements/class/definition/methods-restricted-properties.js":                                 true,
+		"test/language/expressions/class/static-method-length-dflt.js":                                               true,
+		"test/language/expressions/class/setter-length-dflt.js":                                                      true,
+		"test/language/expressions/class/restricted-properties.js":                                                   true,
+		"test/language/expressions/class/method-length-dflt.js":                                                      true,
+		"test/language/expressions/arrow-function/lexical-super-property-from-within-constructor.js":                 true,
+		"test/language/expressions/arrow-function/lexical-super-property.js":                                         true,
+		"test/language/expressions/arrow-function/lexical-supercall-from-immediately-invoked-arrow.js":               true,
 
 		// template strings
 		"test/built-ins/String/raw/zero-literal-segments.js":                                                       true,
@@ -274,6 +237,8 @@ var (
 		"test/built-ins/Array/prototype/slice/length-exceeding-integer-limit-proxied-array.js":                     true,
 		"test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/conversion-operation-consistent-nan.js": true,
 		"test/built-ins/TypedArrayConstructors/internals/Set/conversion-operation-consistent-nan.js":               true,
+		"test/built-ins/RegExp/named-groups/functional-replace-non-global.js":                                      true,
+		"test/built-ins/RegExp/named-groups/functional-replace-global.js":                                          true,
 
 		// restricted unicode regexp syntax
 		"test/built-ins/RegExp/unicode_restricted_quantifiable_assertion.js":         true,
@@ -332,12 +297,12 @@ var (
 	}
 
 	featuresBlackList = []string{
-		"arrow-function",
 		"async-iteration",
 		"BigInt",
 		"class",
 		"generators",
 		"String.prototype.replaceAll",
+		"String.prototype.at",
 		"super",
 	}
 
@@ -374,7 +339,10 @@ var (
 		"13.13",
 		"13.14",
 		"13.15",
+		"14.1",
+		"14.2",
 		"14.3.8",
+		"16.1",
 		"18",
 		"19",
 		"20",
@@ -434,6 +402,9 @@ var (
 		"sec-function-definitions-static-semantics-early-errors",
 		"sec-functiondeclarationinstantiation",
 		"sec-functiondeclarations-in-ifstatement-statement-clauses",
+		"sec-arrow-function-definitions",
+		"sec-arrow-function-definitions-runtime-semantics-evaluation",
+		"sec-arrow-function-definitions-static-semantics-early-errors",
 		"sec-evaldeclarationinstantiation",
 		"sec-integer-indexed-exotic-objects-defineownproperty-p-desc",
 		"sec-integer-indexed-exotic-objects-get-p-receiver",
@@ -625,6 +596,9 @@ func (ctx *tc39TestCtx) runTC39File(name string, t testing.TB) {
 		t.Errorf("Could not parse %s: %v", name, err)
 		return
 	}
+	if meta.hasFlag("async") {
+		t.Skip("async")
+	}
 	if meta.Es5id == "" {
 		skip := true
 		//t.Logf("%s: Not ES5, skipped", name)

+ 27 - 4
vm.go

@@ -2607,6 +2607,16 @@ repeat:
 		vm.pc = 0
 		vm.stack[vm.sp-n-1], vm.stack[vm.sp-n-2] = vm.stack[vm.sp-n-2], vm.stack[vm.sp-n-1]
 		return
+	case *arrowFuncObject:
+		vm.pc++
+		vm.pushCtx()
+		vm.args = n
+		vm.prg = f.prg
+		vm.stash = f.stash
+		vm.pc = 0
+		vm.stack[vm.sp-n-1], vm.stack[vm.sp-n-2] = f.this, vm.stack[vm.sp-n-1]
+		vm.newTarget = f.newTarget
+		return
 	case *nativeFuncObject:
 		vm._nativeCall(f, n)
 	case *boundFuncObject:
@@ -2952,17 +2962,30 @@ func (e *enterFuncStashless) exec(vm *vm) {
 type newFunc struct {
 	prg    *Program
 	name   unistring.String
+	source string
+
 	length uint32
 	strict bool
-
-	srcStart, srcEnd uint32
 }
 
 func (n *newFunc) exec(vm *vm) {
 	obj := vm.r.newFunc(n.name, int(n.length), n.strict)
 	obj.prg = n.prg
 	obj.stash = vm.stash
-	obj.src = n.prg.src.Source()[n.srcStart:n.srcEnd]
+	obj.src = n.source
+	vm.push(obj.val)
+	vm.pc++
+}
+
+type newArrowFunc struct {
+	newFunc
+}
+
+func (n *newArrowFunc) exec(vm *vm) {
+	obj := vm.r.newArrowFunc(n.name, int(n.length), n.strict)
+	obj.prg = n.prg
+	obj.stash = vm.stash
+	obj.src = n.source
 	vm.push(obj.val)
 	vm.pc++
 }
@@ -3558,7 +3581,7 @@ func (_typeof) exec(vm *vm) {
 	case *Object:
 	repeat:
 		switch s := v.self.(type) {
-		case *funcObject, *nativeFuncObject, *boundFuncObject:
+		case *funcObject, *nativeFuncObject, *boundFuncObject, *arrowFuncObject:
 			r = stringFunction
 		case *lazyObject:
 			v.self = s.create(v)