Browse Source

Implemented classes (including private elements). Closes #349.

Dmitry Panov 3 years ago
parent
commit
0b5d210524
23 changed files with 3565 additions and 506 deletions
  1. 97 0
      ast/node.go
  2. 14 9
      builtin_function.go
  3. 8 3
      builtin_object.go
  4. 417 74
      compiler.go
  5. 985 94
      compiler_expr.go
  6. 29 18
      compiler_stmt.go
  7. 742 1
      compiler_test.go
  8. 4 0
      destruct.go
  9. 122 17
      func.go
  10. 54 0
      object.go
  11. 4 0
      object_dynamic.go
  12. 6 0
      object_lazy.go
  13. 122 29
      parser/expression.go
  14. 10 0
      parser/lexer.go
  15. 1 1
      parser/lexer_test.go
  16. 25 8
      parser/parser_test.go
  17. 137 1
      parser/statement.go
  18. 56 36
      runtime.go
  19. 147 134
      tc39_test.go
  20. 16 9
      token/token_const.go
  21. 1 0
      value.go
  22. 567 71
      vm.go
  23. 1 1
      vm_test.go

+ 97 - 0
ast/node.go

@@ -118,6 +118,11 @@ type (
 		Identifier Identifier
 	}
 
+	PrivateDotExpression struct {
+		Left       Expression
+		Identifier PrivateIdentifier
+	}
+
 	OptionalChain struct {
 		Expression
 	}
@@ -136,6 +141,15 @@ type (
 		DeclarationList []*VariableDeclaration
 	}
 
+	ClassLiteral struct {
+		Class      file.Idx
+		RightBrace file.Idx
+		Name       *Identifier
+		SuperClass Expression
+		Body       []ClassElement
+		Source     string
+	}
+
 	ConciseBody interface {
 		Node
 		_conciseBody()
@@ -158,6 +172,10 @@ type (
 		Idx  file.Idx
 	}
 
+	PrivateIdentifier struct {
+		Identifier
+	}
+
 	NewExpression struct {
 		New              file.Idx
 		Callee           Expression
@@ -254,6 +272,10 @@ type (
 		Idx file.Idx
 	}
 
+	SuperExpression struct {
+		Idx file.Idx
+	}
+
 	UnaryExpression struct {
 		Operator token.Token
 		Idx      file.Idx // If a prefix operation
@@ -278,7 +300,9 @@ func (*BracketExpression) _expressionNode()     {}
 func (*CallExpression) _expressionNode()        {}
 func (*ConditionalExpression) _expressionNode() {}
 func (*DotExpression) _expressionNode()         {}
+func (*PrivateDotExpression) _expressionNode()  {}
 func (*FunctionLiteral) _expressionNode()       {}
+func (*ClassLiteral) _expressionNode()          {}
 func (*ArrowFunctionLiteral) _expressionNode()  {}
 func (*Identifier) _expressionNode()            {}
 func (*NewExpression) _expressionNode()         {}
@@ -290,6 +314,7 @@ func (*SequenceExpression) _expressionNode()    {}
 func (*StringLiteral) _expressionNode()         {}
 func (*TemplateLiteral) _expressionNode()       {}
 func (*ThisExpression) _expressionNode()        {}
+func (*SuperExpression) _expressionNode()       {}
 func (*UnaryExpression) _expressionNode()       {}
 func (*MetaProperty) _expressionNode()          {}
 func (*ObjectPattern) _expressionNode()         {}
@@ -442,6 +467,10 @@ type (
 	FunctionDeclaration struct {
 		Function *FunctionLiteral
 	}
+
+	ClassDeclaration struct {
+		Class *ClassLiteral
+	}
 )
 
 // _statementNode
@@ -469,6 +498,7 @@ func (*WhileStatement) _statementNode()      {}
 func (*WithStatement) _statementNode()       {}
 func (*LexicalDeclaration) _statementNode()  {}
 func (*FunctionDeclaration) _statementNode() {}
+func (*ClassDeclaration) _statementNode()    {}
 
 // =========== //
 // Declaration //
@@ -479,6 +509,33 @@ type (
 		Var  file.Idx
 		List []*Binding
 	}
+
+	ClassElement interface {
+		Node
+		_classElement()
+	}
+
+	FieldDefinition struct {
+		Idx         file.Idx
+		Key         Expression
+		Initializer Expression
+		Static      bool
+	}
+
+	MethodDefinition struct {
+		Idx    file.Idx
+		Key    Expression
+		Kind   PropertyKind // "method", "get" or "set"
+		Body   *FunctionLiteral
+		Static bool
+	}
+
+	ClassStaticBlock struct {
+		Static          file.Idx
+		Block           *BlockStatement
+		Source          string
+		DeclarationList []*VariableDeclaration
+	}
 )
 
 type (
@@ -500,6 +557,7 @@ type (
 	}
 
 	ForInto interface {
+		Node
 		_forInto()
 	}
 
@@ -543,6 +601,10 @@ func (*Identifier) _bindingTarget() {}
 func (*BlockStatement) _conciseBody() {}
 func (*ExpressionBody) _conciseBody() {}
 
+func (*FieldDefinition) _classElement()  {}
+func (*MethodDefinition) _classElement() {}
+func (*ClassStaticBlock) _classElement() {}
+
 // ==== //
 // Node //
 // ==== //
@@ -570,7 +632,9 @@ func (self *BracketExpression) Idx0() file.Idx     { return self.Left.Idx0() }
 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 *PrivateDotExpression) Idx0() file.Idx  { return self.Left.Idx0() }
 func (self *FunctionLiteral) Idx0() file.Idx       { return self.Function }
+func (self *ClassLiteral) Idx0() file.Idx          { return self.Class }
 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 }
@@ -582,6 +646,7 @@ func (self *SequenceExpression) Idx0() file.Idx    { return self.Sequence[0].Idx
 func (self *StringLiteral) Idx0() file.Idx         { return self.Idx }
 func (self *TemplateLiteral) Idx0() file.Idx       { return self.OpenQuote }
 func (self *ThisExpression) Idx0() file.Idx        { return self.Idx }
+func (self *SuperExpression) Idx0() file.Idx       { return self.Idx }
 func (self *UnaryExpression) Idx0() file.Idx       { return self.Idx }
 func (self *MetaProperty) Idx0() file.Idx          { return self.Idx }
 
@@ -609,6 +674,7 @@ func (self *WhileStatement) Idx0() file.Idx      { return self.While }
 func (self *WithStatement) Idx0() file.Idx       { return self.With }
 func (self *LexicalDeclaration) Idx0() file.Idx  { return self.Idx }
 func (self *FunctionDeclaration) Idx0() file.Idx { return self.Function.Idx0() }
+func (self *ClassDeclaration) Idx0() file.Idx    { return self.Class.Idx0() }
 func (self *Binding) Idx0() file.Idx             { return self.Target.Idx0() }
 
 func (self *ForLoopInitializerVarDeclList) Idx0() file.Idx { return self.List[0].Idx0() }
@@ -616,6 +682,14 @@ func (self *PropertyShort) Idx0() file.Idx                 { return self.Name.Id
 func (self *PropertyKeyed) Idx0() file.Idx                 { return self.Key.Idx0() }
 func (self *ExpressionBody) Idx0() file.Idx                { return self.Expression.Idx0() }
 
+func (self *FieldDefinition) Idx0() file.Idx  { return self.Idx }
+func (self *MethodDefinition) Idx0() file.Idx { return self.Idx }
+func (self *ClassStaticBlock) Idx0() file.Idx { return self.Static }
+
+func (self *ForDeclaration) Idx0() file.Idx    { return self.Idx }
+func (self *ForIntoVar) Idx0() file.Idx        { return self.Binding.Idx0() }
+func (self *ForIntoExpression) Idx0() file.Idx { return self.Expression.Idx0() }
+
 // ==== //
 // Idx1 //
 // ==== //
@@ -630,7 +704,9 @@ func (self *BracketExpression) Idx1() file.Idx     { return self.RightBracket +
 func (self *CallExpression) Idx1() file.Idx        { return self.RightParenthesis + 1 }
 func (self *ConditionalExpression) Idx1() file.Idx { return self.Test.Idx1() }
 func (self *DotExpression) Idx1() file.Idx         { return self.Identifier.Idx1() }
+func (self *PrivateDotExpression) Idx1() file.Idx  { return self.Identifier.Idx1() }
 func (self *FunctionLiteral) Idx1() file.Idx       { return self.Body.Idx1() }
+func (self *ClassLiteral) Idx1() file.Idx          { return self.RightBrace + 1 }
 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 {
@@ -649,6 +725,7 @@ func (self *SequenceExpression) Idx1() file.Idx { return self.Sequence[len(self.
 func (self *StringLiteral) Idx1() file.Idx      { return file.Idx(int(self.Idx) + len(self.Literal)) }
 func (self *TemplateLiteral) Idx1() file.Idx    { return self.CloseQuote + 1 }
 func (self *ThisExpression) Idx1() file.Idx     { return self.Idx + 4 }
+func (self *SuperExpression) Idx1() file.Idx    { return self.Idx + 5 }
 func (self *UnaryExpression) Idx1() file.Idx {
 	if self.Postfix {
 		return self.Operand.Idx1() + 2 // ++ --
@@ -696,6 +773,7 @@ func (self *WhileStatement) Idx1() file.Idx      { return self.Body.Idx1() }
 func (self *WithStatement) Idx1() file.Idx       { return self.Body.Idx1() }
 func (self *LexicalDeclaration) Idx1() file.Idx  { return self.List[len(self.List)-1].Idx1() }
 func (self *FunctionDeclaration) Idx1() file.Idx { return self.Function.Idx1() }
+func (self *ClassDeclaration) Idx1() file.Idx    { return self.Class.Idx1() }
 func (self *Binding) Idx1() file.Idx {
 	if self.Initializer != nil {
 		return self.Initializer.Idx1()
@@ -715,3 +793,22 @@ 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() }
+
+func (self *FieldDefinition) Idx1() file.Idx {
+	if self.Initializer != nil {
+		return self.Initializer.Idx1()
+	}
+	return self.Key.Idx1()
+}
+
+func (self *MethodDefinition) Idx1() file.Idx {
+	return self.Body.Idx1()
+}
+
+func (self *ClassStaticBlock) Idx1() file.Idx {
+	return self.Block.Idx1()
+}
+
+func (self *ForDeclaration) Idx1() file.Idx    { return self.Target.Idx1() }
+func (self *ForIntoVar) Idx1() file.Idx        { return self.Binding.Idx1() }
+func (self *ForIntoExpression) Idx1() file.Idx { return self.Expression.Idx1() }

+ 14 - 9
builtin_function.go

@@ -23,7 +23,7 @@ func (r *Runtime) builtin_Function(args []Value, proto *Object) *Object {
 	}
 	sb.WriteString(asciiString("\n})"))
 
-	ret := r.toObject(r.eval(sb.String(), false, false, _undefined))
+	ret := r.toObject(r.eval(sb.String(), false, false))
 	ret.self.setProto(proto, true)
 	return ret
 }
@@ -32,10 +32,12 @@ func (r *Runtime) functionproto_toString(call FunctionCall) Value {
 	obj := r.toObject(call.This)
 repeat:
 	switch f := obj.self.(type) {
-	case *methodFuncObject:
-		return newStringValue(f.src)
 	case *funcObject:
 		return newStringValue(f.src)
+	case *classFuncObject:
+		return newStringValue(f.src)
+	case *methodFuncObject:
+		return newStringValue(f.src)
 	case *arrowFuncObject:
 		return newStringValue(f.src)
 	case *nativeFuncObject:
@@ -48,7 +50,7 @@ repeat:
 	case *proxyObject:
 	repeat2:
 		switch c := f.target.self.(type) {
-		case *methodFuncObject, *funcObject, *arrowFuncObject, *nativeFuncObject, *boundFuncObject:
+		case *classFuncObject, *methodFuncObject, *funcObject, *arrowFuncObject, *nativeFuncObject, *boundFuncObject:
 			return asciiString("function () { [native code] }")
 		case *lazyObject:
 			f.target.self = c.create(obj)
@@ -126,7 +128,7 @@ func (r *Runtime) boundCallable(target func(FunctionCall) Value, boundArgs []Val
 	}
 }
 
-func (r *Runtime) boundConstruct(target func([]Value, *Object) *Object, boundArgs []Value) func([]Value, *Object) *Object {
+func (r *Runtime) boundConstruct(f *Object, target func([]Value, *Object) *Object, boundArgs []Value) func([]Value, *Object) *Object {
 	if target == nil {
 		return nil
 	}
@@ -137,7 +139,9 @@ func (r *Runtime) boundConstruct(target func([]Value, *Object) *Object, boundArg
 	}
 	return func(fargs []Value, newTarget *Object) *Object {
 		a := append(args, fargs...)
-		copy(a, args)
+		if newTarget == f {
+			newTarget = nil
+		}
 		return target(a, newTarget)
 	}
 }
@@ -185,12 +189,13 @@ lenNotInt:
 	}
 
 	v := &Object{runtime: r}
-
-	ff := r.newNativeFuncObj(v, r.boundCallable(fcall, call.Arguments), r.boundConstruct(construct, call.Arguments), nameStr.string(), nil, l)
-	v.self = &boundFuncObject{
+	ff := r.newNativeFuncAndConstruct(v, r.boundCallable(fcall, call.Arguments), r.boundConstruct(v, construct, call.Arguments), nil, nameStr.string(), l)
+	bf := &boundFuncObject{
 		nativeFuncObject: *ff,
 		wrapped:          obj,
 	}
+	bf.prototype = obj.self.proto()
+	v.self = bf
 
 	return v
 }

+ 8 - 3
builtin_object.go

@@ -4,14 +4,18 @@ import (
 	"fmt"
 )
 
-func (r *Runtime) builtin_Object(args []Value, proto *Object) *Object {
+func (r *Runtime) builtin_Object(args []Value, newTarget *Object) *Object {
+	if newTarget != nil && newTarget != r.global.Object {
+		proto := r.getPrototypeFromCtor(newTarget, nil, r.global.ObjectPrototype)
+		return r.newBaseObject(proto, classObject).val
+	}
 	if len(args) > 0 {
 		arg := args[0]
 		if arg != _undefined && arg != _null {
 			return arg.ToObject(r)
 		}
 	}
-	return r.newBaseObject(proto, classObject).val
+	return r.NewObject()
 }
 
 func (r *Runtime) object_getPrototypeOf(call FunctionCall) Value {
@@ -566,7 +570,8 @@ func (r *Runtime) initObject() {
 		Configurable: FLAG_TRUE,
 	}, true)
 
-	r.global.Object = r.newNativeFuncConstruct(r.builtin_Object, classObject, r.global.ObjectPrototype, 1)
+	r.global.Object = r.newNativeConstructOnly(nil, r.builtin_Object, r.global.ObjectPrototype, "Object", 1).val
+	r.global.ObjectPrototype.self._putProp("constructor", r.global.Object, true, false, true)
 	o = r.global.Object.self
 	o._putProp("assign", r.newNativeFunc(r.object_assign, nil, "assign", nil, 2), true, false, true)
 	o._putProp("defineProperty", r.newNativeFunc(r.object_defineProperty, nil, "defineProperty", nil, 3), true, false, true)

+ 417 - 74
compiler.go

@@ -42,6 +42,8 @@ const (
 	varTypeConst
 )
 
+const thisBindingName = " this" // must not be a valid identifier
+
 type CompilerError struct {
 	Message string
 	File    *file.File
@@ -75,9 +77,12 @@ type compiler struct {
 	scope *scope
 	block *block
 
+	classScope *classScope
+
 	enumGetExpr compiledEnumGetExpr
 
-	evalVM *vm
+	evalVM *vm // VM used to evaluate constant expressions
+	ctxVM  *vm // VM in which an eval() code is compiled
 }
 
 type binding struct {
@@ -124,18 +129,18 @@ func (b *binding) markAccessPoint() {
 func (b *binding) emitGet() {
 	b.markAccessPoint()
 	if b.isVar && !b.isArg {
-		b.scope.c.emit(loadStash(0))
+		b.scope.c.emit(loadStack(0))
 	} else {
-		b.scope.c.emit(loadStashLex(0))
+		b.scope.c.emit(loadStackLex(0))
 	}
 }
 
 func (b *binding) emitGetAt(pos int) {
 	b.markAccessPointAt(pos)
 	if b.isVar && !b.isArg {
-		b.scope.c.p.code[pos] = loadStash(0)
+		b.scope.c.p.code[pos] = loadStack(0)
 	} else {
-		b.scope.c.p.code[pos] = loadStashLex(0)
+		b.scope.c.p.code[pos] = loadStackLex(0)
 	}
 }
 
@@ -145,7 +150,7 @@ func (b *binding) emitGetP() {
 	} else {
 		// make sure TDZ is checked
 		b.markAccessPoint()
-		b.scope.c.emit(loadStashLex(0), pop)
+		b.scope.c.emit(loadStackLex(0), pop)
 	}
 }
 
@@ -158,9 +163,9 @@ func (b *binding) emitSet() {
 	}
 	b.markAccessPoint()
 	if b.isVar && !b.isArg {
-		b.scope.c.emit(storeStash(0))
+		b.scope.c.emit(storeStack(0))
 	} else {
-		b.scope.c.emit(storeStashLex(0))
+		b.scope.c.emit(storeStackLex(0))
 	}
 }
 
@@ -173,15 +178,55 @@ func (b *binding) emitSetP() {
 	}
 	b.markAccessPoint()
 	if b.isVar && !b.isArg {
-		b.scope.c.emit(storeStashP(0))
+		b.scope.c.emit(storeStackP(0))
+	} else {
+		b.scope.c.emit(storeStackLexP(0))
+	}
+}
+
+func (b *binding) emitInitP() {
+	if !b.isVar && b.scope.outer == nil {
+		b.scope.c.emit(initGlobalP(b.name))
 	} else {
-		b.scope.c.emit(storeStashLexP(0))
+		b.markAccessPoint()
+		b.scope.c.emit(initStackP(0))
 	}
 }
 
 func (b *binding) emitInit() {
-	b.markAccessPoint()
-	b.scope.c.emit(initStash(0))
+	if !b.isVar && b.scope.outer == nil {
+		b.scope.c.emit(initGlobal(b.name))
+	} else {
+		b.markAccessPoint()
+		b.scope.c.emit(initStack(0))
+	}
+}
+
+func (b *binding) emitInitAt(pos int) {
+	if !b.isVar && b.scope.outer == nil {
+		b.scope.c.p.code[pos] = initGlobal(b.name)
+	} else {
+		b.markAccessPointAt(pos)
+		b.scope.c.p.code[pos] = initStack(0)
+	}
+}
+
+func (b *binding) emitInitAtScope(scope *scope, pos int) {
+	if !b.isVar && scope.outer == nil {
+		scope.c.p.code[pos] = initGlobal(b.name)
+	} else {
+		b.markAccessPointAtScope(scope, pos)
+		scope.c.p.code[pos] = initStack(0)
+	}
+}
+
+func (b *binding) emitInitPAtScope(scope *scope, pos int) {
+	if !b.isVar && scope.outer == nil {
+		scope.c.p.code[pos] = initGlobalP(b.name)
+	} else {
+		b.markAccessPointAtScope(scope, pos)
+		scope.c.p.code[pos] = initStackP(0)
+	}
 }
 
 func (b *binding) emitGetVar(callee bool) {
@@ -238,6 +283,9 @@ type scope struct {
 	base       int
 	numArgs    int
 
+	// function type. If not funcNone, this is a function or a top-level lexical environment
+	funcType funcType
+
 	// in strict mode
 	strict bool
 	// eval top-level scope
@@ -247,10 +295,6 @@ type scope struct {
 	// at least one binding has been marked for placement in stash
 	needStash bool
 
-	// 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
@@ -259,8 +303,6 @@ type scope struct {
 	argsInStash bool
 	// need 'arguments' object (functions only)
 	argsNeeded bool
-	// 'this' is used and non-strict, so need to box it (functions only)
-	thisNeeded bool
 }
 
 type block struct {
@@ -364,12 +406,38 @@ func (p *Program) dumpCode(logger func(format string, args ...interface{})) {
 
 func (p *Program) _dumpCode(indent string, logger func(format string, args ...interface{})) {
 	logger("values: %+v", p.values)
+	dumpInitFields := func(initFields *Program) {
+		i := indent + ">"
+		logger("%s ---- init_fields:", i)
+		initFields._dumpCode(i, logger)
+		logger("%s ----", i)
+	}
 	for pc, ins := range p.code {
 		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)
+		var prg *Program
+		switch f := ins.(type) {
+		case *newFunc:
+			prg = f.prg
+		case *newArrowFunc:
+			prg = f.prg
+		case *newMethod:
+			prg = f.prg
+		case *newDerivedClass:
+			if f.initFields != nil {
+				dumpInitFields(f.initFields)
+			}
+			prg = f.ctor
+		case *newClass:
+			if f.initFields != nil {
+				dumpInitFields(f.initFields)
+			}
+		case *newStaticFieldInit:
+			if f.initFields != nil {
+				dumpInitFields(f.initFields)
+			}
+		}
+		if prg != nil {
+			prg._dumpCode(indent+">", logger)
 		}
 	}
 }
@@ -411,15 +479,39 @@ func (s *scope) lookupName(name unistring.String) (binding *binding, noDynamics
 		if curScope.dynamic {
 			noDynamics = false
 		}
-		if name == "arguments" && curScope.function && !curScope.arrow {
+		if name == "arguments" && curScope.funcType != funcNone && curScope.funcType != funcArrow {
+			if curScope.funcType == funcClsInit {
+				s.c.throwSyntaxError(0, "'arguments' is not allowed in class field initializer or static initialization block")
+			}
 			curScope.argsNeeded = true
 			binding, _ = curScope.bindName(name)
 			return
 		}
-		if curScope.function {
+		if curScope.isFunction() {
+			toStash = true
+		}
+	}
+}
+
+func (s *scope) lookupThis() (*binding, bool) {
+	toStash := false
+	for curScope := s; curScope != nil; curScope = curScope.outer {
+		if curScope.outer == nil {
+			if curScope.eval {
+				return nil, true
+			}
+		}
+		if b, exists := curScope.boundNames[thisBindingName]; exists {
+			if toStash && !b.inStash {
+				b.moveToStash()
+			}
+			return b, false
+		}
+		if curScope.isFunction() {
 			toStash = true
 		}
 	}
+	return nil, false
 }
 
 func (s *scope) ensureBoundNamesCreated() {
@@ -453,8 +545,14 @@ func (s *scope) bindNameLexical(name unistring.String, unique bool, offset int)
 	return b, true
 }
 
+func (s *scope) createThisBinding() *binding {
+	thisBinding, _ := s.bindNameLexical(thisBindingName, false, 0)
+	thisBinding.isVar = true // don't check on load
+	return thisBinding
+}
+
 func (s *scope) bindName(name unistring.String) (*binding, bool) {
-	if !s.function && !s.variable && s.outer != nil {
+	if !s.isFunction() && !s.variable && s.outer != nil {
 		return s.outer.bindName(name)
 	}
 	b, created := s.bindNameLexical(name, false, 0)
@@ -465,7 +563,7 @@ func (s *scope) bindName(name unistring.String) (*binding, bool) {
 }
 
 func (s *scope) bindNameShadow(name unistring.String) (*binding, bool) {
-	if !s.function && s.outer != nil {
+	if !s.isFunction() && s.outer != nil {
 		return s.outer.bindNameShadow(name)
 	}
 
@@ -482,7 +580,16 @@ func (s *scope) bindNameShadow(name unistring.String) (*binding, bool) {
 
 func (s *scope) nearestFunction() *scope {
 	for sc := s; sc != nil; sc = sc.outer {
-		if sc.function {
+		if sc.isFunction() {
+			return sc
+		}
+	}
+	return nil
+}
+
+func (s *scope) nearestThis() *scope {
+	for sc := s; sc != nil; sc = sc.outer {
+		if sc.eval || sc.isFunction() && sc.funcType != funcArrow {
 			return sc
 		}
 	}
@@ -496,7 +603,15 @@ func (s *scope) finaliseVarAlloc(stackOffset int) (stashSize, stackSize int) {
 	}
 	stackIdx, stashIdx := 0, 0
 	allInStash := s.isDynamic()
+	var derivedCtor bool
+	if fs := s.nearestThis(); fs != nil && fs.funcType == funcDerivedCtor {
+		derivedCtor = true
+	}
 	for i, b := range s.bindings {
+		var this bool
+		if b.name == thisBindingName {
+			this = true
+		}
 		if allInStash || b.inStash {
 			for scope, aps := range b.accessPoints {
 				var level uint32
@@ -511,40 +626,78 @@ func (s *scope) finaliseVarAlloc(stackOffset int) (stashSize, stackSize int) {
 				idx := (level << 24) | uint32(stashIdx)
 				base := scope.base
 				code := scope.prg.code
-				for _, pc := range *aps {
-					ap := &code[base+pc]
-					switch i := (*ap).(type) {
-					case loadStash:
-						*ap = loadStash(idx)
-					case storeStash:
-						*ap = storeStash(idx)
-					case storeStashP:
-						*ap = storeStashP(idx)
-					case loadStashLex:
-						*ap = loadStashLex(idx)
-					case storeStashLex:
-						*ap = storeStashLex(idx)
-					case storeStashLexP:
-						*ap = storeStashLexP(idx)
-					case initStash:
-						*ap = initStash(idx)
-					case *loadMixed:
-						i.idx = idx
-					case *loadMixedLex:
-						i.idx = idx
-					case *resolveMixed:
-						i.idx = idx
+				if this {
+					if derivedCtor {
+						for _, pc := range *aps {
+							ap := &code[base+pc]
+							switch (*ap).(type) {
+							case loadStack:
+								*ap = loadThisStash(idx)
+							case initStack:
+								*ap = initStash(idx)
+							case resolveThisStack:
+								*ap = resolveThisStash(idx)
+							case _ret:
+								*ap = cret(idx)
+							default:
+								s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for 'this'")
+							}
+						}
+					} else {
+						for _, pc := range *aps {
+							ap := &code[base+pc]
+							switch (*ap).(type) {
+							case loadStack:
+								*ap = loadStash(idx)
+							case initStack:
+								*ap = initStash(idx)
+							default:
+								s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for 'this'")
+							}
+						}
+					}
+				} else {
+					for _, pc := range *aps {
+						ap := &code[base+pc]
+						switch i := (*ap).(type) {
+						case loadStack:
+							*ap = loadStash(idx)
+						case storeStack:
+							*ap = storeStash(idx)
+						case storeStackP:
+							*ap = storeStashP(idx)
+						case loadStackLex:
+							*ap = loadStashLex(idx)
+						case storeStackLex:
+							*ap = storeStashLex(idx)
+						case storeStackLexP:
+							*ap = storeStashLexP(idx)
+						case initStackP:
+							*ap = initStashP(idx)
+						case initStack:
+							*ap = initStash(idx)
+						case *loadMixed:
+							i.idx = idx
+						case *loadMixedLex:
+							i.idx = idx
+						case *resolveMixed:
+							i.idx = idx
+						default:
+							s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for binding: %T", i)
+						}
 					}
 				}
 			}
 			stashIdx++
 		} else {
 			var idx int
-			if i < s.numArgs {
-				idx = -(i + 1)
-			} else {
-				stackIdx++
-				idx = stackIdx + stackOffset
+			if !this {
+				if i < s.numArgs {
+					idx = -(i + 1)
+				} else {
+					stackIdx++
+					idx = stackIdx + stackOffset
+				}
 			}
 			for scope, aps := range b.accessPoints {
 				var level int
@@ -558,23 +711,45 @@ func (s *scope) finaliseVarAlloc(stackOffset int) (stashSize, stackSize int) {
 				}
 				code := scope.prg.code
 				base := scope.base
-				if argsInStash {
+				if this {
+					if derivedCtor {
+						for _, pc := range *aps {
+							ap := &code[base+pc]
+							switch (*ap).(type) {
+							case loadStack:
+								*ap = loadThisStack{}
+							case initStack:
+								// no-op
+							case resolveThisStack:
+								// no-op
+							case _ret:
+								// no-op, already in the right place
+							default:
+								s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for 'this'")
+							}
+						}
+					} /*else {
+						no-op
+					}*/
+				} else if argsInStash {
 					for _, pc := range *aps {
 						ap := &code[base+pc]
 						switch i := (*ap).(type) {
-						case loadStash:
+						case loadStack:
 							*ap = loadStack1(idx)
-						case storeStash:
+						case storeStack:
 							*ap = storeStack1(idx)
-						case storeStashP:
+						case storeStackP:
 							*ap = storeStack1P(idx)
-						case loadStashLex:
+						case loadStackLex:
 							*ap = loadStack1Lex(idx)
-						case storeStashLex:
+						case storeStackLex:
 							*ap = storeStack1Lex(idx)
-						case storeStashLexP:
+						case storeStackLexP:
 							*ap = storeStack1LexP(idx)
-						case initStash:
+						case initStackP:
+							*ap = initStack1P(idx)
+						case initStack:
 							*ap = initStack1(idx)
 						case *loadMixed:
 							*ap = &loadMixedStack1{name: i.name, idx: idx, level: uint8(level), callee: i.callee}
@@ -582,32 +757,38 @@ func (s *scope) finaliseVarAlloc(stackOffset int) (stashSize, stackSize int) {
 							*ap = &loadMixedStack1Lex{name: i.name, idx: idx, level: uint8(level), callee: i.callee}
 						case *resolveMixed:
 							*ap = &resolveMixedStack1{typ: i.typ, name: i.name, idx: idx, level: uint8(level), strict: i.strict}
+						default:
+							s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for binding: %T", i)
 						}
 					}
 				} else {
 					for _, pc := range *aps {
 						ap := &code[base+pc]
 						switch i := (*ap).(type) {
-						case loadStash:
+						case loadStack:
 							*ap = loadStack(idx)
-						case storeStash:
+						case storeStack:
 							*ap = storeStack(idx)
-						case storeStashP:
+						case storeStackP:
 							*ap = storeStackP(idx)
-						case loadStashLex:
+						case loadStackLex:
 							*ap = loadStackLex(idx)
-						case storeStashLex:
+						case storeStackLex:
 							*ap = storeStackLex(idx)
-						case storeStashLexP:
+						case storeStackLexP:
 							*ap = storeStackLexP(idx)
-						case initStash:
+						case initStack:
 							*ap = initStack(idx)
+						case initStackP:
+							*ap = initStackP(idx)
 						case *loadMixed:
 							*ap = &loadMixedStack{name: i.name, idx: idx, level: uint8(level), callee: i.callee}
 						case *loadMixedLex:
 							*ap = &loadMixedStackLex{name: i.name, idx: idx, level: uint8(level), callee: i.callee}
 						case *resolveMixed:
 							*ap = &resolveMixedStack{typ: i.typ, name: i.name, idx: idx, level: uint8(level), strict: i.strict}
+						default:
+							s.c.assert(false, s.c.p.sourceOffset(pc), "Unsupported instruction for binding: %T", i)
 						}
 					}
 				}
@@ -631,6 +812,15 @@ func (s *scope) moveArgsToStash() {
 	s.needStash = true
 }
 
+func (s *scope) trimCode(delta int) {
+	s.c.p.code = s.c.p.code[delta:]
+	srcMap := s.c.p.srcMap
+	for i := range srcMap {
+		srcMap[i].pc -= delta
+	}
+	s.adjustBase(-delta)
+}
+
 func (s *scope) adjustBase(delta int) {
 	s.base += delta
 	for _, nested := range s.nested {
@@ -664,6 +854,10 @@ func (s *scope) isDynamic() bool {
 	return s.dynLookup || s.dynamic
 }
 
+func (s *scope) isFunction() bool {
+	return s.funcType != funcNone && !s.eval
+}
+
 func (s *scope) deleteBinding(b *binding) {
 	idx := 0
 	for i, bb := range s.bindings {
@@ -681,7 +875,10 @@ found:
 	s.bindings = s.bindings[:l]
 }
 
-func (c *compiler) compile(in *ast.Program, strict, eval, inGlobal bool) {
+func (c *compiler) compile(in *ast.Program, strict, inGlobal bool, evalVm *vm) {
+	c.ctxVM = evalVm
+
+	eval := evalVm != nil
 	c.p.src = in.File
 	c.newScope()
 	scope := c.scope
@@ -696,7 +893,15 @@ func (c *compiler) compile(in *ast.Program, strict, eval, inGlobal bool) {
 	if ownVarScope {
 		c.newBlockScope()
 		scope = c.scope
-		scope.function = true
+		scope.variable = true
+	}
+	if eval && !inGlobal {
+		for s := evalVm.stash; s != nil; s = s.outer {
+			if ft := s.funcType; ft != funcNone && ft != funcArrow {
+				scope.funcType = ft
+				break
+			}
+		}
 	}
 	funcs := c.extractFunctions(in.Body)
 	c.createFunctionBindings(funcs)
@@ -803,7 +1008,7 @@ func (c *compiler) extractFunctions(list []ast.Statement) (funcs []*ast.Function
 func (c *compiler) createFunctionBindings(funcs []*ast.FunctionDeclaration) {
 	s := c.scope
 	if s.outer != nil {
-		unique := !s.function && !s.variable && s.strict
+		unique := !s.isFunction() && !s.variable && s.strict
 		for _, decl := range funcs {
 			s.bindNameLexical(decl.Function.Name.Name, unique, int(decl.Function.Name.Idx1())-1)
 		}
@@ -962,6 +1167,12 @@ func (c *compiler) compileLexicalDeclarations(list []ast.Statement, scopeDeclare
 				scopeDeclared = true
 			}
 			c.createLexicalBindings(lex)
+		} else if cls, ok := st.(*ast.ClassDeclaration); ok {
+			if !scopeDeclared {
+				c.newBlockScope()
+				scopeDeclared = true
+			}
+			c.createLexicalIdBinding(cls.Class.Name.Name, false, int(cls.Class.Name.Idx)-1)
 		}
 	}
 	return scopeDeclared
@@ -991,7 +1202,7 @@ func (c *compiler) compileFunction(v *ast.FunctionDeclaration) {
 		e.emitSetter(c.compileFunctionLiteral(v.Function, false), false)
 	} else {
 		c.compileFunctionLiteral(v.Function, false).emitGetter(true)
-		b.emitInit()
+		b.emitInitP()
 	}
 }
 
@@ -1083,3 +1294,135 @@ func (c *compiler) compileStatementDummy(statement ast.Statement) {
 	c.compileStatement(statement, false)
 	leave()
 }
+
+func (c *compiler) assert(cond bool, offset int, msg string, args ...interface{}) {
+	if !cond {
+		c.throwSyntaxError(offset, "Compiler bug: "+msg, args...)
+	}
+}
+
+func privateIdString(desc unistring.String) unistring.String {
+	return asciiString("#").concat(stringValueFromRaw(desc)).string()
+}
+
+type privateName struct {
+	idx                  int
+	isStatic             bool
+	isMethod             bool
+	hasGetter, hasSetter bool
+}
+
+type resolvedPrivateName struct {
+	name     unistring.String
+	idx      uint32
+	level    uint8
+	isStatic bool
+	isMethod bool
+}
+
+func (r *resolvedPrivateName) string() unistring.String {
+	return privateIdString(r.name)
+}
+
+type privateEnvRegistry struct {
+	fields, methods []unistring.String
+}
+
+type classScope struct {
+	c            *compiler
+	privateNames map[unistring.String]*privateName
+
+	instanceEnv, staticEnv privateEnvRegistry
+
+	outer *classScope
+}
+
+func (r *privateEnvRegistry) createPrivateMethodId(name unistring.String) int {
+	r.methods = append(r.methods, name)
+	return len(r.methods) - 1
+}
+
+func (r *privateEnvRegistry) createPrivateFieldId(name unistring.String) int {
+	r.fields = append(r.fields, name)
+	return len(r.fields) - 1
+}
+
+func (s *classScope) declarePrivateId(name unistring.String, kind ast.PropertyKind, isStatic bool, offset int) {
+	pn := s.privateNames[name]
+	if pn != nil {
+		if pn.isStatic == isStatic {
+			switch kind {
+			case ast.PropertyKindGet:
+				if pn.hasSetter && !pn.hasGetter {
+					pn.hasGetter = true
+					return
+				}
+			case ast.PropertyKindSet:
+				if pn.hasGetter && !pn.hasSetter {
+					pn.hasSetter = true
+					return
+				}
+			}
+		}
+		s.c.throwSyntaxError(offset, "Identifier '#%s' has already been declared", name)
+		panic("unreachable")
+	}
+	var env *privateEnvRegistry
+	if isStatic {
+		env = &s.staticEnv
+	} else {
+		env = &s.instanceEnv
+	}
+
+	pn = &privateName{
+		isStatic:  isStatic,
+		hasGetter: kind == ast.PropertyKindGet,
+		hasSetter: kind == ast.PropertyKindSet,
+	}
+	if kind != ast.PropertyKindValue {
+		pn.idx = env.createPrivateMethodId(name)
+		pn.isMethod = true
+	} else {
+		pn.idx = env.createPrivateFieldId(name)
+	}
+
+	if s.privateNames == nil {
+		s.privateNames = make(map[unistring.String]*privateName)
+	}
+	s.privateNames[name] = pn
+}
+
+func (s *classScope) getDeclaredPrivateId(name unistring.String) *privateName {
+	if n := s.privateNames[name]; n != nil {
+		return n
+	}
+	s.c.assert(false, 0, "getDeclaredPrivateId() for undeclared id")
+	panic("unreachable")
+}
+
+func (c *compiler) resolvePrivateName(name unistring.String, offset int) (*resolvedPrivateName, *privateId) {
+	level := 0
+	for s := c.classScope; s != nil; s = s.outer {
+		if len(s.privateNames) > 0 {
+			if pn := s.privateNames[name]; pn != nil {
+				return &resolvedPrivateName{
+					name:     name,
+					idx:      uint32(pn.idx),
+					level:    uint8(level),
+					isStatic: pn.isStatic,
+					isMethod: pn.isMethod,
+				}, nil
+			}
+			level++
+		}
+	}
+	if c.ctxVM != nil {
+		for s := c.ctxVM.privEnv; s != nil; s = s.outer {
+			if id := s.names[name]; id != nil {
+				return nil, id
+			}
+		}
+	}
+	c.throwSyntaxError(offset, "Private field '#%s' must be declared in an enclosing class", name)
+	panic("unreachable")
+}

File diff suppressed because it is too large
+ 985 - 94
compiler_expr.go


+ 29 - 18
compiler_stmt.go

@@ -1,7 +1,6 @@
 package goja
 
 import (
-	"fmt"
 	"github.com/dop251/goja/ast"
 	"github.com/dop251/goja/file"
 	"github.com/dop251/goja/token"
@@ -48,11 +47,14 @@ func (c *compiler) compileStatement(v ast.Statement, needResult bool) {
 	case *ast.FunctionDeclaration:
 		c.compileStandaloneFunctionDecl(v)
 		// note functions inside blocks are hoisted to the top of the block and are compiled using compileFunctions()
+	case *ast.ClassDeclaration:
+		c.compileClassDeclaration(v)
 	case *ast.WithStatement:
 		c.compileWithStatement(v, needResult)
 	case *ast.DebuggerStatement:
 	default:
-		panic(fmt.Errorf("Unknown statement type: %T", v))
+		c.assert(false, int(v.Idx0())-1, "Unknown statement type: %T", v)
+		panic("unreachable")
 	}
 }
 
@@ -272,7 +274,8 @@ func (c *compiler) compileLabeledForStatement(v *ast.ForStatement, needResult bo
 	case *ast.ForLoopInitializerExpression:
 		c.compileExpression(init.Expression).emitGetter(false)
 	default:
-		panic(fmt.Sprintf("Unsupported for loop initializer: %T", init))
+		c.assert(false, int(v.For)-1, "Unsupported for loop initializer: %T", init)
+		panic("unreachable")
 	}
 
 	if needResult {
@@ -386,7 +389,7 @@ func (c *compiler) compileForInto(into ast.ForInto, needResult bool) (enter *ent
 		case *ast.Identifier:
 			b := c.createLexicalIdBinding(target.Name, into.IsConst, int(into.Idx)-1)
 			c.emit(enumGet)
-			b.emitInit()
+			b.emitInitP()
 		case ast.Pattern:
 			c.createLexicalBinding(target, into.IsConst)
 			c.emit(enumGet)
@@ -394,10 +397,11 @@ func (c *compiler) compileForInto(into ast.ForInto, needResult bool) (enter *ent
 				c.emitPatternLexicalAssign(target, init)
 			}, false)
 		default:
-			c.throwSyntaxError(int(into.Idx)-1, "Unsupported ForBinding: %T", into.Target)
+			c.assert(false, int(into.Idx)-1, "Unsupported ForBinding: %T", into.Target)
 		}
 	default:
-		panic(fmt.Sprintf("Unsupported for-into: %T", into))
+		c.assert(false, int(into.Idx0())-1, "Unsupported for-into: %T", into)
+		panic("unreachable")
 	}
 
 	return
@@ -553,7 +557,8 @@ func (c *compiler) compileBranchStatement(v *ast.BranchStatement) {
 	case token.CONTINUE:
 		c.compileContinue(v.Label, v.Idx)
 	default:
-		panic(fmt.Errorf("Unknown branch statement token: %s", v.Token.String()))
+		c.assert(false, int(v.Idx0())-1, "Unknown branch statement token: %s", v.Token.String())
+		panic("unreachable")
 	}
 }
 
@@ -723,6 +728,9 @@ func (c *compiler) compileIfStatement(v *ast.IfStatement, needResult bool) {
 }
 
 func (c *compiler) compileReturnStatement(v *ast.ReturnStatement) {
+	if s := c.scope.nearestFunction(); s != nil && s.funcType == funcClsInit {
+		c.throwSyntaxError(int(v.Return)-1, "Illegal return statement")
+	}
 	if v.Argument != nil {
 		c.emitExpr(c.compileExpression(v.Argument), true)
 	} else {
@@ -736,6 +744,11 @@ func (c *compiler) compileReturnStatement(v *ast.ReturnStatement) {
 			c.emit(enumPopClose)
 		}
 	}
+	if s := c.scope.nearestFunction(); s != nil && s.funcType == funcDerivedCtor {
+		b := s.boundNames[thisBindingName]
+		c.assert(b != nil, int(v.Return)-1, "Derived constructor, but no 'this' binding")
+		b.markAccessPoint()
+	}
 	c.emit(ret)
 }
 
@@ -744,7 +757,7 @@ func (c *compiler) checkVarConflict(name unistring.String, offset int) {
 		if b, exists := sc.boundNames[name]; exists && !b.isVar && !(b.isArg && sc != c.scope) {
 			c.throwSyntaxError(offset, "Identifier '%s' has already been declared", name)
 		}
-		if sc.function {
+		if sc.isFunction() {
 			break
 		}
 	}
@@ -757,7 +770,7 @@ func (c *compiler) emitVarAssign(name unistring.String, offset int, init compile
 		if noDyn {
 			c.emitNamedOrConst(init, name)
 			c.p.addSrcMap(offset)
-			b.emitInit()
+			b.emitInitP()
 		} else {
 			c.emitVarRef(name, offset, b)
 			c.emitNamedOrConst(init, name)
@@ -781,9 +794,7 @@ func (c *compiler) compileVarBinding(expr *ast.Binding) {
 
 func (c *compiler) emitLexicalAssign(name unistring.String, offset int, init compiledExpr) {
 	b := c.scope.boundNames[name]
-	if b == nil {
-		panic("Lexical declaration for an unbound name")
-	}
+	c.assert(b != nil, offset, "Lexical declaration for an unbound name")
 	if init != nil {
 		c.emitNamedOrConst(init, name)
 		c.p.addSrcMap(offset)
@@ -793,11 +804,7 @@ func (c *compiler) emitLexicalAssign(name unistring.String, offset int, init com
 		}
 		c.emit(loadUndef)
 	}
-	if c.scope.outer != nil {
-		b.emitInit()
-	} else {
-		c.emit(initGlobal(name))
-	}
+	b.emitInitP()
 }
 
 func (c *compiler) emitPatternVarAssign(target, init compiledExpr) {
@@ -857,7 +864,7 @@ func (c *compiler) compileLexicalDeclaration(v *ast.LexicalDeclaration) {
 func (c *compiler) isEmptyResult(st ast.Statement) bool {
 	switch st := st.(type) {
 	case *ast.EmptyStatement, *ast.VariableStatement, *ast.LexicalDeclaration, *ast.FunctionDeclaration,
-		*ast.BranchStatement, *ast.DebuggerStatement:
+		*ast.ClassDeclaration, *ast.BranchStatement, *ast.DebuggerStatement:
 		return true
 	case *ast.LabelledStatement:
 		return c.isEmptyResult(st.Statement)
@@ -1114,3 +1121,7 @@ func (c *compiler) compileSwitchStatement(v *ast.SwitchStatement, needResult boo
 	}
 	c.leaveBlock()
 }
+
+func (c *compiler) compileClassDeclaration(v *ast.ClassDeclaration) {
+	c.emitLexicalAssign(v.Class.Name.Name, int(v.Class.Class)-1, c.compileClassLiteral(v.Class, false))
+}

+ 742 - 1
compiler_test.go

@@ -2062,7 +2062,7 @@ func TestObjectLiteral(t *testing.T) {
 	var getterCalled = false;
 	var setterCalled = false;
 
-	var o = {get x() {getterCalled = true}, set x() {setterCalled = true}};
+	var o = {get x() {getterCalled = true}, set x(_) {setterCalled = true}};
 
 	o.x;
 	o.x = 42;
@@ -4729,6 +4729,747 @@ func TestOptChainCallee(t *testing.T) {
 	testScriptWithTestLib(SCRIPT, _undefined, t)
 }
 
+func TestObjectLiteralSuper(t *testing.T) {
+	const SCRIPT = `
+	const proto = {
+		m() {
+			return 40;
+		}
+	}
+	const o = {
+		m() {
+			return super.m() + 2;
+		}
+	}
+	o.__proto__ = proto;
+	o.m();
+	`
+	testScript(SCRIPT, intToValue(42), t)
+}
+
+func TestClassCaptureThisInFieldInit(t *testing.T) {
+	const SCRIPT = `
+	let capture;
+
+	class C {
+		a = () => this
+	}
+
+	let c = new C();
+	c.a() === c;
+	`
+	testScript(SCRIPT, valueTrue, t)
+}
+
+func TestClassUseThisInFieldInit(t *testing.T) {
+	const SCRIPT = `
+	let capture;
+
+	class C {
+		a = this
+	}
+
+	let c = new C();
+	c.a === c;
+	`
+	testScript(SCRIPT, valueTrue, t)
+}
+
+func TestClassCaptureThisInStaticFieldInit(t *testing.T) {
+	const SCRIPT = `
+	let capture;
+
+	class C {
+		static a = (capture = () => this, 0)
+	}
+
+	let c = new C();
+	capture() === C;
+	`
+	testScript(SCRIPT, valueTrue, t)
+}
+
+func TestClassDynCaptureThisInStaticFieldInit(t *testing.T) {
+	const SCRIPT = `
+	class C {
+		static a = eval("this")
+	}
+
+	C.a === C;
+	`
+	testScript(SCRIPT, valueTrue, t)
+}
+
+func TestCompileClass(t *testing.T) {
+	const SCRIPT = `
+	class C extends Error {
+		a = true;
+		b = 1;
+		["b".toUpperCase()] = 2
+		static A = Math.random() < 1
+		constructor(message = "My Error") {
+			super(message);
+		}
+		static M() {
+		}
+		static M1() {
+		}
+		m() {
+			//return C.a;
+		}
+		m1() {
+			return true;
+		}
+		static {
+			this.supername = super.name;
+		}
+	}
+	let c = new C();
+	c.a === true && c.b === 1 && c.B === 2 && c.m1() && C.A && C.supername === "Error";
+	`
+	testScript(SCRIPT, valueTrue, t)
+}
+
+func TestSuperInEval(t *testing.T) {
+	const SCRIPT = `
+	class C extends Error {
+		constructor() {
+			eval("super()");
+		}
+		m() {
+			return eval("super.name");
+		}
+	}
+	let c = new C();
+	c.m() === "Error";
+	`
+	testScript(SCRIPT, valueTrue, t)
+}
+
+func TestSuperRefDot(t *testing.T) {
+	const SCRIPT = `
+	let thisGet, thisSet;
+	class P {
+		_p = 0
+	    get p() {
+			thisGet = this;
+	        return this._p;
+	    }
+		set p(v) {
+			thisSet = this;
+			this._p = v;
+		}
+	}
+
+	class C extends P {
+	    g() {
+	        return super.p;
+	    }
+		s(v) {
+			super.p = v;
+		}
+
+		inc() {
+			super.p++;
+		}
+		incR() {
+			return super.p++;
+		}
+
+		inc1() {
+			++super.p;
+		}
+
+		inc1R() {
+			return ++super.p;
+		}
+		unary() {
+			return +super.p;
+		}
+		pattern() {
+			[super.p] = [9];
+		}
+	}
+
+	let o = new C();
+	assert.sameValue(o.g(), 0, "get value");
+	assert.sameValue(thisGet, o, "get this");
+	o.s(1);
+	assert.sameValue(o._p, 1, "set value");
+	assert.sameValue(thisSet, o, "set this");
+
+	thisGet = undefined;
+	thisSet = undefined;
+	o.inc();
+	assert.sameValue(o._p, 2, "inc value");
+	assert.sameValue(thisGet, o, "inc thisGet");
+	assert.sameValue(thisSet, o, "inc thisSet");
+
+	thisGet = undefined;
+	thisSet = undefined;
+	assert.sameValue(o.incR(), 2, "incR result");
+	assert.sameValue(o._p, 3, "incR value");
+	assert.sameValue(thisGet, o, "incR thisGet");
+	assert.sameValue(thisSet, o, "incR thisSet");
+
+	thisGet = undefined;
+	thisSet = undefined;
+	o.inc1();
+	assert.sameValue(o._p, 4, "inc1 value");
+	assert.sameValue(thisGet, o, "inc1 thisGet");
+	assert.sameValue(thisSet, o, "inc1 thisSet");
+
+	thisGet = undefined;
+	thisSet = undefined;
+	assert.sameValue(o.inc1R(), 5, "inc1R result");
+	assert.sameValue(o._p, 5, "inc1R value");
+	assert.sameValue(thisGet, o, "inc1R thisGet");
+	assert.sameValue(thisSet, o, "inc1R thisSet");
+
+	assert.sameValue(o.unary(), 5, "unary");
+
+	o.pattern();
+	assert.sameValue(o._p, 9, "pattern");
+	`
+	testScriptWithTestLib(SCRIPT, _undefined, t)
+}
+
+func TestPrivateRefDot(t *testing.T) {
+	const SCRIPT = `
+	class C {
+		#p = 0;
+	    g() {
+	        return this.#p;
+	    }
+		s(v) {
+			this.#p = v;
+		}
+
+		inc() {
+			this.#p++;
+		}
+		incR() {
+			return this.#p++;
+		}
+
+		inc1() {
+			++this.#p;
+		}
+
+		inc1R() {
+			return ++this.#p;
+		}
+		pattern() {
+			[this.#p] = [9];
+		}
+	}
+
+	let o = new C();
+	assert.sameValue(o.g(), 0, "get value");
+	o.s(1);
+	assert.sameValue(o.g(), 1, "set value");
+
+	o.inc();
+	assert.sameValue(o.g(), 2, "inc value");
+
+	assert.sameValue(o.incR(), 2, "incR result");
+	assert.sameValue(o.g(), 3, "incR value");
+
+	o.inc1();
+	assert.sameValue(o.g(), 4, "inc1 value");
+
+	assert.sameValue(o.inc1R(), 5, "inc1R result");
+	assert.sameValue(o.g(), 5, "inc1R value");
+
+	o.pattern();
+	assert.sameValue(o.g(), 9, "pattern");
+	`
+	testScriptWithTestLib(SCRIPT, _undefined, t)
+}
+
+func TestPrivateRefDotEval(t *testing.T) {
+	const SCRIPT = `
+	class C {
+		#p = 0;
+	    g() {
+	        return eval("this.#p");
+	    }
+		s(v) {
+			eval("this.#p = v");
+		}
+
+		incR() {
+			return eval("this.#p++");
+		}
+
+		inc1R() {
+			return eval("++this.#p");
+		}
+
+		pattern() {
+			eval("[this.#p] = [9]");
+		}
+	}
+
+	let o = new C();
+	assert.sameValue(o.g(), 0, "get value");
+	o.s(1);
+	assert.sameValue(o.g(), 1, "set value");
+
+	assert.sameValue(o.incR(), 1, "incR result");
+	assert.sameValue(o.g(), 2, "incR value");
+
+	assert.sameValue(o.inc1R(), 3, "inc1R result");
+	assert.sameValue(o.g(), 3, "inc1R value");
+
+	o.pattern();
+	assert.sameValue(o.g(), 9, "pattern");
+	`
+	testScriptWithTestLib(SCRIPT, _undefined, t)
+}
+
+func TestSuperRefDotCallee(t *testing.T) {
+	const SCRIPT = `
+	class P {
+	    get p() {
+	        return function() {
+	            return this;
+	        };
+	    }
+	}
+
+	class C extends P {
+	    m() {
+	        return super.p();
+	    }
+	}
+
+	let o = new C();
+	o.m() === o;
+	`
+	testScript(SCRIPT, valueTrue, t)
+}
+
+func TestSuperRefBracket(t *testing.T) {
+	const SCRIPT = `
+	let PROP = "p";
+	let thisGet, thisSet;
+	class P {
+		_p = 0
+	    get p() {
+			thisGet = this;
+	        return this._p;
+	    }
+		set p(v) {
+			thisSet = this;
+			this._p = v;
+		}
+	}
+
+	class C extends P {
+	    g() {
+	        return super[PROP];
+	    }
+		s(v) {
+			super[PROP] = v;
+		}
+
+		inc() {
+			super[PROP]++;
+		}
+		incR() {
+			return super[PROP]++;
+		}
+
+		inc1() {
+			++super[PROP];
+		}
+
+		inc1R() {
+			return ++super[PROP];
+		}
+		pattern() {
+			[super[PROP]] = [9];
+		}
+	}
+
+	let o = new C();
+	assert.sameValue(o.g(), 0, "get value");
+	assert.sameValue(thisGet, o, "get this");
+	o.s(1);
+	assert.sameValue(o._p, 1, "set value");
+	assert.sameValue(thisSet, o, "set this");
+
+	thisGet = undefined;
+	thisSet = undefined;
+	o.inc();
+	assert.sameValue(o._p, 2, "inc value");
+	assert.sameValue(thisGet, o, "inc thisGet");
+	assert.sameValue(thisSet, o, "inc thisSet");
+
+	thisGet = undefined;
+	thisSet = undefined;
+	assert.sameValue(o.incR(), 2, "incR result");
+	assert.sameValue(o._p, 3, "incR value");
+	assert.sameValue(thisGet, o, "incR thisGet");
+	assert.sameValue(thisSet, o, "incR thisSet");
+
+	thisGet = undefined;
+	thisSet = undefined;
+	o.inc1();
+	assert.sameValue(o._p, 4, "inc1 value");
+	assert.sameValue(thisGet, o, "inc1 thisGet");
+	assert.sameValue(thisSet, o, "inc1 thisSet");
+
+	thisGet = undefined;
+	thisSet = undefined;
+	assert.sameValue(o.inc1R(), 5, "inc1R result");
+	assert.sameValue(o._p, 5, "inc1R value");
+	assert.sameValue(thisGet, o, "inc1R thisGet");
+	assert.sameValue(thisSet, o, "inc1R thisSet");
+
+	o.pattern();
+	assert.sameValue(o._p, 9, "pattern");
+	`
+	testScriptWithTestLib(SCRIPT, _undefined, t)
+}
+
+func TestSuperRefBracketEvalOrder(t *testing.T) {
+	const SCRIPT = `
+	let keyCallCount = 0;
+
+	function key() {
+		keyCallCount++;
+		C.prototype.__proto__ = null;
+	    return "k";
+	}
+
+	class C {
+	    constructor() {
+	        super[key()]++;
+	    }
+	}
+
+	assert.throws(TypeError, () => new C());
+	assert.sameValue(keyCallCount, 1);
+	`
+	testScriptWithTestLib(SCRIPT, _undefined, t)
+}
+
+func TestSuperRefBracketCallee(t *testing.T) {
+	const SCRIPT = `
+	let PROP = "p";
+	class P {
+	    get p() {
+	        return function() {
+	            return this;
+	        };
+	    }
+	}
+
+	class C extends P {
+	    m() {
+	        return super[PROP]();
+	    }
+	}
+
+	let o = new C();
+	o.m() === o;
+	`
+	testScript(SCRIPT, valueTrue, t)
+}
+
+func TestSuperBaseInCtor(t *testing.T) {
+	const SCRIPT = `
+	let result;
+	class Derived extends Object {
+	    constructor() {
+	        super();
+	        result = super.constructor === Object;
+	    }
+	}
+	new Derived();
+	result;
+	`
+	testScript(SCRIPT, valueTrue, t)
+}
+
+func TestClassNamedEval(t *testing.T) {
+	const SCRIPT = `
+	const C = class {
+	}
+
+	C.name === "C";
+	`
+	testScript(SCRIPT, valueTrue, t)
+}
+
+func TestClassNonDerived(t *testing.T) {
+	const SCRIPT = `
+	function initF() {
+	}
+	class C {
+	    f = initF()
+	}
+	let c = new C();
+	`
+
+	testScript(SCRIPT, _undefined, t)
+}
+
+func TestClassExpr(t *testing.T) {
+	const SCRIPT = `
+	typeof Object.getOwnPropertyDescriptor(class {get f() {}}.prototype, "f").get === "function";
+	`
+
+	testScript(SCRIPT, valueTrue, t)
+}
+
+func TestClassSuperInHeritage(t *testing.T) {
+	const SCRIPT = `
+	class P {
+	    a() {
+	        return Error;
+	    }
+	}
+
+	class C extends P {
+	    m() {
+	        class Inner extends super.a() {
+	        }
+	        return new Inner();
+	    }
+	}
+
+	new C().m() instanceof Error;
+	`
+
+	testScript(SCRIPT, valueTrue, t)
+}
+
+func TestClassSuperInHeritageExpr(t *testing.T) {
+	const SCRIPT = `
+	class P {
+	    a() {
+	        return Error;
+	    }
+	}
+
+	class C extends P {
+	    m() {
+			function f(cls) {
+				return new cls();
+			}
+	        return f(class Inner extends super.a() {
+	        })
+	    }
+	}
+
+	new C().m() instanceof Error;
+	`
+
+	testScript(SCRIPT, valueTrue, t)
+}
+
+func TestClassReferToBinding(t *testing.T) {
+	const SCRIPT = `
+	const CC = class C {
+		static T = 40
+	    f = C.T + 2
+	}
+	let c = new CC();
+	c.f;
+	`
+
+	testScript(SCRIPT, intToValue(42), t)
+}
+
+func TestClassReferToBindingInStaticDecl(t *testing.T) {
+	const SCRIPT = `
+	class C {
+		static T = C.name
+	}
+	C.T;
+	`
+
+	testScript(SCRIPT, asciiString("C"), t)
+}
+
+func TestClassReferToBindingInStaticEval(t *testing.T) {
+	const SCRIPT = `
+	const CC = class C {
+		static T = eval("C.name")
+	}
+	CC.T;
+	`
+
+	testScript(SCRIPT, asciiString("C"), t)
+}
+
+func TestClassReferToBindingFromHeritage(t *testing.T) {
+	const SCRIPT = `
+	assert.throws(ReferenceError, () => {
+		class C extends C {
+		}
+	});
+	`
+
+	testScriptWithTestLib(SCRIPT, _undefined, t)
+}
+
+func TestClassCaptureSuperCallInArrowFunc(t *testing.T) {
+	const SCRIPT = `
+	let f;
+	class C extends class {} {
+		constructor() {
+			f = () => super();
+			f();
+		}
+	}
+	let c = new C();
+	`
+
+	testScript(SCRIPT, _undefined, t)
+}
+
+func TestClassCaptureSuperCallInNestedArrowFunc(t *testing.T) {
+	const SCRIPT = `
+	let f;
+	class P {
+	}
+	class C extends P {
+		constructor() {
+			f = () => () => super();
+			f()();
+		}
+	}
+	new C() instanceof P;
+	`
+
+	testScript(SCRIPT, valueTrue, t)
+}
+
+func TestThisInEval(t *testing.T) {
+	const SCRIPT = `
+	assert.sameValue(eval("this"), this, "global");
+
+	let o = {
+		f() {
+			return eval("this");
+		}
+	}
+	assert.sameValue(o.f(), o, "obj literal");
+	`
+
+	testScriptWithTestLib(SCRIPT, _undefined, t)
+}
+
+func TestStaticAsBindingTarget(t *testing.T) {
+	const SCRIPT = `
+	let [static] = [];
+	`
+	testScript(SCRIPT, _undefined, t)
+}
+
+func TestEvalInStaticFieldInit(t *testing.T) {
+	const SCRIPT = `
+	var C = class {
+		static f = 'test';
+		static g = this.f + '262';
+		static h = eval('this.g') + 'test';
+	}
+	C.f === "test" && C.g === "test262" && C.h === "test262test";
+	`
+	testScript(SCRIPT, valueTrue, t)
+}
+
+func TestClassPrivateElemInEval(t *testing.T) {
+	const SCRIPT = `
+	let f1, f2;
+
+	class C extends (f1 = o => eval("o.#a"), Object) {
+	    static #a = 42;
+	    static {
+	        f2 = o => eval("o.#a");
+			assert.sameValue(C.#a, 42);
+			assert.sameValue((() => C.#a)(), 42);
+	    }
+	}
+
+	assert.throws(SyntaxError, () => f1(C));
+	assert.sameValue(f2(C), 42);
+	`
+	testScriptWithTestLib(SCRIPT, _undefined, t)
+}
+
+func TestClassPrivateElemInIndirectEval(t *testing.T) {
+	const SCRIPT = `
+	let f1, f2;
+
+	class C extends (f1 = o => (0, eval)("o.#a"), Object) {
+	    static #a = 42;
+	    static {
+	        f2 = o => (0, eval)("o.#a");
+			assert.throws(SyntaxError, () => (0, eval)("C.#a"));
+	    }
+	}
+
+	assert.throws(SyntaxError, () => f1(C));
+	assert.throws(SyntaxError, () => f2(C));
+	`
+	testScriptWithTestLib(SCRIPT, _undefined, t)
+}
+
+func TestClassPrivateElemInFunction(t *testing.T) {
+	const SCRIPT = `
+	assert.throws(SyntaxError, () => {
+		class C {
+		    static #a = 42;
+		    static {
+		        Function("o", "return o.#a");
+		    }
+		}
+	});
+	`
+	testScriptWithTestLib(SCRIPT, _undefined, t)
+}
+
+func TestClassPrivateElementsDecl(t *testing.T) {
+	const SCRIPT = `
+	class C {
+		#a = 42;
+		get #b() {}
+		set #b(_) {}
+		get c() {
+			return this.#a;
+		}
+		#m() {
+			return this.#a;
+		}
+		static getter(inst) {
+			return inst.#m();
+		}
+	}
+	let c = new C();
+	c.c + C.getter(c);
+	`
+	testScript(SCRIPT, intToValue(84), t)
+}
+
+func TestPrivateIn(t *testing.T) {
+	const SCRIPT = `
+	class C {
+		#a = 42;
+		static check(inst) {
+			return #a in inst;
+		}
+	}
+	let c = new C();
+	C.check(c);
+	`
+	testScript(SCRIPT, valueTrue, t)
+}
+
 /*
 func TestBabel(t *testing.T) {
 	src, err := ioutil.ReadFile("babel7.js")

+ 4 - 0
destruct.go

@@ -298,3 +298,7 @@ func (d *destructKeyedSource) _putProp(name unistring.String, value Value, writa
 func (d *destructKeyedSource) _putSym(s *Symbol, prop Value) {
 	d.w()._putSym(s, prop)
 }
+
+func (d *destructKeyedSource) getPrivateEnv(typ *privateEnvType, create bool) *privateElements {
+	return d.w().getPrivateEnv(typ, create)
+}

+ 122 - 17
func.go

@@ -15,7 +15,9 @@ type baseFuncObject struct {
 type baseJsFuncObject struct {
 	baseFuncObject
 
-	stash  *stash
+	stash   *stash
+	privEnv *privateEnv
+
 	prg    *Program
 	src    string
 	strict bool
@@ -25,13 +27,25 @@ type funcObject struct {
 	baseJsFuncObject
 }
 
+type classFuncObject struct {
+	baseJsFuncObject
+	initFields   *Program
+	computedKeys []Value
+
+	privateEnvType *privateEnvType
+	privateMethods []Value
+
+	derived bool
+}
+
 type methodFuncObject struct {
 	baseJsFuncObject
+	homeObject *Object
 }
 
 type arrowFuncObject struct {
 	baseJsFuncObject
-	this      Value
+	funcObj   *Object
 	newTarget Value
 }
 
@@ -128,7 +142,17 @@ func (f *funcObject) iterateStringKeys() iterNextFunc {
 	return f.baseFuncObject.iterateStringKeys()
 }
 
-func (f *funcObject) construct(args []Value, newTarget *Object) *Object {
+func (f *baseFuncObject) createInstance(newTarget *Object) *Object {
+	r := f.val.runtime
+	if newTarget == nil {
+		newTarget = f.val
+	}
+	proto := r.getPrototypeFromCtor(newTarget, nil, r.global.ObjectPrototype)
+
+	return f.val.runtime.newBaseObject(proto, classObject).val
+}
+
+func (f *baseJsFuncObject) construct(args []Value, newTarget *Object) *Object {
 	if newTarget == nil {
 		newTarget = f.val
 	}
@@ -152,23 +176,110 @@ func (f *funcObject) construct(args []Value, newTarget *Object) *Object {
 	return obj
 }
 
+func (f *classFuncObject) Call(FunctionCall) Value {
+	panic(f.val.runtime.NewTypeError("Class constructor cannot be invoked without 'new'"))
+}
+
+func (f *classFuncObject) assertCallable() (func(FunctionCall) Value, bool) {
+	return f.Call, true
+}
+
+func (f *classFuncObject) createInstance(args []Value, newTarget *Object) (instance *Object) {
+	if f.derived {
+		if ctor := f.prototype.self.assertConstructor(); ctor != nil {
+			instance = ctor(args, newTarget)
+		} else {
+			panic(f.val.runtime.NewTypeError("Super constructor is not a constructor"))
+		}
+	} else {
+		instance = f.baseFuncObject.createInstance(newTarget)
+	}
+	return
+}
+
+func (f *classFuncObject) _initFields(instance *Object) {
+	if f.privateEnvType != nil {
+		penv := instance.self.getPrivateEnv(f.privateEnvType, true)
+		penv.methods = f.privateMethods
+	}
+	if f.initFields != nil {
+		vm := f.val.runtime.vm
+		vm.pushCtx()
+		vm.prg = f.initFields
+		vm.stash = f.stash
+		vm.privEnv = f.privEnv
+		vm.newTarget = nil
+
+		// so that 'super' base could be correctly resolved (including from direct eval())
+		vm.push(f.val)
+
+		vm.sb = vm.sp
+		vm.push(instance)
+		vm.pc = 0
+		vm.run()
+		vm.popCtx()
+		vm.sp -= 2
+		vm.halt = false
+	}
+}
+
+func (f *classFuncObject) construct(args []Value, newTarget *Object) *Object {
+	if newTarget == nil {
+		newTarget = f.val
+	}
+	if f.prg == nil {
+		instance := f.createInstance(args, newTarget)
+		f._initFields(instance)
+		return instance
+	} else {
+		var instance *Object
+		var thisVal Value
+		if !f.derived {
+			instance = f.createInstance(args, newTarget)
+			f._initFields(instance)
+			thisVal = instance
+		}
+		ret := f._call(args, newTarget, thisVal)
+
+		if ret, ok := ret.(*Object); ok {
+			return ret
+		}
+		if f.derived {
+			r := f.val.runtime
+			if ret != _undefined {
+				panic(r.NewTypeError("Derived constructors may only return object or undefined"))
+			}
+			if v := r.vm.stack[r.vm.sp+1]; v != nil { // using residual 'this' value (a bit hacky)
+				instance = r.toObject(v)
+			} else {
+				panic(r.newError(r.global.ReferenceError, "Must call super constructor in derived class before returning from derived constructor"))
+			}
+		}
+		return instance
+	}
+}
+
+func (f *classFuncObject) assertConstructor() func(args []Value, newTarget *Object) *Object {
+	return f.construct
+}
+
 func (f *baseJsFuncObject) Call(call FunctionCall) Value {
 	return f.call(call, nil)
 }
 
 func (f *arrowFuncObject) Call(call FunctionCall) Value {
-	return f._call(call, f.newTarget, f.this)
+	return f._call(call.Arguments, f.newTarget, nil)
 }
 
-func (f *baseJsFuncObject) _call(call FunctionCall, newTarget, this Value) Value {
+func (f *baseJsFuncObject) _call(args []Value, newTarget, this Value) Value {
 	vm := f.val.runtime.vm
 
-	vm.stack.expand(vm.sp + len(call.Arguments) + 1)
+	vm.stack.expand(vm.sp + len(args) + 1)
 	vm.stack[vm.sp] = f.val
 	vm.sp++
 	vm.stack[vm.sp] = this
 	vm.sp++
-	for _, arg := range call.Arguments {
+	for _, arg := range args {
 		if arg != nil {
 			vm.stack[vm.sp] = arg
 		} else {
@@ -185,9 +296,10 @@ func (f *baseJsFuncObject) _call(call FunctionCall, newTarget, this Value) Value
 	} else {
 		vm.pushCtx()
 	}
-	vm.args = len(call.Arguments)
+	vm.args = len(args)
 	vm.prg = f.prg
 	vm.stash = f.stash
+	vm.privEnv = f.privEnv
 	vm.newTarget = newTarget
 	vm.pc = 0
 	vm.run()
@@ -200,7 +312,7 @@ func (f *baseJsFuncObject) _call(call FunctionCall, newTarget, this Value) Value
 }
 
 func (f *baseJsFuncObject) call(call FunctionCall, newTarget Value) Value {
-	return f._call(call, newTarget, nilSafe(call.This))
+	return f._call(call.Arguments, newTarget, nilSafe(call.This))
 }
 
 func (f *funcObject) export(*objectExportCtx) interface{} {
@@ -259,14 +371,7 @@ func (f *baseFuncObject) hasInstance(v Value) bool {
 }
 
 func (f *nativeFuncObject) defaultConstruct(ccall func(ConstructorCall) *Object, args []Value, newTarget *Object) *Object {
-	proto := f.getStr("prototype", nil)
-	var protoObj *Object
-	if p, ok := proto.(*Object); ok {
-		protoObj = p
-	} else {
-		protoObj = f.val.runtime.global.ObjectPrototype
-	}
-	obj := f.val.runtime.newBaseObject(protoObj, classObject).val
+	obj := f.createInstance(newTarget)
 	ret := ccall(ConstructorCall{
 		This:      obj,
 		Arguments: args,

+ 54 - 0
object.go

@@ -207,6 +207,7 @@ type objectImpl interface {
 
 	_putProp(name unistring.String, value Value, writable, enumerable, configurable bool) Value
 	_putSym(s *Symbol, prop Value)
+	getPrivateEnv(typ *privateEnvType, create bool) *privateElements
 }
 
 type baseObject struct {
@@ -221,6 +222,8 @@ type baseObject struct {
 	lastSortedPropLen, idxPropCount int
 
 	symValues *orderedMap
+
+	privateElements map[*privateEnvType]*privateElements
 }
 
 type guardedObject struct {
@@ -810,6 +813,23 @@ func (o *baseObject) _putSym(s *Symbol, prop Value) {
 	o.symValues.set(s, prop)
 }
 
+func (o *baseObject) getPrivateEnv(typ *privateEnvType, create bool) *privateElements {
+	env := o.privateElements[typ]
+	if env != nil && create {
+		panic(o.val.runtime.NewTypeError("Private fields for the class have already been set"))
+	}
+	if env == nil && create {
+		env = &privateElements{
+			fields: make([]Value, typ.numFields),
+		}
+		if o.privateElements == nil {
+			o.privateElements = make(map[*privateEnvType]*privateElements)
+		}
+		o.privateElements[typ] = env
+	}
+	return env
+}
+
 func (o *Object) tryPrimitive(methodName unistring.String) Value {
 	if method, ok := o.self.getStr(methodName, nil).(*Object); ok {
 		if call, ok := method.self.assertCallable(); ok {
@@ -1776,3 +1796,37 @@ func iterateEnumerableStringProperties(o *Object) iterNextFunc {
 		}).next,
 	}).next
 }
+
+type privateId struct {
+	typ      *privateEnvType
+	name     unistring.String
+	idx      uint32
+	isMethod bool
+}
+
+type privateEnvType struct {
+	numFields, numMethods uint32
+}
+
+type privateNames map[unistring.String]*privateId
+
+type privateEnv struct {
+	instanceType, staticType *privateEnvType
+
+	names privateNames
+
+	outer *privateEnv
+}
+
+type privateElements struct {
+	methods []Value
+	fields  []Value
+}
+
+func (i *privateId) String() string {
+	return "#" + i.name.String()
+}
+
+func (i *privateId) string() unistring.String {
+	return privateIdString(i.name)
+}

+ 4 - 0
object_dynamic.go

@@ -526,6 +526,10 @@ func (*baseDynamicObject) _putProp(name unistring.String, value Value, writable,
 func (*baseDynamicObject) _putSym(s *Symbol, prop Value) {
 }
 
+func (o *baseDynamicObject) getPrivateEnv(*privateEnvType, bool) *privateElements {
+	panic(o.val.runtime.NewTypeError("Dynamic objects cannot have private elements"))
+}
+
 func (a *dynamicArray) sortLen() int {
 	return a.a.Len()
 }

+ 6 - 0
object_lazy.go

@@ -295,6 +295,12 @@ func (o *lazyObject) setProto(proto *Object, throw bool) bool {
 	return obj.setProto(proto, throw)
 }
 
+func (o *lazyObject) getPrivateEnv(typ *privateEnvType, create bool) *privateElements {
+	obj := o.create(o.val)
+	o.val.self = obj
+	return obj.getPrivateEnv(typ, create)
+}
+
 func (o *lazyObject) sortLen() int {
 	obj := o.create(o.val)
 	o.val.self = obj

+ 122 - 29
parser/expression.go

@@ -93,8 +93,12 @@ func (self *_parser) parsePrimaryExpression() ast.Expression {
 		return &ast.ThisExpression{
 			Idx: idx,
 		}
+	case token.SUPER:
+		return self.parseSuperProperty()
 	case token.FUNCTION:
 		return self.parseFunction(false)
+	case token.CLASS:
+		return self.parseClass(false)
 	}
 
 	self.errorUnexpectedToken(self.token)
@@ -102,6 +106,44 @@ func (self *_parser) parsePrimaryExpression() ast.Expression {
 	return &ast.BadExpression{From: idx, To: self.idx}
 }
 
+func (self *_parser) parseSuperProperty() ast.Expression {
+	idx := self.idx
+	self.next()
+	switch self.token {
+	case token.PERIOD:
+		self.next()
+		if !token.IsId(self.token) {
+			self.expect(token.IDENTIFIER)
+			self.nextStatement()
+			return &ast.BadExpression{From: idx, To: self.idx}
+		}
+		idIdx := self.idx
+		parsedLiteral := self.parsedLiteral
+		self.next()
+		return &ast.DotExpression{
+			Left: &ast.SuperExpression{
+				Idx: idx,
+			},
+			Identifier: ast.Identifier{
+				Name: parsedLiteral,
+				Idx:  idIdx,
+			},
+		}
+	case token.LEFT_BRACKET:
+		return self.parseBracketMember(&ast.SuperExpression{
+			Idx: idx,
+		})
+	case token.LEFT_PARENTHESIS:
+		return self.parseCallExpression(&ast.SuperExpression{
+			Idx: idx,
+		})
+	default:
+		self.error(idx, "'super' keyword unexpected here")
+		self.nextStatement()
+		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))
@@ -211,10 +253,15 @@ func (self *_parser) parseRegExpLiteral() *ast.RegExpLiteral {
 	}
 }
 
-func (self *_parser) parseBindingTarget() (target ast.BindingTarget) {
-	if self.token == token.LET {
+func (self *_parser) tokenToId() {
+	switch self.token {
+	case token.LET, token.STATIC:
 		self.token = token.IDENTIFIER
 	}
+}
+
+func (self *_parser) parseBindingTarget() (target ast.BindingTarget) {
+	self.tokenToId()
 	switch self.token {
 	case token.IDENTIFIER:
 		target = &ast.Identifier{
@@ -308,6 +355,13 @@ func (self *_parser) parseObjectPropertyKey() (string, unistring.String, ast.Exp
 			Literal: literal,
 			Value:   parsedLiteral,
 		}
+	case token.PRIVATE_IDENTIFIER:
+		value = &ast.PrivateIdentifier{
+			Identifier: ast.Identifier{
+				Idx:  idx,
+				Name: parsedLiteral,
+			},
+		}
 	default:
 		// null, false, class, etc.
 		if token.IsId(tkn) {
@@ -375,27 +429,18 @@ func (self *_parser) parseObjectProperty() ast.Property {
 			if keyValue == nil {
 				return nil
 			}
+
 			var kind ast.PropertyKind
-			idx1 := self.idx
-			parameterList := self.parseFunctionParameterList()
 			if literal == "get" {
 				kind = ast.PropertyKindGet
-				if len(parameterList.List) > 0 || parameterList.Rest != nil {
-					self.error(idx1, "Getter must not have any formal parameters.")
-				}
 			} else {
 				kind = ast.PropertyKindSet
 			}
-			node := &ast.FunctionLiteral{
-				Function:      keyStartIdx,
-				ParameterList: parameterList,
-			}
-			node.Body, node.DeclarationList = self.parseFunctionBlock()
-			node.Source = self.slice(keyStartIdx, node.Body.Idx1())
+
 			return &ast.PropertyKeyed{
 				Key:   keyValue,
 				Kind:  kind,
-				Value: node,
+				Value: self.parseMethodDefinition(keyStartIdx, kind),
 			}
 		}
 	}
@@ -409,6 +454,28 @@ func (self *_parser) parseObjectProperty() ast.Property {
 	}
 }
 
+func (self *_parser) parseMethodDefinition(keyStartIdx file.Idx, kind ast.PropertyKind) *ast.FunctionLiteral {
+	idx1 := self.idx
+	parameterList := self.parseFunctionParameterList()
+	switch kind {
+	case ast.PropertyKindGet:
+		if len(parameterList.List) > 0 || parameterList.Rest != nil {
+			self.error(idx1, "Getter must not have any formal parameters.")
+		}
+	case ast.PropertyKindSet:
+		if len(parameterList.List) != 1 || parameterList.Rest != nil {
+			self.error(idx1, "Setter must have exactly one formal parameter.")
+		}
+	}
+	node := &ast.FunctionLiteral{
+		Function:      keyStartIdx,
+		ParameterList: parameterList,
+	}
+	node.Body, node.DeclarationList = self.parseFunctionBlock()
+	node.Source = self.slice(keyStartIdx, node.Body.Idx1())
+	return node
+}
+
 func (self *_parser) parseObjectLiteral() *ast.ObjectLiteral {
 	var value []ast.Property
 	idx0 := self.expect(token.LEFT_BRACE)
@@ -542,16 +609,29 @@ func (self *_parser) parseDotMember(left ast.Expression) ast.Expression {
 	literal := self.parsedLiteral
 	idx := self.idx
 
+	if self.token == token.PRIVATE_IDENTIFIER {
+		self.next()
+		return &ast.PrivateDotExpression{
+			Left: left,
+			Identifier: ast.PrivateIdentifier{
+				Identifier: ast.Identifier{
+					Idx:  idx,
+					Name: literal,
+				},
+			},
+		}
+	}
+
 	if !token.IsId(self.token) {
 		self.expect(token.IDENTIFIER)
 		self.nextStatement()
 		return &ast.BadExpression{From: period, To: self.idx}
 	}
 
-	if leftStr, ok := left.(*ast.StringLiteral); ok && leftStr.Value == "new" {
+	/*if leftStr, ok := left.(*ast.StringLiteral); ok && leftStr.Value == "new" {
 		self.error(left.Idx0(), "Keyword must not contain escaped characters")
 		return &ast.BadExpression{From: period, To: self.idx}
-	}
+	}*/
 
 	self.next()
 
@@ -581,9 +661,6 @@ func (self *_parser) parseNewExpression() ast.Expression {
 	if self.token == token.PERIOD {
 		self.next()
 		if self.literal == "target" {
-			if !self.scope.inFunction {
-				self.error(idx, "new.target expression is not allowed here")
-			}
 			return &ast.MetaProperty{
 				Meta: &ast.Identifier{
 					Name: unistring.String(token.NEW.String()),
@@ -704,7 +781,7 @@ func (self *_parser) parsePostfixExpression() ast.Expression {
 		idx := self.idx
 		self.next()
 		switch operand.(type) {
-		case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression:
+		case *ast.Identifier, *ast.DotExpression, *ast.PrivateDotExpression, *ast.BracketExpression:
 		default:
 			self.error(idx, "Invalid left-hand side in assignment")
 			self.nextStatement()
@@ -741,7 +818,7 @@ func (self *_parser) parseUnaryExpression() ast.Expression {
 		self.next()
 		operand := self.parseUnaryExpression()
 		switch operand.(type) {
-		case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression:
+		case *ast.Identifier, *ast.DotExpression, *ast.PrivateDotExpression, *ast.BracketExpression:
 		default:
 			self.error(idx, "Invalid left-hand side in assignment")
 			self.nextStatement()
@@ -830,6 +907,24 @@ func (self *_parser) parseShiftExpression() ast.Expression {
 }
 
 func (self *_parser) parseRelationalExpression() ast.Expression {
+	if self.scope.allowIn && self.token == token.PRIVATE_IDENTIFIER {
+		left := &ast.PrivateIdentifier{
+			Identifier: ast.Identifier{
+				Idx:  self.idx,
+				Name: self.parsedLiteral,
+			},
+		}
+		self.next()
+		if self.token == token.IN {
+			self.next()
+			return &ast.BinaryExpression{
+				Operator: self.token,
+				Left:     left,
+				Right:    self.parseShiftExpression(),
+			}
+		}
+		return left
+	}
 	left := self.parseShiftExpression()
 
 	allowIn := self.scope.allowIn
@@ -1036,11 +1131,11 @@ 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 {
+	if self.token == token.LEFT_PARENTHESIS {
 		self.mark(&state)
 		parenthesis = true
+	} else {
+		self.tokenToId()
 	}
 	left := self.parseConditionalExpression()
 	var operator token.Token
@@ -1107,7 +1202,7 @@ func (self *_parser) parseAssignmentExpression() ast.Expression {
 		self.next()
 		ok := false
 		switch l := left.(type) {
-		case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression:
+		case *ast.Identifier, *ast.DotExpression, *ast.PrivateDotExpression, *ast.BracketExpression:
 			ok = true
 		case *ast.ArrayLiteral:
 			if !parenthesis && operator == token.ASSIGN {
@@ -1136,9 +1231,7 @@ func (self *_parser) parseAssignmentExpression() ast.Expression {
 }
 
 func (self *_parser) parseExpression() ast.Expression {
-	if self.token == token.LET {
-		self.token = token.IDENTIFIER
-	}
+	self.tokenToId()
 	left := self.parseAssignmentExpression()
 
 	if self.token == token.COMMA {
@@ -1373,7 +1466,7 @@ func (self *_parser) reinterpretAsDestructAssignTarget(item ast.Expression) ast.
 		return self.reinterpretAsArrayAssignmentPattern(item)
 	case *ast.ObjectLiteral:
 		return self.reinterpretAsObjectAssignmentPattern(item)
-	case ast.Pattern, *ast.Identifier, *ast.DotExpression, *ast.BracketExpression:
+	case ast.Pattern, *ast.Identifier, *ast.DotExpression, *ast.PrivateDotExpression, *ast.BracketExpression:
 		return item
 	}
 	self.error(item.Idx0(), "Invalid destructuring assignment target")

+ 10 - 0
parser/lexer.go

@@ -429,6 +429,16 @@ func (self *_parser) scan() (tkn token.Token, literal string, parsedLiteral unis
 				}
 			case '`':
 				tkn = token.BACKTICK
+			case '#':
+				var err string
+				literal, parsedLiteral, _, err = self.scanIdentifier()
+				if err != "" || literal == "" {
+					tkn = token.ILLEGAL
+					break
+				}
+				self.insertSemicolon = true
+				tkn = token.PRIVATE_IDENTIFIER
+				return
 			default:
 				self.errorUnexpected(idx, chr)
 				tkn = token.ILLEGAL

+ 1 - 1
parser/lexer_test.go

@@ -200,7 +200,7 @@ Second line \
 			token.VAR, "var", 1,
 			token.IF, "if", 5,
 			token.VAR, "var", 8,
-			token.KEYWORD, "class", 12,
+			token.CLASS, "class", 12,
 			token.EOF, "", 17,
 		)
 

+ 25 - 8
parser/parser_test.go

@@ -237,7 +237,7 @@ func TestParserErr(t *testing.T) {
 
 		test("a if", "(anonymous): Line 1:3 Unexpected token if")
 
-		test("a class", "(anonymous): Line 1:3 Unexpected reserved word")
+		test("a class", "(anonymous): Line 1:3 Unexpected token class")
 
 		test("break\n", "(anonymous): Line 1:1 Illegal break statement")
 
@@ -382,7 +382,7 @@ func TestParserErr(t *testing.T) {
 
 		test("/*/.source", "(anonymous): Line 1:11 Unexpected end of input")
 
-		test("var class", "(anonymous): Line 1:5 Unexpected reserved word")
+		test("var class", "(anonymous): Line 1:5 Unexpected token class")
 
 		test("var if", "(anonymous): Line 1:5 Unexpected token if")
 
@@ -416,9 +416,9 @@ func TestParserErr(t *testing.T) {
 
 		{ // Reserved words
 
-			test("class", "(anonymous): Line 1:1 Unexpected reserved word")
+			test("class", "(anonymous): Line 1:6 Unexpected end of input")
 			test("abc.class = 1", nil)
-			test("var class;", "(anonymous): Line 1:5 Unexpected reserved word")
+			test("var class;", "(anonymous): Line 1:5 Unexpected token class")
 
 			test("const", "(anonymous): Line 1:6 Unexpected end of input")
 			test("abc.const = 1", nil)
@@ -432,17 +432,17 @@ func TestParserErr(t *testing.T) {
 			test("abc.export = 1", nil)
 			test("var export;", "(anonymous): Line 1:5 Unexpected reserved word")
 
-			test("extends", "(anonymous): Line 1:1 Unexpected reserved word")
+			test("extends", "(anonymous): Line 1:1 Unexpected token extends")
 			test("abc.extends = 1", nil)
-			test("var extends;", "(anonymous): Line 1:5 Unexpected reserved word")
+			test("var extends;", "(anonymous): Line 1:5 Unexpected token extends")
 
 			test("import", "(anonymous): Line 1:1 Unexpected reserved word")
 			test("abc.import = 1", nil)
 			test("var import;", "(anonymous): Line 1:5 Unexpected reserved word")
 
-			test("super", "(anonymous): Line 1:1 Unexpected reserved word")
+			test("super", "(anonymous): Line 1:1 'super' keyword unexpected here")
 			test("abc.super = 1", nil)
-			test("var super;", "(anonymous): Line 1:5 Unexpected reserved word")
+			test("var super;", "(anonymous): Line 1:5 Unexpected token super")
 			test(`
 			obj = {
 			  aaa: 1
@@ -506,6 +506,11 @@ func TestParserErr(t *testing.T) {
 		test(`(0 ?? 0 || true)`, "(anonymous): Line 1:9 Logical expressions and coalesce expressions cannot be mixed. Wrap either by parentheses")
 		test(`(a || b ?? c)`, "(anonymous): Line 1:9 Logical expressions and coalesce expressions cannot be mixed. Wrap either by parentheses")
 		test(`2 ?? 2 && 3 + 3`, "(anonymous): Line 1:3 Logical expressions and coalesce expressions cannot be mixed. Wrap either by parentheses")
+		test(`
+		class C {
+            st\u0061tic m() {}
+		}
+		`, "(anonymous): Line 3:25 Unexpected identifier")
 	})
 }
 
@@ -911,6 +916,18 @@ func TestParser(t *testing.T) {
 		program = test(`a ?? b ?? c`, nil)
 		is(len(program.Body), 1)
 		is(program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right.(*ast.Identifier).Name, "c")
+
+		program = test(`
+		class C {
+			a
+			b
+			#c
+			m() {
+				return this.#c;
+			}
+		}
+		`, nil)
+		is(len(program.Body), 1)
 	})
 }
 

+ 137 - 1
parser/statement.go

@@ -77,6 +77,10 @@ func (self *_parser) parseStatement() ast.Statement {
 		return &ast.FunctionDeclaration{
 			Function: self.parseFunction(true),
 		}
+	case token.CLASS:
+		return &ast.ClassDeclaration{
+			Class: self.parseClass(true),
+		}
 	case token.SWITCH:
 		return self.parseSwitchStatement()
 	case token.RETURN:
@@ -221,6 +225,138 @@ func (self *_parser) parseArrowFunctionBody() (ast.ConciseBody, []*ast.VariableD
 	}, nil
 }
 
+func (self *_parser) parseClass(declaration bool) *ast.ClassLiteral {
+	if !self.scope.allowLet && self.token == token.CLASS {
+		self.errorUnexpectedToken(token.CLASS)
+	}
+
+	node := &ast.ClassLiteral{
+		Class: self.expect(token.CLASS),
+	}
+
+	var name *ast.Identifier
+	if self.token == token.IDENTIFIER {
+		name = self.parseIdentifier()
+	} else if declaration {
+		// Use expect error handling
+		self.expect(token.IDENTIFIER)
+	}
+
+	node.Name = name
+
+	if self.token != token.LEFT_BRACE {
+		self.expect(token.EXTENDS)
+		node.SuperClass = self.parseLeftHandSideExpressionAllowCall()
+	}
+
+	self.expect(token.LEFT_BRACE)
+
+	for self.token != token.RIGHT_BRACE && self.token != token.EOF {
+		if self.token == token.SEMICOLON {
+			self.next()
+			continue
+		}
+		start := self.idx
+		static := false
+		if self.token == token.STATIC {
+			switch self.peek() {
+			case token.ASSIGN, token.SEMICOLON, token.RIGHT_BRACE, token.LEFT_PARENTHESIS:
+				// treat as identifier
+			default:
+				self.next()
+				if self.token == token.LEFT_BRACE {
+					b := &ast.ClassStaticBlock{
+						Static: start,
+					}
+					b.Block, b.DeclarationList = self.parseFunctionBlock()
+					b.Source = self.slice(b.Block.LeftBrace, b.Block.Idx1())
+					node.Body = append(node.Body, b)
+					continue
+				}
+				static = true
+			}
+		}
+
+		var kind ast.PropertyKind
+		methodBodyStart := self.idx
+		if self.literal == "get" || self.literal == "set" {
+			if self.peek() != token.LEFT_PARENTHESIS {
+				if self.literal == "get" {
+					kind = ast.PropertyKindGet
+				} else {
+					kind = ast.PropertyKindSet
+				}
+				self.next()
+			}
+		}
+
+		_, keyName, value, _ := self.parseObjectPropertyKey()
+		if value == nil {
+			continue
+		}
+		_, private := value.(*ast.PrivateIdentifier)
+
+		if static && !private && keyName == "prototype" {
+			self.error(value.Idx0(), "Classes may not have a static property named 'prototype'")
+		}
+
+		if kind == "" && self.token == token.LEFT_PARENTHESIS {
+			kind = ast.PropertyKindMethod
+		}
+
+		if kind != "" {
+			// method
+			if keyName == "constructor" {
+				if !static && kind != ast.PropertyKindMethod {
+					self.error(value.Idx0(), "Class constructor may not be an accessor")
+				} else if private {
+					self.error(value.Idx0(), "Class constructor may not be a private method")
+				}
+			}
+			md := &ast.MethodDefinition{
+				Idx:    start,
+				Key:    value,
+				Kind:   kind,
+				Static: static,
+				Body:   self.parseMethodDefinition(methodBodyStart, kind),
+			}
+			node.Body = append(node.Body, md)
+		} else {
+			// field
+			isCtor := keyName == "constructor"
+			if !isCtor {
+				if name, ok := value.(*ast.PrivateIdentifier); ok {
+					isCtor = name.Name == "constructor"
+				}
+			}
+			if isCtor {
+				self.error(value.Idx0(), "Classes may not have a field named 'constructor'")
+			}
+			var initializer ast.Expression
+			if self.token == token.ASSIGN {
+				self.next()
+				initializer = self.parseExpression()
+			}
+
+			if !self.implicitSemicolon && self.token != token.SEMICOLON && self.token != token.RIGHT_BRACE {
+				self.errorUnexpectedToken(self.token)
+				break
+			}
+			node.Body = append(node.Body, &ast.FieldDefinition{
+				Idx:         start,
+				Key:         value,
+				Static:      static,
+				Initializer: initializer,
+			})
+		}
+	}
+
+	node.RightBrace = self.expect(token.RIGHT_BRACE)
+	node.Source = self.slice(node.Class, node.RightBrace+1)
+
+	return node
+}
+
 func (self *_parser) parseDebuggerStatement() ast.Statement {
 	idx := self.expect(token.DEBUGGER)
 
@@ -500,7 +636,7 @@ func (self *_parser) parseForOrForInStatement() ast.Statement {
 			}
 			if forIn || forOf {
 				switch e := expr.(type) {
-				case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression, *ast.Binding:
+				case *ast.Identifier, *ast.DotExpression, *ast.PrivateDotExpression, *ast.BracketExpression, *ast.Binding:
 					// These are all acceptable
 				case *ast.ObjectLiteral:
 					expr = self.reinterpretAsObjectAssignmentPattern(e)

+ 56 - 36
runtime.go

@@ -569,6 +569,21 @@ func (r *Runtime) newFunc(name unistring.String, length int, strict bool) (f *fu
 	return
 }
 
+func (r *Runtime) newClassFunc(name unistring.String, length int, proto *Object, derived bool) (f *classFuncObject) {
+	v := &Object{runtime: r}
+
+	f = &classFuncObject{}
+	f.class = classFunction
+	f.val = v
+	f.extensible = true
+	f.strict = true
+	f.derived = derived
+	v.self = f
+	f.prototype = proto
+	f.init(name, intToValue(int64(length)))
+	return
+}
+
 func (r *Runtime) newMethod(name unistring.String, length int, strict bool) (f *methodFuncObject) {
 	v := &Object{runtime: r}
 
@@ -593,14 +608,7 @@ func (r *Runtime) newArrowFunc(name unistring.String, length int, strict bool) (
 	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
@@ -673,6 +681,18 @@ func (r *Runtime) newNativeConstructor(call func(ConstructorCall) *Object, name
 }
 
 func (r *Runtime) newNativeConstructOnly(v *Object, ctor func(args []Value, newTarget *Object) *Object, defaultProto *Object, name unistring.String, length int64) *nativeFuncObject {
+	return r.newNativeFuncAndConstruct(v, func(call FunctionCall) Value {
+		return ctor(call.Arguments, nil)
+	},
+		func(args []Value, newTarget *Object) *Object {
+			if newTarget == nil {
+				newTarget = v
+			}
+			return ctor(args, newTarget)
+		}, defaultProto, name, intToValue(length))
+}
+
+func (r *Runtime) newNativeFuncAndConstruct(v *Object, call func(call FunctionCall) Value, ctor func(args []Value, newTarget *Object) *Object, defaultProto *Object, name unistring.String, l Value) *nativeFuncObject {
 	if v == nil {
 		v = &Object{runtime: r}
 	}
@@ -686,18 +706,11 @@ func (r *Runtime) newNativeConstructOnly(v *Object, ctor func(args []Value, newT
 				prototype:  r.global.FunctionPrototype,
 			},
 		},
-		f: func(call FunctionCall) Value {
-			return ctor(call.Arguments, nil)
-		},
-		construct: func(args []Value, newTarget *Object) *Object {
-			if newTarget == nil {
-				newTarget = v
-			}
-			return ctor(args, newTarget)
-		},
+		f:         call,
+		construct: ctor,
 	}
 	v.self = f
-	f.init(name, intToValue(length))
+	f.init(name, l)
 	if defaultProto != nil {
 		f._putProp("prototype", defaultProto, false, false, false)
 	}
@@ -874,38 +887,45 @@ func (r *Runtime) builtin_thrower(call FunctionCall) Value {
 	return nil
 }
 
-func (r *Runtime) eval(srcVal valueString, direct, strict bool, this Value) Value {
+func (r *Runtime) eval(srcVal valueString, direct, strict bool) Value {
 	src := escapeInvalidUtf16(srcVal)
 	vm := r.vm
 	inGlobal := true
 	if direct {
 		for s := vm.stash; s != nil; s = s.outer {
-			if s.variable {
+			if s.isVariable() {
 				inGlobal = false
 				break
 			}
 		}
 	}
-	p, err := r.compile("<eval>", src, strict, true, inGlobal)
+	vm.pushCtx()
+	funcObj := _undefined
+	if !direct {
+		vm.stash = &r.global.stash
+		vm.privEnv = nil
+	} else {
+		if sb := vm.sb; sb > 0 {
+			funcObj = vm.stack[sb-1]
+		}
+	}
+	p, err := r.compile("<eval>", src, strict, inGlobal, r.vm)
 	if err != nil {
 		panic(err)
 	}
 
-	vm.pushCtx()
 	vm.prg = p
 	vm.pc = 0
 	vm.args = 0
 	vm.result = _undefined
-	if !direct {
-		vm.stash = &r.global.stash
-	}
+	vm.push(funcObj)
 	vm.sb = vm.sp
-	vm.push(this)
+	vm.push(nil) // this
 	vm.run()
 	retval := vm.result
 	vm.popCtx()
 	vm.halt = false
-	vm.sp -= 1
+	vm.sp -= 2
 	return retval
 }
 
@@ -914,7 +934,7 @@ func (r *Runtime) builtin_eval(call FunctionCall) Value {
 		return _undefined
 	}
 	if str, ok := call.Arguments[0].(valueString); ok {
-		return r.eval(str, false, false, r.globalObject)
+		return r.eval(str, false, false)
 	}
 	return call.Arguments[0]
 }
@@ -1256,14 +1276,14 @@ func New() *Runtime {
 // method. This representation is not linked to a runtime in any way and can be run in multiple runtimes (possibly
 // at the same time).
 func Compile(name, src string, strict bool) (*Program, error) {
-	return compile(name, src, strict, false, true)
+	return compile(name, src, strict, true, nil)
 }
 
 // CompileAST creates an internal representation of the JavaScript code that can be later run using the Runtime.RunProgram()
 // method. This representation is not linked to a runtime in any way and can be run in multiple runtimes (possibly
 // at the same time).
 func CompileAST(prg *js_ast.Program, strict bool) (*Program, error) {
-	return compileAST(prg, strict, false, true)
+	return compileAST(prg, strict, true, nil)
 }
 
 // MustCompile is like Compile but panics if the code cannot be compiled.
@@ -1299,16 +1319,16 @@ func Parse(name, src string, options ...parser.Option) (prg *js_ast.Program, err
 	return
 }
 
-func compile(name, src string, strict, eval, inGlobal bool, parserOptions ...parser.Option) (p *Program, err error) {
+func compile(name, src string, strict, inGlobal bool, evalVm *vm, parserOptions ...parser.Option) (p *Program, err error) {
 	prg, err := Parse(name, src, parserOptions...)
 	if err != nil {
 		return
 	}
 
-	return compileAST(prg, strict, eval, inGlobal)
+	return compileAST(prg, strict, inGlobal, evalVm)
 }
 
-func compileAST(prg *js_ast.Program, strict, eval, inGlobal bool) (p *Program, err error) {
+func compileAST(prg *js_ast.Program, strict, inGlobal bool, evalVm *vm) (p *Program, err error) {
 	c := newCompiler()
 
 	defer func() {
@@ -1323,13 +1343,13 @@ func compileAST(prg *js_ast.Program, strict, eval, inGlobal bool) (p *Program, e
 		}
 	}()
 
-	c.compile(prg, strict, eval, inGlobal)
+	c.compile(prg, strict, inGlobal, evalVm)
 	p = c.p
 	return
 }
 
-func (r *Runtime) compile(name, src string, strict, eval, inGlobal bool) (p *Program, err error) {
-	p, err = compile(name, src, strict, eval, inGlobal, r.parserOptions...)
+func (r *Runtime) compile(name, src string, strict, inGlobal bool, evalVm *vm) (p *Program, err error) {
+	p, err = compile(name, src, strict, inGlobal, evalVm, r.parserOptions...)
 	if err != nil {
 		switch x1 := err.(type) {
 		case *CompilerSyntaxError:
@@ -1352,7 +1372,7 @@ func (r *Runtime) RunString(str string) (Value, error) {
 
 // RunScript executes the given string in the global context.
 func (r *Runtime) RunScript(name, src string) (Value, error) {
-	p, err := r.compile(name, src, false, false, true)
+	p, err := r.compile(name, src, false, true, nil)
 
 	if err != nil {
 		return nil, err

+ 147 - 134
tc39_test.go

@@ -43,89 +43,11 @@ var (
 		// GetFunctionRealm
 		"test/built-ins/Function/internals/Construct/base-ctor-revoked-proxy.js": true,
 
-		// Go 1.14 supports unicode 12
-		"test/language/identifiers/start-unicode-13.0.0.js":         true,
-		"test/language/identifiers/start-unicode-13.0.0-escaped.js": true,
-		"test/language/identifiers/start-unicode-14.0.0.js":         true,
-		"test/language/identifiers/start-unicode-14.0.0-escaped.js": true,
-		"test/language/identifiers/part-unicode-13.0.0.js":          true,
-		"test/language/identifiers/part-unicode-13.0.0-escaped.js":  true,
-		"test/language/identifiers/part-unicode-14.0.0.js":          true,
-		"test/language/identifiers/part-unicode-14.0.0-escaped.js":  true,
-
-		// class
-		"test/built-ins/Array/prototype/concat/Array.prototype.concat_non-array.js":                    true,
-		"test/built-ins/ArrayBuffer/isView/arg-is-typedarray-subclass-instance.js":                     true,
-		"test/built-ins/ArrayBuffer/isView/arg-is-dataview-subclass-instance.js":                       true,
-		"test/language/expressions/object/method-definition/name-invoke-ctor.js":                       true,
-		"test/language/expressions/object/method.js":                                                   true,
-		"test/language/expressions/object/setter-super-prop.js":                                        true,
-		"test/language/expressions/object/getter-super-prop.js":                                        true,
-		"test/language/expressions/delete/super-property.js":                                           true,
-		"test/language/statements/let/dstr/obj-ptrn-id-init-fn-name-class.js":                          true,
-		"test/language/statements/let/dstr/ary-ptrn-elem-id-init-fn-name-class.js":                     true,
-		"test/language/statements/for/dstr/var-obj-ptrn-id-init-fn-name-class.js":                      true,
-		"test/language/statements/for/dstr/var-ary-ptrn-elem-id-init-fn-name-class.js":                 true,
-		"test/language/statements/for/dstr/let-ary-ptrn-elem-id-init-fn-name-class.js":                 true,
-		"test/language/statements/for/dstr/let-obj-ptrn-id-init-fn-name-class.js":                      true,
-		"test/language/statements/const/dstr/ary-ptrn-elem-id-init-fn-name-class.js":                   true,
-		"test/language/statements/for/dstr/const-obj-ptrn-id-init-fn-name-class.js":                    true,
-		"test/language/statements/const/dstr/obj-ptrn-id-init-fn-name-class.js":                        true,
-		"test/language/statements/for/dstr/const-ary-ptrn-elem-id-init-fn-name-class.js":               true,
-		"test/language/statements/variable/dstr/obj-ptrn-id-init-fn-name-class.js":                     true,
-		"test/language/statements/variable/dstr/ary-ptrn-elem-id-init-fn-name-class.js":                true,
-		"test/language/expressions/object/method-definition/name-name-prop-symbol.js":                  true,
-		"test/language/expressions/function/dstr/dflt-obj-ptrn-id-init-fn-name-class.js":               true,
-		"test/language/expressions/function/dstr/dflt-ary-ptrn-elem-id-init-fn-name-class.js":          true,
-		"test/language/expressions/function/dstr/ary-ptrn-elem-id-init-fn-name-class.js":               true,
-		"test/language/expressions/function/dstr/obj-ptrn-id-init-fn-name-class.js":                    true,
-		"test/language/statements/function/dstr/dflt-ary-ptrn-elem-id-init-fn-name-class.js":           true,
-		"test/language/statements/function/dstr/obj-ptrn-id-init-fn-name-class.js":                     true,
-		"test/language/statements/function/dstr/ary-ptrn-elem-id-init-fn-name-class.js":                true,
-		"test/language/statements/function/dstr/dflt-obj-ptrn-id-init-fn-name-class.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-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,
-		"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,
-		"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/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,
-		"test/built-ins/Promise/prototype/finally/subclass-species-constructor-resolve-count.js":       true,
-		"test/built-ins/Promise/prototype/finally/subclass-species-constructor-reject-count.js":        true,
-		"test/built-ins/Promise/prototype/finally/subclass-resolve-count.js":                           true,
-		"test/built-ins/Promise/prototype/finally/species-symbol.js":                                   true,
-		"test/built-ins/Promise/prototype/finally/subclass-reject-count.js":                            true,
-		"test/built-ins/Promise/prototype/finally/species-constructor.js":                              true,
-		"test/language/statements/switch/scope-lex-class.js":                                           true,
-		"test/language/expressions/arrow-function/lexical-super-call-from-within-constructor.js":       true,
-		"test/language/expressions/object/dstr/meth-dflt-ary-ptrn-elem-id-init-fn-name-class.js":       true,
-		"test/language/expressions/object/dstr/meth-ary-ptrn-elem-id-init-fn-name-class.js":            true,
-		"test/language/expressions/object/dstr/meth-dflt-obj-ptrn-id-init-fn-name-class.js":            true,
-		"test/language/expressions/object/dstr/meth-obj-ptrn-id-init-fn-name-class.js":                 true,
-		"test/built-ins/Promise/prototype/finally/resolved-observable-then-calls-PromiseResolve.js":    true,
-		"test/built-ins/Promise/prototype/finally/rejected-observable-then-calls-PromiseResolve.js":    true,
-		"test/built-ins/Function/prototype/toString/class-expression-explicit-ctor.js":                 true,
-		"test/built-ins/Function/prototype/toString/class-expression-implicit-ctor.js":                 true,
-		"test/language/global-code/decl-lex.js":                                                        true,
-		"test/language/global-code/decl-lex-deletion.js":                                               true,
-		"test/language/global-code/script-decl-var-collision.js":                                       true,
-		"test/language/global-code/script-decl-lex.js":                                                 true,
-		"test/language/global-code/script-decl-lex-lex.js":                                             true,
-		"test/language/global-code/script-decl-lex-deletion.js":                                        true,
-		"test/language/expressions/optional-chaining/super-property-optional-call.js":                  true,
-		"test/language/expressions/optional-chaining/member-expression.js":                             true,
-		"test/language/expressions/optional-chaining/call-expression.js":                               true,
+		// Uses deprecated __lookupGetter__/__lookupSetter__
+		"test/language/expressions/class/elements/private-getter-is-not-a-own-property.js": true,
+		"test/language/expressions/class/elements/private-setter-is-not-a-own-property.js": true,
+		"test/language/statements/class/elements/private-setter-is-not-a-own-property.js":  true,
+		"test/language/statements/class/elements/private-getter-is-not-a-own-property.js":  true,
 
 		// restricted unicode regexp syntax
 		"test/built-ins/RegExp/unicode_restricted_quantifiable_assertion.js":         true,
@@ -149,45 +71,109 @@ var (
 		"test/annexB/built-ins/RegExp/RegExp-trailing-escape-BMP.js": true,
 
 		// generators
-		"test/annexB/built-ins/RegExp/RegExp-control-escape-russian-letter.js":                                       true,
-		"test/language/statements/switch/scope-lex-generator.js":                                                     true,
-		"test/language/expressions/in/rhs-yield-present.js":                                                          true,
-		"test/language/expressions/object/cpn-obj-lit-computed-property-name-from-yield-expression.js":               true,
-		"test/language/expressions/object/cpn-obj-lit-computed-property-name-from-generator-function-declaration.js": true,
-		"test/built-ins/TypedArrayConstructors/ctors/object-arg/as-generator-iterable-returns.js":                    true,
-		"test/built-ins/Object/seal/seal-generatorfunction.js":                                                       true,
+		"test/annexB/built-ins/RegExp/RegExp-control-escape-russian-letter.js":                                                        true,
+		"test/language/statements/switch/scope-lex-generator.js":                                                                      true,
+		"test/language/expressions/in/rhs-yield-present.js":                                                                           true,
+		"test/language/expressions/object/cpn-obj-lit-computed-property-name-from-yield-expression.js":                                true,
+		"test/language/expressions/object/cpn-obj-lit-computed-property-name-from-generator-function-declaration.js":                  true,
+		"test/built-ins/TypedArrayConstructors/ctors/object-arg/as-generator-iterable-returns.js":                                     true,
+		"test/built-ins/Object/seal/seal-generatorfunction.js":                                                                        true,
+		"test/language/statements/class/syntax/class-declaration-computed-method-generator-definition.js":                             true,
+		"test/language/statements/class/cpn-class-decl-fields-methods-computed-property-name-from-yield-expression.js":                true,
+		"test/language/statements/class/cpn-class-decl-fields-methods-computed-property-name-from-generator-function-declaration.js":  true,
+		"test/language/statements/class/cpn-class-decl-fields-computed-property-name-from-yield-expression.js":                        true,
+		"test/language/statements/class/cpn-class-decl-fields-computed-property-name-from-generator-function-declaration.js":          true,
+		"test/language/statements/class/cpn-class-decl-computed-property-name-from-yield-expression.js":                               true,
+		"test/language/statements/class/cpn-class-decl-computed-property-name-from-generator-function-declaration.js":                 true,
+		"test/language/statements/class/cpn-class-decl-accessors-computed-property-name-from-yield-expression.js":                     true,
+		"test/language/statements/class/cpn-class-decl-accessors-computed-property-name-from-generator-function-declaration.js":       true,
+		"test/language/expressions/class/cpn-class-expr-fields-computed-property-name-from-yield-expression.js":                       true,
+		"test/language/expressions/class/cpn-class-expr-fields-computed-property-name-from-generator-function-declaration.js":         true,
+		"test/language/expressions/class/cpn-class-expr-computed-property-name-from-yield-expression.js":                              true,
+		"test/language/expressions/class/cpn-class-expr-computed-property-name-from-generator-function-declaration.js":                true,
+		"test/language/expressions/class/cpn-class-expr-accessors-computed-property-name-from-yield-expression.js":                    true,
+		"test/language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-yield-expression.js":               true,
+		"test/language/expressions/class/cpn-class-expr-accessors-computed-property-name-from-generator-function-declaration.js":      true,
+		"test/language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-generator-function-declaration.js": true,
+		"test/language/statements/class/static-init-arguments-methods.js":                                                             true,
+		"test/language/statements/class/static-init-arguments-functions.js":                                                           true,
+		"test/language/expressions/object/method-definition/static-init-await-reference-generator.js":                                 true,
+		"test/language/expressions/generators/static-init-await-binding.js":                                                           true,
+		"test/language/expressions/generators/static-init-await-reference.js":                                                         true,
+		"test/language/expressions/optional-chaining/member-expression.js":                                                            true,
+		"test/language/expressions/class/elements/private-generator-method-name.js":                                                   true,
+		"test/language/statements/class/elements/private-generator-method-name.js":                                                    true,
+		"test/language/expressions/in/private-field-rhs-yield-present.js":                                                             true,
+		"test/language/expressions/class/elements/private-static-generator-method-name.js":                                            true,
+		"test/language/expressions/class/elements/private-static-async-generator-method-name.js":                                      true,
 
 		// async
-		"test/language/eval-code/direct/async-func-decl-a-preceding-parameter-is-named-arguments-declare-arguments-and-assign.js": true,
-		"test/language/statements/switch/scope-lex-async-generator.js":                                                            true,
-		"test/language/statements/switch/scope-lex-async-function.js":                                                             true,
-		"test/language/statements/for-of/head-lhs-async-invalid.js":                                                               true,
-		"test/language/expressions/object/cpn-obj-lit-computed-property-name-from-async-arrow-function-expression.js":             true,
-		"test/language/expressions/object/cpn-obj-lit-computed-property-name-from-await-expression.js":                            true,
-		"test/language/statements/async-function/evaluation-body.js":                                                              true,
-		"test/language/expressions/object/method-definition/object-method-returns-promise.js":                                     true,
-		"test/language/expressions/object/method-definition/async-super-call-param.js":                                            true,
-		"test/language/expressions/object/method-definition/async-super-call-body.js":                                             true,
-		"test/built-ins/Object/seal/seal-asyncgeneratorfunction.js":                                                               true,
-		"test/built-ins/Object/seal/seal-asyncfunction.js":                                                                        true,
-		"test/built-ins/Object/seal/seal-asyncarrowfunction.js":                                                                   true,
-		"test/language/statements/for/head-init-async-of.js":                                                                      true,
-		"test/language/reserved-words/await-module.js":                                                                            true,
-		"test/language/expressions/optional-chaining/optional-chain-async-square-brackets.js":                                     true,
-		"test/language/expressions/optional-chaining/optional-chain-async-optional-chain-square-brackets.js":                      true,
-		"test/language/expressions/optional-chaining/member-expression-async-this.js":                                             true,
-		"test/language/expressions/optional-chaining/member-expression-async-literal.js":                                          true,
-		"test/language/expressions/optional-chaining/member-expression-async-identifier.js":                                       true,
-		"test/language/expressions/optional-chaining/iteration-statement-for-await-of.js":                                         true,
+		"test/language/eval-code/direct/async-func-decl-a-preceding-parameter-is-named-arguments-declare-arguments-and-assign.js":      true,
+		"test/language/statements/switch/scope-lex-async-generator.js":                                                                 true,
+		"test/language/statements/switch/scope-lex-async-function.js":                                                                  true,
+		"test/language/statements/for-of/head-lhs-async-invalid.js":                                                                    true,
+		"test/language/expressions/object/cpn-obj-lit-computed-property-name-from-async-arrow-function-expression.js":                  true,
+		"test/language/expressions/object/cpn-obj-lit-computed-property-name-from-await-expression.js":                                 true,
+		"test/language/statements/async-function/evaluation-body.js":                                                                   true,
+		"test/language/expressions/object/method-definition/object-method-returns-promise.js":                                          true,
+		"test/language/expressions/object/method-definition/async-super-call-param.js":                                                 true,
+		"test/language/expressions/object/method-definition/async-super-call-body.js":                                                  true,
+		"test/built-ins/Object/seal/seal-asyncgeneratorfunction.js":                                                                    true,
+		"test/built-ins/Object/seal/seal-asyncfunction.js":                                                                             true,
+		"test/built-ins/Object/seal/seal-asyncarrowfunction.js":                                                                        true,
+		"test/language/statements/for/head-init-async-of.js":                                                                           true,
+		"test/language/reserved-words/await-module.js":                                                                                 true,
+		"test/language/expressions/optional-chaining/optional-chain-async-square-brackets.js":                                          true,
+		"test/language/expressions/optional-chaining/optional-chain-async-optional-chain-square-brackets.js":                           true,
+		"test/language/expressions/optional-chaining/member-expression-async-this.js":                                                  true,
+		"test/language/expressions/optional-chaining/member-expression-async-literal.js":                                               true,
+		"test/language/expressions/optional-chaining/member-expression-async-identifier.js":                                            true,
+		"test/language/expressions/optional-chaining/iteration-statement-for-await-of.js":                                              true,
+		"test/language/statements/class/cpn-class-decl-fields-methods-computed-property-name-from-async-arrow-function-expression.js":  true,
+		"test/language/statements/class/cpn-class-decl-fields-computed-property-name-from-async-arrow-function-expression.js":          true,
+		"test/language/statements/class/cpn-class-decl-computed-property-name-from-async-arrow-function-expression.js":                 true,
+		"test/language/statements/class/cpn-class-decl-accessors-computed-property-name-from-async-arrow-function-expression.js":       true,
+		"test/language/expressions/class/cpn-class-expr-fields-computed-property-name-from-async-arrow-function-expression.js":         true,
+		"test/language/expressions/class/cpn-class-expr-computed-property-name-from-async-arrow-function-expression.js":                true,
+		"test/language/expressions/class/cpn-class-expr-accessors-computed-property-name-from-async-arrow-function-expression.js":      true,
+		"test/language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-async-arrow-function-expression.js": true,
+		"test/language/statements/let/static-init-await-binding-invalid.js":                                                            true,
+		"test/language/statements/labeled/static-init-invalid-await.js":                                                                true,
+		"test/language/statements/variable/dstr/obj-ptrn-elem-id-static-init-await-invalid.js":                                         true,
+		"test/language/statements/variable/static-init-await-binding-invalid.js":                                                       true,
+		"test/language/statements/variable/dstr/ary-ptrn-elem-id-static-init-await-invalid.js":                                         true,
+		"test/language/statements/try/static-init-await-binding-invalid.js":                                                            true,
+		"test/language/statements/function/static-init-await-binding-invalid.js":                                                       true,
+		"test/language/statements/const/static-init-await-binding-invalid.js":                                                          true,
+		"test/language/statements/class/static-init-await-binding-invalid.js":                                                          true,
+		"test/language/identifier-resolution/static-init-invalid-await.js":                                                             true,
+		"test/language/expressions/class/static-init-await-binding.js":                                                                 true,
+		"test/language/expressions/class/heritage-async-arrow-function.js":                                                             true,
+		"test/language/expressions/arrow-function/static-init-await-reference.js":                                                      true,
+		"test/language/expressions/arrow-function/static-init-await-binding.js":                                                        true,
+		"test/language/expressions/object/method-definition/static-init-await-binding-generator.js":                                    true,
+		"test/language/expressions/object/identifier-shorthand-static-init-await-invalid.js":                                           true,
+		"test/language/expressions/class/heritage-arrow-function.js":                                                                   true,
+		"test/language/expressions/class/elements/private-async-generator-method-name.js":                                              true,
+		"test/language/expressions/class/elements/private-async-method-name.js":                                                        true,
+		"test/language/statements/class/elements/private-async-generator-method-name.js":                                               true,
+		"test/language/statements/class/elements/private-async-method-name.js":                                                         true,
+		"test/language/expressions/in/private-field-rhs-await-present.js":                                                              true,
+		"test/language/expressions/class/elements/private-static-async-method-name.js":                                                 true,
 
 		// legacy number literals
 		"test/language/literals/numeric/non-octal-decimal-integer.js": true,
 
-		// coalesce
-		"test/language/expressions/object/cpn-obj-lit-computed-property-name-from-expression-coalesce.js": true,
-
 		// integer separators
-		"test/language/expressions/object/cpn-obj-lit-computed-property-name-from-integer-separators.js": true,
+		"test/language/expressions/object/cpn-obj-lit-computed-property-name-from-integer-separators.js":                  true,
+		"test/language/expressions/class/cpn-class-expr-accessors-computed-property-name-from-integer-separators.js":      true,
+		"test/language/statements/class/cpn-class-decl-fields-computed-property-name-from-integer-separators.js":          true,
+		"test/language/statements/class/cpn-class-decl-computed-property-name-from-integer-separators.js":                 true,
+		"test/language/statements/class/cpn-class-decl-accessors-computed-property-name-from-integer-separators.js":       true,
+		"test/language/statements/class/cpn-class-decl-fields-methods-computed-property-name-from-integer-separators.js":  true,
+		"test/language/expressions/class/cpn-class-expr-fields-computed-property-name-from-integer-separators.js":         true,
+		"test/language/expressions/class/cpn-class-expr-computed-property-name-from-integer-separators.js":                true,
+		"test/language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-integer-separators.js": true,
 
 		// BigInt
 		"test/built-ins/Object/seal/seal-biguint64array.js": true,
@@ -195,10 +181,6 @@ var (
 
 		// FIXME bugs
 
-		// new.target availability
-		"test/language/global-code/new.target-arrow.js":   true,
-		"test/language/eval-code/direct/new.target-fn.js": true,
-
 		// 'in' in a branch
 		"test/language/expressions/conditional/in-branch-1.js": true,
 
@@ -211,11 +193,6 @@ var (
 		"Symbol.asyncIterator",
 		"async-functions",
 		"BigInt",
-		"class",
-		"class-static-block",
-		"class-fields-private",
-		"class-fields-private-in",
-		"super",
 		"generators",
 		"String.prototype.replaceAll",
 		"resizable-arraybuffer",
@@ -243,6 +220,8 @@ var (
 		"ShadowRealm",
 		"SharedArrayBuffer",
 		"error-cause",
+		"decorators",
+		"regexp-v-flag",
 	}
 )
 
@@ -255,16 +234,11 @@ func init() {
 	}
 
 	skip(
-		// class
-		"test/language/statements/class/",
-		"test/language/expressions/class/",
-		"test/language/expressions/super/",
-		"test/language/expressions/assignment/target-super-",
-		"test/language/arguments-object/cls-",
-		"test/built-ins/Function/prototype/toString/class-",
-		"test/built-ins/Function/prototype/toString/setter-class-",
-		"test/built-ins/Function/prototype/toString/method-class-",
-		"test/built-ins/Function/prototype/toString/getter-class-",
+		// Go 1.14 only supports unicode 12
+		"test/language/identifiers/start-unicode-13.",
+		"test/language/identifiers/part-unicode-13.",
+		"test/language/identifiers/start-unicode-14.",
+		"test/language/identifiers/part-unicode-14.",
 
 		// async
 		"test/language/eval-code/direct/async-",
@@ -272,11 +246,50 @@ func init() {
 		"test/language/expressions/await/",
 		"test/language/statements/async-function/",
 		"test/built-ins/Async",
+		"test/language/statements/class/elements/private-static-async-",
+		"test/language/statements/class/elements/wrapped-in-sc-rs-static-async-",
+		"test/language/expressions/class/elements/wrapped-in-sc-rs-static-async-",
+		"test/language/statements/class/elements/after-same-line-static-method-rs-static-async-",
+		"test/language/expressions/class/elements/after-same-line-static-method-rs-static-async-",
+		"test/language/statements/class/elements/after-same-line-method-rs-static-async-",
+		"test/language/expressions/class/elements/after-same-line-method-rs-static-async-",
+		"test/language/statements/class/elements/new-sc-line-method-rs-static-async-",
+		"test/language/expressions/class/elements/new-sc-line-method-rs-static-async-",
+		"test/language/statements/class/elements/new-no-sc-line-method-rs-static-async-",
+		"test/language/expressions/class/elements/new-no-sc-line-method-rs-static-async-",
+		"test/language/statements/class/elements/same-line-method-rs-static-async-",
+		"test/language/expressions/class/elements/same-line-method-rs-static-async-",
+		"test/language/statements/class/elements/regular-definitions-rs-static-async-",
+		"test/language/expressions/class/elements/regular-definitions-rs-static-async-",
+		"test/language/statements/class/elements/multiple-stacked-definitions-rs-static-async-",
+		"test/language/expressions/class/elements/multiple-stacked-definitions-rs-static-async-",
+		"test/language/statements/class/elements/multiple-definitions-rs-static-async-",
+		"test/language/expressions/class/elements/multiple-definitions-rs-static-async-",
 
 		// generators
 		"test/language/eval-code/direct/gen-",
 		"test/built-ins/GeneratorFunction/",
 		"test/built-ins/Function/prototype/toString/generator-",
+		"test/language/statements/class/elements/private-static-generator-",
+		"test/language/statements/class/subclass/builtin-objects/GeneratorFunction/",
+		"test/language/statements/class/elements/wrapped-in-sc-rs-static-generator-",
+		"test/language/expressions/class/elements/wrapped-in-sc-rs-static-generator-",
+		"test/language/statements/class/elements/after-same-line-method-rs-static-generator-",
+		"test/language/expressions/class/elements/after-same-line-method-rs-static-generator-",
+		"test/language/statements/class/elements/after-same-line-static-method-rs-static-generator-",
+		"test/language/expressions/class/elements/after-same-line-static-method-rs-static-generator-",
+		"test/language/statements/class/elements/new-sc-line-method-rs-static-generator-",
+		"test/language/expressions/class/elements/new-sc-line-method-rs-static-generator-",
+		"test/language/statements/class/elements/new-no-sc-line-method-rs-static-generator-",
+		"test/language/expressions/class/elements/new-no-sc-line-method-rs-static-generator-",
+		"test/language/statements/class/elements/same-line-method-rs-static-generator-",
+		"test/language/expressions/class/elements/same-line-method-rs-static-generator-",
+		"test/language/statements/class/elements/regular-definitions-rs-static-generator-",
+		"test/language/expressions/class/elements/regular-definitions-rs-static-generator-",
+		"test/language/statements/class/elements/multiple-stacked-definitions-rs-static-generator-",
+		"test/language/expressions/class/elements/multiple-stacked-definitions-rs-static-generator-",
+		"test/language/statements/class/elements/multiple-definitions-rs-static-generator-",
+		"test/language/expressions/class/elements/multiple-definitions-rs-static-generator-",
 
 		// BigInt
 		"test/built-ins/TypedArrayConstructors/BigUint64Array/",

+ 16 - 9
token/token_const.go

@@ -75,6 +75,8 @@ const (
 	ELLIPSIS          // ...
 	BACKTICK          // `
 
+	PRIVATE_IDENTIFIER
+
 	// tokens below (and only them) are syntactically valid identifiers
 
 	IDENTIFIER
@@ -104,14 +106,18 @@ const (
 	BREAK
 	CATCH
 	THROW
+	CLASS
+	SUPER
 
 	RETURN
 	TYPEOF
 	DELETE
 	SWITCH
+	STATIC
 
 	DEFAULT
 	FINALLY
+	EXTENDS
 
 	FUNCTION
 	CONTINUE
@@ -130,6 +136,7 @@ var token2string = [...]string{
 	NULL:                        "NULL",
 	NUMBER:                      "NUMBER",
 	IDENTIFIER:                  "IDENTIFIER",
+	PRIVATE_IDENTIFIER:          "PRIVATE_IDENTIFIER",
 	PLUS:                        "+",
 	MINUS:                       "-",
 	EXPONENT:                    "**",
@@ -204,12 +211,16 @@ var token2string = [...]string{
 	BREAK:                       "break",
 	CATCH:                       "catch",
 	THROW:                       "throw",
+	CLASS:                       "class",
+	SUPER:                       "super",
 	RETURN:                      "return",
 	TYPEOF:                      "typeof",
 	DELETE:                      "delete",
 	SWITCH:                      "switch",
+	STATIC:                      "static",
 	DEFAULT:                     "default",
 	FINALLY:                     "finally",
+	EXTENDS:                     "extends",
 	FUNCTION:                    "function",
 	CONTINUE:                    "continue",
 	DEBUGGER:                    "debugger",
@@ -299,8 +310,7 @@ var keywordTable = map[string]_keyword{
 		token: CONST,
 	},
 	"class": {
-		token:         KEYWORD,
-		futureKeyword: true,
+		token: CLASS,
 	},
 	"enum": {
 		token:         KEYWORD,
@@ -311,16 +321,14 @@ var keywordTable = map[string]_keyword{
 		futureKeyword: true,
 	},
 	"extends": {
-		token:         KEYWORD,
-		futureKeyword: true,
+		token: EXTENDS,
 	},
 	"import": {
 		token:         KEYWORD,
 		futureKeyword: true,
 	},
 	"super": {
-		token:         KEYWORD,
-		futureKeyword: true,
+		token: SUPER,
 	},
 	"implements": {
 		token:         KEYWORD,
@@ -357,8 +365,7 @@ var keywordTable = map[string]_keyword{
 		strict:        true,
 	},
 	"static": {
-		token:         KEYWORD,
-		futureKeyword: true,
-		strict:        true,
+		token:  STATIC,
+		strict: true,
 	},
 }

+ 1 - 0
value.go

@@ -96,6 +96,7 @@ type valueContainer interface {
 type typeError string
 type rangeError string
 type referenceError string
+type syntaxError string
 
 type valueInt int64
 type valueFloat float64

File diff suppressed because it is too large
+ 567 - 71
vm.go


+ 1 - 1
vm_test.go

@@ -192,7 +192,7 @@ fib(35);
 	}
 
 	c := newCompiler()
-	c.compile(prg, false, false, true)
+	c.compile(prg, false, true, nil)
 	c.p.dumpCode(b.Logf)
 
 	r := &Runtime{}

Some files were not shown because too many files changed in this diff