Browse Source

Merge branch 'upgrade-tests'

Dmitry Panov 3 years ago
parent
commit
26ebff68a7

+ 1 - 1
.tc39_test262_checkout.sh

@@ -1,5 +1,5 @@
 #!/bin/sh
-sha=ddfe24afe3043388827aa220ef623b8540958bbd # this is just the commit it was last tested with
+sha=26f1f4567ee7e33163d961c867d689173cbb9065 # this is just the commit it was last tested with
 mkdir -p testdata/test262
 cd testdata/test262
 git init

+ 23 - 4
README.md

@@ -16,12 +16,11 @@ Features
 --------
 
  * Full ECMAScript 5.1 support (including regex and strict mode).
- * Passes nearly all [tc39 tests](https://github.com/tc39/test262) tagged with es5id. The goal is to pass all of them.
-   Note, the current working commit is https://github.com/tc39/test262/commit/ddfe24afe3043388827aa220ef623b8540958bbd.
-   The next commit removed most of the es5id tags which made it impossible to distinguish which tests to run.
+ * Passes nearly all [tc39 tests](https://github.com/tc39/test262) for the features implemented so far. The goal is to
+   pass all of them. Note, the current working commit is https://github.com/tc39/test262/commit/26f1f4567ee7e33163d961c867d689173cbb9065.
  * Capable of running Babel, Typescript compiler and pretty much anything written in ES5.
  * Sourcemaps.
- * Some ES6 functionality, still work in progress, see https://github.com/dop251/goja/milestone/1?closed=1
+ * Most of ES6 functionality, still work in progress, see https://github.com/dop251/goja/milestone/1?closed=1
  
 Known incompatibilities and caveats
 -----------------------------------
@@ -53,6 +52,26 @@ above is the only reasonable way I can think of without involving finalizers. Th
 
 Note, this does not have any effect on the application logic, but may cause a higher-than-expected memory usage.
 
+### WeakRef and FinalizationRegistry
+For the reason mentioned above implementing WeakRef and FinalizationRegistry does not seem to be possible at this stage.
+
+### JSON
+`JSON.parse()` uses the standard Go library which operates in UTF-8. Therefore, it cannot correctly parse broken UTF-16
+surrogate pairs, for example:
+
+```javascript
+JSON.parse(`"\\uD800"`).charCodeAt(0).toString(16) // returns "fffd" instead of "d800"
+```
+
+### Date
+Conversion from calendar date to epoch timestamp uses the standard Go library which uses `int`, rather than `float` as per
+ECMAScript specification. This means if you pass arguments that overflow int to the `Date()` constructor or  if there is
+an integer overflow, the result will be incorrect, for example:
+
+```javascript
+Date.UTC(1970, 0, 1, 80063993375, 29, 1, -288230376151711740) // returns 29256 instead of 29312
+```
+
 FAQ
 ---
 

+ 50 - 51
array.go

@@ -20,6 +20,9 @@ func (ai *arrayIterObject) next() Value {
 	if ai.obj == nil {
 		return ai.val.runtime.createIterResultObject(_undefined, true)
 	}
+	if ta, ok := ai.obj.self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached(true)
+	}
 	l := toLength(ai.obj.self.getStr("length", nil))
 	index := ai.nextIdx
 	if index >= l {
@@ -74,49 +77,45 @@ func (a *arrayObject) init() {
 	a._put("length", &a.lengthProp)
 }
 
-func (a *arrayObject) _setLengthInt(l int64, throw bool) bool {
-	if l >= 0 && l <= math.MaxUint32 {
-		l := uint32(l)
-		ret := true
-		if l <= a.length {
-			if a.propValueCount > 0 {
-				// Slow path
-				for i := len(a.values) - 1; i >= int(l); i-- {
-					if prop, ok := a.values[i].(*valueProperty); ok {
-						if !prop.configurable {
-							l = uint32(i) + 1
-							ret = false
-							break
-						}
-						a.propValueCount--
+func (a *arrayObject) _setLengthInt(l uint32, throw bool) bool {
+	ret := true
+	if l <= a.length {
+		if a.propValueCount > 0 {
+			// Slow path
+			for i := len(a.values) - 1; i >= int(l); i-- {
+				if prop, ok := a.values[i].(*valueProperty); ok {
+					if !prop.configurable {
+						l = uint32(i) + 1
+						ret = false
+						break
 					}
+					a.propValueCount--
 				}
 			}
 		}
-		if l <= uint32(len(a.values)) {
-			if l >= 16 && l < uint32(cap(a.values))>>2 {
-				ar := make([]Value, l)
-				copy(ar, a.values)
-				a.values = ar
-			} else {
-				ar := a.values[l:len(a.values)]
-				for i := range ar {
-					ar[i] = nil
-				}
-				a.values = a.values[:l]
+	}
+	if l <= uint32(len(a.values)) {
+		if l >= 16 && l < uint32(cap(a.values))>>2 {
+			ar := make([]Value, l)
+			copy(ar, a.values)
+			a.values = ar
+		} else {
+			ar := a.values[l:len(a.values)]
+			for i := range ar {
+				ar[i] = nil
 			}
+			a.values = a.values[:l]
 		}
-		a.length = l
-		if !ret {
-			a.val.runtime.typeErrorResult(throw, "Cannot redefine property: length")
-		}
-		return ret
 	}
-	panic(a.val.runtime.newError(a.val.runtime.global.RangeError, "Invalid array length"))
+	a.length = l
+	if !ret {
+		a.val.runtime.typeErrorResult(throw, "Cannot redefine property: length")
+	}
+	return ret
 }
 
-func (a *arrayObject) setLengthInt(l int64, throw bool) bool {
-	if l == int64(a.length) {
+func (a *arrayObject) setLengthInt(l uint32, throw bool) bool {
+	if l == a.length {
 		return true
 	}
 	if !a.lengthProp.writable {
@@ -126,19 +125,15 @@ func (a *arrayObject) setLengthInt(l int64, throw bool) bool {
 	return a._setLengthInt(l, throw)
 }
 
-func (a *arrayObject) setLength(v Value, throw bool) bool {
-	l, ok := toIntIgnoreNegZero(v)
-	if ok && l == int64(a.length) {
+func (a *arrayObject) setLength(v uint32, throw bool) bool {
+	if v == a.length {
 		return true
 	}
 	if !a.lengthProp.writable {
 		a.val.runtime.typeErrorResult(throw, "length is not writable")
 		return false
 	}
-	if ok {
-		return a._setLengthInt(l, throw)
-	}
-	panic(a.val.runtime.newError(a.val.runtime.global.RangeError, "Invalid array length"))
+	return a._setLengthInt(v, throw)
 }
 
 func (a *arrayObject) getIdx(idx valueInt, receiver Value) Value {
@@ -237,7 +232,7 @@ func (a *arrayObject) _setOwnIdx(idx uint32, val Value, throw bool) bool {
 			return false
 		} else {
 			if idx >= a.length {
-				if !a.setLengthInt(int64(idx)+1, throw) {
+				if !a.setLengthInt(idx+1, throw) {
 					return false
 				}
 			}
@@ -268,7 +263,7 @@ func (a *arrayObject) setOwnStr(name unistring.String, val Value, throw bool) bo
 		return a._setOwnIdx(idx, val, throw)
 	} else {
 		if name == "length" {
-			return a.setLength(val, throw)
+			return a.setLength(a.val.runtime.toLengthUint32(val), throw)
 		} else {
 			return a.baseObject.setOwnStr(name, val, throw)
 		}
@@ -291,7 +286,7 @@ type arrayPropIter struct {
 
 func (i *arrayPropIter) next() (propIterItem, iterNextFunc) {
 	for i.idx < len(i.a.values) && i.idx < i.limit {
-		name := unistring.String(strconv.Itoa(i.idx))
+		name := asciiString(strconv.Itoa(i.idx))
 		prop := i.a.values[i.idx]
 		i.idx++
 		if prop != nil {
@@ -299,17 +294,17 @@ func (i *arrayPropIter) next() (propIterItem, iterNextFunc) {
 		}
 	}
 
-	return i.a.baseObject.enumerateOwnKeys()()
+	return i.a.baseObject.iterateStringKeys()()
 }
 
-func (a *arrayObject) enumerateOwnKeys() iterNextFunc {
+func (a *arrayObject) iterateStringKeys() iterNextFunc {
 	return (&arrayPropIter{
 		a:     a,
 		limit: len(a.values),
 	}).next
 }
 
-func (a *arrayObject) ownKeys(all bool, accum []Value) []Value {
+func (a *arrayObject) stringKeys(all bool, accum []Value) []Value {
 	for i, prop := range a.values {
 		name := strconv.Itoa(i)
 		if prop != nil {
@@ -321,7 +316,7 @@ func (a *arrayObject) ownKeys(all bool, accum []Value) []Value {
 			accum = append(accum, asciiString(name))
 		}
 	}
-	return a.baseObject.ownKeys(all, accum)
+	return a.baseObject.stringKeys(all, accum)
 }
 
 func (a *arrayObject) hasOwnPropertyStr(name unistring.String) bool {
@@ -373,15 +368,19 @@ func (a *arrayObject) expand(idx uint32) bool {
 	return true
 }
 
-func (r *Runtime) defineArrayLength(prop *valueProperty, descr PropertyDescriptor, setter func(Value, bool) bool, throw bool) bool {
+func (r *Runtime) defineArrayLength(prop *valueProperty, descr PropertyDescriptor, setter func(uint32, bool) bool, throw bool) bool {
+	var newLen uint32
 	ret := true
+	if descr.Value != nil {
+		newLen = r.toLengthUint32(descr.Value)
+	}
 
 	if descr.Configurable == FLAG_TRUE || descr.Enumerable == FLAG_TRUE || descr.Getter != nil || descr.Setter != nil {
 		ret = false
 		goto Reject
 	}
 
-	if newLen := descr.Value; newLen != nil {
+	if descr.Value != nil {
 		ret = setter(newLen, false)
 	} else {
 		ret = true
@@ -415,7 +414,7 @@ func (a *arrayObject) _defineIdxProperty(idx uint32, desc PropertyDescriptor, th
 	prop, ok := a.baseObject._defineOwnProperty(unistring.String(strconv.FormatUint(uint64(idx), 10)), existing, desc, throw)
 	if ok {
 		if idx >= a.length {
-			if !a.setLengthInt(int64(idx)+1, throw) {
+			if !a.setLengthInt(idx+1, throw) {
 				return false
 			}
 		}

+ 39 - 47
array_sparse.go

@@ -29,48 +29,44 @@ func (a *sparseArrayObject) findIdx(idx uint32) int {
 	})
 }
 
-func (a *sparseArrayObject) _setLengthInt(l int64, throw bool) bool {
-	if l >= 0 && l <= math.MaxUint32 {
-		ret := true
-		l := uint32(l)
-		if l <= a.length {
-			if a.propValueCount > 0 {
-				// Slow path
-				for i := len(a.items) - 1; i >= 0; i-- {
-					item := a.items[i]
-					if item.idx <= l {
+func (a *sparseArrayObject) _setLengthInt(l uint32, throw bool) bool {
+	ret := true
+	if l <= a.length {
+		if a.propValueCount > 0 {
+			// Slow path
+			for i := len(a.items) - 1; i >= 0; i-- {
+				item := a.items[i]
+				if item.idx <= l {
+					break
+				}
+				if prop, ok := item.value.(*valueProperty); ok {
+					if !prop.configurable {
+						l = item.idx + 1
+						ret = false
 						break
 					}
-					if prop, ok := item.value.(*valueProperty); ok {
-						if !prop.configurable {
-							l = item.idx + 1
-							ret = false
-							break
-						}
-						a.propValueCount--
-					}
+					a.propValueCount--
 				}
 			}
 		}
+	}
 
-		idx := a.findIdx(l)
+	idx := a.findIdx(l)
 
-		aa := a.items[idx:]
-		for i := range aa {
-			aa[i].value = nil
-		}
-		a.items = a.items[:idx]
-		a.length = l
-		if !ret {
-			a.val.runtime.typeErrorResult(throw, "Cannot redefine property: length")
-		}
-		return ret
+	aa := a.items[idx:]
+	for i := range aa {
+		aa[i].value = nil
+	}
+	a.items = a.items[:idx]
+	a.length = l
+	if !ret {
+		a.val.runtime.typeErrorResult(throw, "Cannot redefine property: length")
 	}
-	panic(a.val.runtime.newError(a.val.runtime.global.RangeError, "Invalid array length"))
+	return ret
 }
 
-func (a *sparseArrayObject) setLengthInt(l int64, throw bool) bool {
-	if l == int64(a.length) {
+func (a *sparseArrayObject) setLengthInt(l uint32, throw bool) bool {
+	if l == a.length {
 		return true
 	}
 	if !a.lengthProp.writable {
@@ -80,19 +76,15 @@ func (a *sparseArrayObject) setLengthInt(l int64, throw bool) bool {
 	return a._setLengthInt(l, throw)
 }
 
-func (a *sparseArrayObject) setLength(v Value, throw bool) bool {
-	l, ok := toIntIgnoreNegZero(v)
-	if ok && l == int64(a.length) {
+func (a *sparseArrayObject) setLength(v uint32, throw bool) bool {
+	if v == a.length {
 		return true
 	}
 	if !a.lengthProp.writable {
 		a.val.runtime.typeErrorResult(throw, "length is not writable")
 		return false
 	}
-	if ok {
-		return a._setLengthInt(l, throw)
-	}
-	panic(a.val.runtime.newError(a.val.runtime.global.RangeError, "Invalid array length"))
+	return a._setLengthInt(v, throw)
 }
 
 func (a *sparseArrayObject) _getIdx(idx uint32) Value {
@@ -181,7 +173,7 @@ func (a *sparseArrayObject) _setOwnIdx(idx uint32, val Value, throw bool) bool {
 		}
 
 		if idx >= a.length {
-			if !a.setLengthInt(int64(idx)+1, throw) {
+			if !a.setLengthInt(idx+1, throw) {
 				return false
 			}
 		}
@@ -218,7 +210,7 @@ func (a *sparseArrayObject) setOwnStr(name unistring.String, val Value, throw bo
 		return a._setOwnIdx(idx, val, throw)
 	} else {
 		if name == "length" {
-			return a.setLength(val, throw)
+			return a.setLength(a.val.runtime.toLengthUint32(val), throw)
 		} else {
 			return a.baseObject.setOwnStr(name, val, throw)
 		}
@@ -248,7 +240,7 @@ type sparseArrayPropIter struct {
 
 func (i *sparseArrayPropIter) next() (propIterItem, iterNextFunc) {
 	for i.idx < len(i.a.items) {
-		name := unistring.String(strconv.Itoa(int(i.a.items[i.idx].idx)))
+		name := asciiString(strconv.Itoa(int(i.a.items[i.idx].idx)))
 		prop := i.a.items[i.idx].value
 		i.idx++
 		if prop != nil {
@@ -256,16 +248,16 @@ func (i *sparseArrayPropIter) next() (propIterItem, iterNextFunc) {
 		}
 	}
 
-	return i.a.baseObject.enumerateOwnKeys()()
+	return i.a.baseObject.iterateStringKeys()()
 }
 
-func (a *sparseArrayObject) enumerateOwnKeys() iterNextFunc {
+func (a *sparseArrayObject) iterateStringKeys() iterNextFunc {
 	return (&sparseArrayPropIter{
 		a: a,
 	}).next
 }
 
-func (a *sparseArrayObject) ownKeys(all bool, accum []Value) []Value {
+func (a *sparseArrayObject) stringKeys(all bool, accum []Value) []Value {
 	if all {
 		for _, item := range a.items {
 			accum = append(accum, asciiString(strconv.FormatUint(uint64(item.idx), 10)))
@@ -279,7 +271,7 @@ func (a *sparseArrayObject) ownKeys(all bool, accum []Value) []Value {
 		}
 	}
 
-	return a.baseObject.ownKeys(all, accum)
+	return a.baseObject.stringKeys(all, accum)
 }
 
 func (a *sparseArrayObject) setValues(values []Value, objCount int) {
@@ -343,7 +335,7 @@ func (a *sparseArrayObject) _defineIdxProperty(idx uint32, desc PropertyDescript
 	prop, ok := a.baseObject._defineOwnProperty(unistring.String(strconv.FormatUint(uint64(idx), 10)), existing, desc, throw)
 	if ok {
 		if idx >= a.length {
-			if !a.setLengthInt(int64(idx)+1, throw) {
+			if !a.setLengthInt(idx+1, throw) {
 				return false
 			}
 		}

+ 20 - 13
ast/node.go

@@ -200,9 +200,10 @@ type (
 	}
 
 	PropertyKeyed struct {
-		Key   Expression
-		Kind  PropertyKind
-		Value Expression
+		Key      Expression
+		Kind     PropertyKind
+		Value    Expression
+		Computed bool
 	}
 
 	SpreadElement struct {
@@ -624,16 +625,22 @@ func (self *DotExpression) Idx1() file.Idx         { return self.Identifier.Idx1
 func (self *FunctionLiteral) Idx1() file.Idx       { return self.Body.Idx1() }
 func (self *ArrowFunctionLiteral) Idx1() file.Idx  { return self.Body.Idx1() }
 func (self *Identifier) Idx1() file.Idx            { return file.Idx(int(self.Idx) + len(self.Name)) }
-func (self *NewExpression) Idx1() file.Idx         { return self.RightParenthesis + 1 }
-func (self *NullLiteral) Idx1() file.Idx           { return file.Idx(int(self.Idx) + 4) } // "null"
-func (self *NumberLiteral) Idx1() file.Idx         { return file.Idx(int(self.Idx) + len(self.Literal)) }
-func (self *ObjectLiteral) Idx1() file.Idx         { return self.RightBrace + 1 }
-func (self *ObjectPattern) Idx1() file.Idx         { return self.RightBrace + 1 }
-func (self *RegExpLiteral) Idx1() file.Idx         { return file.Idx(int(self.Idx) + len(self.Literal)) }
-func (self *SequenceExpression) Idx1() file.Idx    { return self.Sequence[len(self.Sequence)-1].Idx1() }
-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 *NewExpression) Idx1() file.Idx {
+	if self.ArgumentList != nil {
+		return self.RightParenthesis + 1
+	} else {
+		return self.Callee.Idx1()
+	}
+}
+func (self *NullLiteral) Idx1() file.Idx        { return file.Idx(int(self.Idx) + 4) } // "null"
+func (self *NumberLiteral) Idx1() file.Idx      { return file.Idx(int(self.Idx) + len(self.Literal)) }
+func (self *ObjectLiteral) Idx1() file.Idx      { return self.RightBrace + 1 }
+func (self *ObjectPattern) Idx1() file.Idx      { return self.RightBrace + 1 }
+func (self *RegExpLiteral) Idx1() file.Idx      { return file.Idx(int(self.Idx) + len(self.Literal)) }
+func (self *SequenceExpression) Idx1() file.Idx { return self.Sequence[len(self.Sequence)-1].Idx1() }
+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 *UnaryExpression) Idx1() file.Idx {
 	if self.Postfix {
 		return self.Operand.Idx1() + 2 // ++ --

+ 2 - 2
builtin_array.go

@@ -1197,7 +1197,7 @@ func (r *Runtime) array_from(call FunctionCall) Value {
 		if mapFn == nil {
 			if a := r.checkStdArrayObj(arr); a != nil {
 				var values []Value
-				r.iterate(iter, func(val Value) {
+				iter.iterate(func(val Value) {
 					values = append(values, val)
 				})
 				setArrayValues(a, values)
@@ -1205,7 +1205,7 @@ func (r *Runtime) array_from(call FunctionCall) Value {
 			}
 		}
 		k := int64(0)
-		r.iterate(iter, func(val Value) {
+		iter.iterate(func(val Value) {
 			if mapFn != nil {
 				val = mapFn(FunctionCall{This: t, Arguments: []Value{val, intToValue(k)}})
 			}

+ 1 - 1
builtin_date.go

@@ -997,7 +997,7 @@ func (r *Runtime) createDateProto(val *Object) objectImpl {
 }
 
 func (r *Runtime) createDate(val *Object) objectImpl {
-	o := r.newNativeFuncObj(val, r.builtin_date, r.builtin_newDate, "Date", r.global.DatePrototype, 7)
+	o := r.newNativeFuncObj(val, r.builtin_date, r.builtin_newDate, "Date", r.global.DatePrototype, intToValue(7))
 
 	o._putProp("parse", r.newNativeFunc(r.date_parse, nil, "parse", nil, 1), true, false, true)
 	o._putProp("UTC", r.newNativeFunc(r.date_UTC, nil, "UTC", nil, 7), true, false, true)

+ 3 - 3
builtin_error.go

@@ -10,8 +10,8 @@ func (r *Runtime) builtin_Error(args []Value, proto *Object) *Object {
 
 func (r *Runtime) builtin_AggregateError(args []Value, proto *Object) *Object {
 	obj := r.newBaseObject(proto, classAggError)
-	if len(args) > 1 && args[1] != _undefined {
-		obj._putProp("message", args[1], true, false, true)
+	if len(args) > 1 && args[1] != nil && args[1] != _undefined {
+		obj._putProp("message", args[1].toString(), true, false, true)
 	}
 	var errors []Value
 	if len(args) > 0 {
@@ -40,7 +40,7 @@ func (r *Runtime) initErrors() {
 	r.addToGlobal("Error", r.global.Error)
 
 	r.global.AggregateErrorPrototype = r.createErrorPrototype(stringAggregateError)
-	r.global.AggregateError = r.newNativeFuncConstructProto(r.builtin_AggregateError, "AggregateError", r.global.AggregateErrorPrototype, r.global.Error, 1)
+	r.global.AggregateError = r.newNativeFuncConstructProto(r.builtin_AggregateError, "AggregateError", r.global.AggregateErrorPrototype, r.global.Error, 2)
 	r.addToGlobal("AggregateError", r.global.AggregateError)
 
 	r.global.TypeErrorPrototype = r.createErrorPrototype(stringTypeError)

+ 37 - 26
builtin_function.go

@@ -2,6 +2,7 @@ package goja
 
 import (
 	"fmt"
+	"math"
 )
 
 func (r *Runtime) builtin_Function(args []Value, proto *Object) *Object {
@@ -16,11 +17,11 @@ func (r *Runtime) builtin_Function(args []Value, proto *Object) *Object {
 			}
 		}
 	}
-	sb.WriteString(asciiString("){"))
+	sb.WriteString(asciiString("\n) {\n"))
 	if len(args) > 0 {
 		sb.WriteString(args[len(args)-1].toString())
 	}
-	sb.WriteString(asciiString("})"))
+	sb.WriteString(asciiString("\n})"))
 
 	ret := r.toObject(r.eval(sb.String(), false, false, _undefined))
 	ret.self.setProto(proto, true)
@@ -31,6 +32,8 @@ 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 *arrowFuncObject:
@@ -43,28 +46,16 @@ repeat:
 		obj.self = f.create(obj)
 		goto repeat
 	case *proxyObject:
-		var name string
 	repeat2:
 		switch c := f.target.self.(type) {
-		case *funcObject:
-			name = c.src
-		case *arrowFuncObject:
-			name = c.src
-		case *nativeFuncObject:
-			name = nilSafe(f.getStr("name", nil)).toString().String()
-		case *boundFuncObject:
-			name = nilSafe(f.getStr("name", nil)).toString().String()
+		case *methodFuncObject, *funcObject, *arrowFuncObject, *nativeFuncObject, *boundFuncObject:
+			return asciiString("function () { [native code] }")
 		case *lazyObject:
 			f.target.self = c.create(obj)
 			goto repeat2
-		default:
-			name = f.target.String()
 		}
-		return newStringValue(fmt.Sprintf("function proxy() { [%s] }", name))
 	}
-
-	r.typeErrorResult(true, "Object is not a function")
-	return nil
+	panic(r.NewTypeError("Function.prototype.toString requires that 'this' be a Function"))
 }
 
 func (r *Runtime) functionproto_hasInstance(call FunctionCall) Value {
@@ -157,12 +148,36 @@ func (r *Runtime) functionproto_bind(call FunctionCall) Value {
 	fcall := r.toCallable(call.This)
 	construct := obj.self.assertConstructor()
 
-	l := int(toUint32(nilSafe(obj.self.getStr("length", nil))))
-	l -= len(call.Arguments) - 1
-	if l < 0 {
-		l = 0
+	var l = _positiveZero
+	if obj.self.hasOwnPropertyStr("length") {
+		var li int64
+		switch lenProp := nilSafe(obj.self.getStr("length", nil)).(type) {
+		case valueInt:
+			li = lenProp.ToInteger()
+		case valueFloat:
+			switch lenProp {
+			case _positiveInf:
+				l = lenProp
+				goto lenNotInt
+			case _negativeInf:
+				goto lenNotInt
+			case _negativeZero:
+				// no-op, li == 0
+			default:
+				if !math.IsNaN(float64(lenProp)) {
+					li = int64(math.Abs(float64(lenProp)))
+				} // else li = 0
+			}
+		}
+		if len(call.Arguments) > 1 {
+			li -= int64(len(call.Arguments)) - 1
+		}
+		if li < 0 {
+			li = 0
+		}
+		l = intToValue(li)
 	}
-
+lenNotInt:
 	name := obj.self.getStr("name", nil)
 	nameStr := stringBound_
 	if s, ok := name.(valueString); ok {
@@ -177,10 +192,6 @@ func (r *Runtime) functionproto_bind(call FunctionCall) Value {
 		wrapped:          obj,
 	}
 
-	//ret := r.newNativeFunc(r.boundCallable(f, call.Arguments), nil, "", nil, l)
-	//o := ret.self
-	//o.putStr("caller", r.global.throwerProperty, false)
-	//o.putStr("arguments", r.global.throwerProperty, false)
 	return v
 }
 

+ 29 - 19
builtin_json.go

@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"io"
 	"math"
+	"strconv"
 	"strings"
 	"unicode/utf16"
 
@@ -34,7 +35,7 @@ func (r *Runtime) builtinJSON_parse(call FunctionCall) Value {
 
 	if reviver != nil {
 		root := r.NewObject()
-		root.self.setOwnStr("", value, false)
+		createDataPropertyOrThrow(root, stringEmpty, value)
 		return r.builtinJSON_reviveWalk(reviver, root, stringEmpty)
 	}
 
@@ -138,24 +139,21 @@ func (r *Runtime) builtinJSON_reviveWalk(reviver func(FunctionCall) Value, holde
 		if isArray(object) {
 			length := toLength(object.self.getStr("length", nil))
 			for index := int64(0); index < length; index++ {
-				name := intToValue(index)
+				name := asciiString(strconv.FormatInt(index, 10))
 				value := r.builtinJSON_reviveWalk(reviver, object, name)
 				if value == _undefined {
 					object.delete(name, false)
 				} else {
-					object.setOwn(name, value, false)
+					createDataProperty(object, name, value)
 				}
 			}
 		} else {
-			iter := &enumerableIter{
-				wrapped: object.self.enumerateOwnKeys(),
-			}
-			for item, next := iter.next(); next != nil; item, next = next() {
-				value := r.builtinJSON_reviveWalk(reviver, object, stringValueFromRaw(item.name))
+			for _, name := range object.self.stringKeys(false, nil) {
+				value := r.builtinJSON_reviveWalk(reviver, object, name)
 				if value == _undefined {
-					object.self.deleteStr(item.name, false)
+					object.self.deleteStr(name.string(), false)
 				} else {
-					object.self.setOwnStr(item.name, value, false)
+					createDataProperty(object, name, value)
 				}
 			}
 		}
@@ -197,14 +195,18 @@ func (r *Runtime) builtinJSON_stringify(call FunctionCall) Value {
 					switch v.self.className() {
 					case classNumber, classString:
 						name = value.String()
+					default:
+						continue
 					}
+				default:
+					continue
 				}
 				if seen[name] {
 					continue
 				}
 				seen[name] = true
+				propertyList[length] = newStringValue(name)
 				length += 1
-				propertyList[index] = newStringValue(name)
 			}
 			ctx.propertyList = propertyList[0:length]
 		} else if c, ok := replacer.self.assertCallable(); ok {
@@ -213,11 +215,14 @@ func (r *Runtime) builtinJSON_stringify(call FunctionCall) Value {
 	}
 	if spaceValue := call.Argument(2); spaceValue != _undefined {
 		if o, ok := spaceValue.(*Object); ok {
-			switch o := o.self.(type) {
+			switch oImpl := o.self.(type) {
 			case *primitiveValueObject:
-				spaceValue = o.pValue
+				switch oImpl.pValue.(type) {
+				case valueInt, valueFloat:
+					spaceValue = o.ToNumber()
+				}
 			case *stringObject:
-				spaceValue = o.value
+				spaceValue = o.ToString()
 			}
 		}
 		isNum := false
@@ -256,7 +261,7 @@ func (r *Runtime) builtinJSON_stringify(call FunctionCall) Value {
 
 func (ctx *_builtinJSON_stringifyContext) do(v Value) bool {
 	holder := ctx.r.NewObject()
-	holder.self.setOwnStr("", v, false)
+	createDataPropertyOrThrow(holder, stringEmpty, v)
 	return ctx.str(stringEmpty, holder)
 }
 
@@ -284,9 +289,14 @@ func (ctx *_builtinJSON_stringifyContext) str(key Value, holder *Object) bool {
 	if o, ok := value.(*Object); ok {
 		switch o1 := o.self.(type) {
 		case *primitiveValueObject:
-			value = o1.pValue
+			switch pValue := o1.pValue.(type) {
+			case valueInt, valueFloat:
+				value = o.ToNumber()
+			default:
+				value = pValue
+			}
 		case *stringObject:
-			value = o1.value
+			value = o.toString()
 		case *objectGoReflect:
 			if o1.toJson != nil {
 				value = ctx.r.ToValue(o1.toJson())
@@ -379,7 +389,7 @@ func (ctx *_builtinJSON_stringifyContext) ja(array *Object) {
 	}
 
 	for i := int64(0); i < length; i++ {
-		if !ctx.str(intToValue(i), array) {
+		if !ctx.str(asciiString(strconv.FormatInt(i, 10)), array) {
 			ctx.buf.WriteString("null")
 		}
 		if i < length-1 {
@@ -414,7 +424,7 @@ func (ctx *_builtinJSON_stringifyContext) jo(object *Object) {
 
 	var props []Value
 	if ctx.propertyList == nil {
-		props = object.self.ownKeys(false, nil)
+		props = object.self.stringKeys(false, nil)
 	} else {
 		props = ctx.propertyList
 	}

+ 2 - 2
builtin_map.go

@@ -159,7 +159,7 @@ func (r *Runtime) builtin_newMap(args []Value, newTarget *Object) *Object {
 			i0 := valueInt(0)
 			i1 := valueInt(1)
 			if adder == r.global.mapAdder {
-				r.iterate(iter, func(item Value) {
+				iter.iterate(func(item Value) {
 					itemObj := r.toObject(item)
 					k := nilSafe(itemObj.self.getIdx(i0, nil))
 					v := nilSafe(itemObj.self.getIdx(i1, nil))
@@ -170,7 +170,7 @@ func (r *Runtime) builtin_newMap(args []Value, newTarget *Object) *Object {
 				if adderFn == nil {
 					panic(r.NewTypeError("Map.set in missing"))
 				}
-				r.iterate(iter, func(item Value) {
+				iter.iterate(func(item Value) {
 					itemObj := r.toObject(item)
 					k := itemObj.self.getIdx(i0, nil)
 					v := itemObj.self.getIdx(i1, nil)

+ 41 - 28
builtin_math.go

@@ -138,41 +138,47 @@ func (r *Runtime) math_log2(call FunctionCall) Value {
 }
 
 func (r *Runtime) math_max(call FunctionCall) Value {
-	if len(call.Arguments) == 0 {
-		return _negativeInf
-	}
-
-	result := call.Arguments[0].ToFloat()
-	if math.IsNaN(result) {
-		return _NaN
-	}
-	for _, arg := range call.Arguments[1:] {
-		f := arg.ToFloat()
-		if math.IsNaN(f) {
-			return _NaN
+	result := math.Inf(-1)
+	args := call.Arguments
+	for i, arg := range args {
+		n := nilSafe(arg).ToFloat()
+		if math.IsNaN(n) {
+			args = args[i+1:]
+			goto NaNLoop
 		}
-		result = math.Max(result, f)
+		result = math.Max(result, n)
 	}
+
 	return floatToValue(result)
-}
 
-func (r *Runtime) math_min(call FunctionCall) Value {
-	if len(call.Arguments) == 0 {
-		return _positiveInf
+NaNLoop:
+	// All arguments still need to be coerced to number according to the specs.
+	for _, arg := range args {
+		nilSafe(arg).ToFloat()
 	}
+	return _NaN
+}
 
-	result := call.Arguments[0].ToFloat()
-	if math.IsNaN(result) {
-		return _NaN
-	}
-	for _, arg := range call.Arguments[1:] {
-		f := arg.ToFloat()
-		if math.IsNaN(f) {
-			return _NaN
+func (r *Runtime) math_min(call FunctionCall) Value {
+	result := math.Inf(1)
+	args := call.Arguments
+	for i, arg := range args {
+		n := nilSafe(arg).ToFloat()
+		if math.IsNaN(n) {
+			args = args[i+1:]
+			goto NaNLoop
 		}
-		result = math.Min(result, f)
+		result = math.Min(result, n)
 	}
+
 	return floatToValue(result)
+
+NaNLoop:
+	// All arguments still need to be coerced to number according to the specs.
+	for _, arg := range args {
+		nilSafe(arg).ToFloat()
+	}
+	return _NaN
 }
 
 func (r *Runtime) math_pow(call FunctionCall) Value {
@@ -192,8 +198,15 @@ func (r *Runtime) math_pow(call FunctionCall) Value {
 			}
 		}
 	}
-
-	return floatToValue(math.Pow(x.ToFloat(), y.ToFloat()))
+	xf := x.ToFloat()
+	yf := y.ToFloat()
+	if math.Abs(xf) == 1 && math.IsInf(yf, 0) {
+		return _NaN
+	}
+	if xf == 1 && math.IsNaN(yf) {
+		return _NaN
+	}
+	return floatToValue(math.Pow(xf, yf))
 }
 
 func (r *Runtime) math_random(call FunctionCall) Value {

+ 80 - 71
builtin_object.go

@@ -76,12 +76,20 @@ func (r *Runtime) object_getOwnPropertyDescriptor(call FunctionCall) Value {
 
 func (r *Runtime) object_getOwnPropertyDescriptors(call FunctionCall) Value {
 	o := call.Argument(0).ToObject(r)
-	ownKeys := o.self.ownPropertyKeys(true, nil)
 	result := r.newBaseObject(r.global.ObjectPrototype, classObject).val
-	for _, key := range ownKeys {
-		descriptor := r.valuePropToDescriptorObject(o.getOwnProp(key))
+	for item, next := o.self.iterateKeys()(); next != nil; item, next = next() {
+		var prop Value
+		if item.value == nil {
+			prop = o.getOwnProp(item.name)
+			if prop == nil {
+				continue
+			}
+		} else {
+			prop = item.value
+		}
+		descriptor := r.valuePropToDescriptorObject(prop)
 		if descriptor != _undefined {
-			createDataPropertyOrThrow(result, key, descriptor)
+			createDataPropertyOrThrow(result, item.name, descriptor)
 		}
 	}
 	return result
@@ -90,12 +98,12 @@ func (r *Runtime) object_getOwnPropertyDescriptors(call FunctionCall) Value {
 func (r *Runtime) object_getOwnPropertyNames(call FunctionCall) Value {
 	obj := call.Argument(0).ToObject(r)
 
-	return r.newArrayValues(obj.self.ownKeys(true, nil))
+	return r.newArrayValues(obj.self.stringKeys(true, nil))
 }
 
 func (r *Runtime) object_getOwnPropertySymbols(call FunctionCall) Value {
 	obj := call.Argument(0).ToObject(r)
-	return r.newArrayValues(obj.self.ownSymbols(true, nil))
+	return r.newArrayValues(obj.self.symbols(true, nil))
 }
 
 func (r *Runtime) toValueProp(v Value) *valueProperty {
@@ -132,7 +140,7 @@ func (r *Runtime) toValueProp(v Value) *valueProperty {
 	}
 
 	if setter != nil && setter != _undefined {
-		o := r.toObject(v)
+		o := r.toObject(setter)
 		if _, ok := o.self.assertCallable(); !ok {
 			r.typeErrorResult(true, "setter must be a function")
 		}
@@ -196,12 +204,11 @@ func (r *Runtime) _defineProperties(o *Object, p Value) {
 		prop PropertyDescriptor
 	}
 	props := p.ToObject(r)
-	names := props.self.ownPropertyKeys(false, nil)
-	list := make([]propItem, 0, len(names))
-	for _, itemName := range names {
+	var list []propItem
+	for item, next := iterateEnumerableProperties(props)(); next != nil; item, next = next() {
 		list = append(list, propItem{
-			name: itemName,
-			prop: r.toPropertyDescriptor(props.get(itemName, nil)),
+			name: item.name,
+			prop: r.toPropertyDescriptor(item.value),
 		})
 	}
 	for _, prop := range list {
@@ -248,24 +255,19 @@ func (r *Runtime) object_seal(call FunctionCall) Value {
 	// ES6
 	arg := call.Argument(0)
 	if obj, ok := arg.(*Object); ok {
+		obj.self.preventExtensions(true)
 		descr := PropertyDescriptor{
-			Writable:     FLAG_TRUE,
-			Enumerable:   FLAG_TRUE,
 			Configurable: FLAG_FALSE,
 		}
-		for _, key := range obj.self.ownPropertyKeys(true, nil) {
-			v := obj.getOwnProp(key)
-			if prop, ok := v.(*valueProperty); ok {
-				if !prop.configurable {
-					continue
-				}
+
+		for item, next := obj.self.iterateKeys()(); next != nil; item, next = next() {
+			if prop, ok := item.value.(*valueProperty); ok {
 				prop.configurable = false
 			} else {
-				descr.Value = v
-				obj.defineOwnProperty(key, descr, true)
+				obj.defineOwnProperty(item.name, descr, true)
 			}
 		}
-		obj.self.preventExtensions(false)
+
 		return obj
 	}
 	return arg
@@ -274,24 +276,27 @@ func (r *Runtime) object_seal(call FunctionCall) Value {
 func (r *Runtime) object_freeze(call FunctionCall) Value {
 	arg := call.Argument(0)
 	if obj, ok := arg.(*Object); ok {
-		descr := PropertyDescriptor{
-			Writable:     FLAG_FALSE,
-			Enumerable:   FLAG_TRUE,
-			Configurable: FLAG_FALSE,
-		}
-		for _, key := range obj.self.ownPropertyKeys(true, nil) {
-			v := obj.getOwnProp(key)
-			if prop, ok := v.(*valueProperty); ok {
+		obj.self.preventExtensions(true)
+
+		for item, next := obj.self.iterateKeys()(); next != nil; item, next = next() {
+			if prop, ok := item.value.(*valueProperty); ok {
 				prop.configurable = false
-				if prop.value != nil {
+				if !prop.accessor {
 					prop.writable = false
 				}
 			} else {
-				descr.Value = v
-				obj.defineOwnProperty(key, descr, true)
+				prop := obj.getOwnProp(item.name)
+				descr := PropertyDescriptor{
+					Configurable: FLAG_FALSE,
+				}
+				if prop, ok := prop.(*valueProperty); ok && prop.accessor {
+					// no-op
+				} else {
+					descr.Writable = FLAG_FALSE
+				}
+				obj.defineOwnProperty(item.name, descr, true)
 			}
 		}
-		obj.self.preventExtensions(false)
 		return obj
 	} else {
 		// ES6 behavior
@@ -302,12 +307,8 @@ func (r *Runtime) object_freeze(call FunctionCall) Value {
 func (r *Runtime) object_preventExtensions(call FunctionCall) (ret Value) {
 	arg := call.Argument(0)
 	if obj, ok := arg.(*Object); ok {
-		obj.self.preventExtensions(false)
-		return obj
+		obj.self.preventExtensions(true)
 	}
-	// ES6
-	//r.typeErrorResult(true, "Object.preventExtensions called on non-object")
-	//panic("Unreachable")
 	return arg
 }
 
@@ -316,8 +317,16 @@ func (r *Runtime) object_isSealed(call FunctionCall) Value {
 		if obj.self.isExtensible() {
 			return valueFalse
 		}
-		for _, key := range obj.self.ownPropertyKeys(true, nil) {
-			prop := obj.getOwnProp(key)
+		for item, next := obj.self.iterateKeys()(); next != nil; item, next = next() {
+			var prop Value
+			if item.value == nil {
+				prop = obj.getOwnProp(item.name)
+				if prop == nil {
+					continue
+				}
+			} else {
+				prop = item.value
+			}
 			if prop, ok := prop.(*valueProperty); ok {
 				if prop.configurable {
 					return valueFalse
@@ -335,8 +344,16 @@ func (r *Runtime) object_isFrozen(call FunctionCall) Value {
 		if obj.self.isExtensible() {
 			return valueFalse
 		}
-		for _, key := range obj.self.ownPropertyKeys(true, nil) {
-			prop := obj.getOwnProp(key)
+		for item, next := obj.self.iterateKeys()(); next != nil; item, next = next() {
+			var prop Value
+			if item.value == nil {
+				prop = obj.getOwnProp(item.name)
+				if prop == nil {
+					continue
+				}
+			} else {
+				prop = item.value
+			}
 			if prop, ok := prop.(*valueProperty); ok {
 				if prop.configurable || prop.value != nil && prop.writable {
 					return valueFalse
@@ -365,20 +382,16 @@ func (r *Runtime) object_isExtensible(call FunctionCall) Value {
 func (r *Runtime) object_keys(call FunctionCall) Value {
 	obj := call.Argument(0).ToObject(r)
 
-	return r.newArrayValues(obj.self.ownKeys(false, nil))
+	return r.newArrayValues(obj.self.stringKeys(false, nil))
 }
 
 func (r *Runtime) object_entries(call FunctionCall) Value {
 	obj := call.Argument(0).ToObject(r)
 
 	var values []Value
-	iter := &enumerableIter{
-		wrapped: obj.self.enumerateOwnKeys(),
-	}
 
-	for item, next := iter.next(); next != nil; item, next = next() {
-		v := nilSafe(obj.self.getStr(item.name, nil))
-		values = append(values, r.newArrayValues([]Value{stringValueFromRaw(item.name), v}))
+	for item, next := iterateEnumerableStringProperties(obj)(); next != nil; item, next = next() {
+		values = append(values, r.newArrayValues([]Value{item.name, item.value}))
 	}
 
 	return r.newArrayValues(values)
@@ -388,12 +401,9 @@ func (r *Runtime) object_values(call FunctionCall) Value {
 	obj := call.Argument(0).ToObject(r)
 
 	var values []Value
-	iter := &enumerableIter{
-		wrapped: obj.self.enumerateOwnKeys(),
-	}
 
-	for item, next := iter.next(); next != nil; item, next = next() {
-		values = append(values, nilSafe(obj.self.getStr(item.name, nil)))
+	for item, next := iterateEnumerableStringProperties(obj)(); next != nil; item, next = next() {
+		values = append(values, item.value)
 	}
 
 	return r.newArrayValues(values)
@@ -476,14 +486,23 @@ func (r *Runtime) objectproto_getProto(call FunctionCall) Value {
 	return _null
 }
 
-func (r *Runtime) objectproto_setProto(call FunctionCall) Value {
-	o := call.This
+func (r *Runtime) setObjectProto(o, arg Value) {
 	r.checkObjectCoercible(o)
-	proto := r.toProto(call.Argument(0))
+	var proto *Object
+	if arg != _null {
+		if obj, ok := arg.(*Object); ok {
+			proto = obj
+		} else {
+			return
+		}
+	}
 	if o, ok := o.(*Object); ok {
 		o.self.setProto(proto, true)
 	}
+}
 
+func (r *Runtime) objectproto_setProto(call FunctionCall) Value {
+	r.setObjectProto(call.This, call.Argument(0))
 	return _undefined
 }
 
@@ -497,18 +516,8 @@ func (r *Runtime) object_assign(call FunctionCall) Value {
 		for _, arg := range call.Arguments[1:] {
 			if arg != _undefined && arg != _null {
 				source := arg.ToObject(r)
-				for _, key := range source.self.ownPropertyKeys(true, nil) {
-					p := source.getOwnProp(key)
-					if p == nil {
-						continue
-					}
-					if v, ok := p.(*valueProperty); ok {
-						if !v.enumerable {
-							continue
-						}
-						p = v.get(source)
-					}
-					to.setOwn(key, p, true)
+				for item, next := iterateEnumerableProperties(source)(); next != nil; item, next = next() {
+					to.setOwn(item.name, item.value, true)
 				}
 			}
 		}

+ 4 - 4
builtin_promise.go

@@ -390,7 +390,7 @@ func (r *Runtime) promise_all(call FunctionCall) Value {
 		iter := r.getIterator(call.Argument(0), nil)
 		var values []Value
 		remainingElementsCount := 1
-		r.iterate(iter, func(nextValue Value) {
+		iter.iterate(func(nextValue Value) {
 			index := len(values)
 			values = append(values, _undefined)
 			nextPromise := promiseResolve(FunctionCall{This: c, Arguments: []Value{nextValue}})
@@ -427,7 +427,7 @@ func (r *Runtime) promise_allSettled(call FunctionCall) Value {
 		iter := r.getIterator(call.Argument(0), nil)
 		var values []Value
 		remainingElementsCount := 1
-		r.iterate(iter, func(nextValue Value) {
+		iter.iterate(func(nextValue Value) {
 			index := len(values)
 			values = append(values, _undefined)
 			nextPromise := promiseResolve(FunctionCall{This: c, Arguments: []Value{nextValue}})
@@ -471,7 +471,7 @@ func (r *Runtime) promise_any(call FunctionCall) Value {
 		iter := r.getIterator(call.Argument(0), nil)
 		var errors []Value
 		remainingElementsCount := 1
-		r.iterate(iter, func(nextValue Value) {
+		iter.iterate(func(nextValue Value) {
 			index := len(errors)
 			errors = append(errors, _undefined)
 			nextPromise := promiseResolve(FunctionCall{This: c, Arguments: []Value{nextValue}})
@@ -511,7 +511,7 @@ func (r *Runtime) promise_race(call FunctionCall) Value {
 	pcap.try(func() {
 		promiseResolve := r.toCallable(c.self.getStr("resolve", nil))
 		iter := r.getIterator(call.Argument(0), nil)
-		r.iterate(iter, func(nextValue Value) {
+		iter.iterate(func(nextValue Value) {
 			nextPromise := promiseResolve(FunctionCall{This: c, Arguments: []Value{nextValue}})
 			r.invoke(nextPromise, "then", pcap.resolveObj, pcap.rejectObj)
 		})

+ 7 - 2
builtin_proxy_test.go

@@ -167,7 +167,8 @@ func TestProxy_proxy_preventExtensions(t *testing.T) {
 	var proxy = new Proxy(obj, {
 		preventExtensions: function(target) {
 			target.canEvolve = false;
-			return false;
+			Object.preventExtensions(obj);
+			return true;
 		}
 	});
 	Object.preventExtensions(proxy);
@@ -194,7 +195,11 @@ func TestProxy_native_proxy_preventExtensions(t *testing.T) {
 	proxy := runtime.NewProxy(target, &ProxyTrapConfig{
 		PreventExtensions: func(target *Object) (success bool) {
 			target.Set("canEvolve", false)
-			return false
+			_, err := runtime.RunString("Object.preventExtensions(target)")
+			if err != nil {
+				panic(err)
+			}
+			return true
 		},
 	})
 	runtime.Set("proxy", proxy)

+ 1 - 1
builtin_reflect.go

@@ -79,7 +79,7 @@ func (r *Runtime) builtin_reflect_isExtensible(call FunctionCall) Value {
 
 func (r *Runtime) builtin_reflect_ownKeys(call FunctionCall) Value {
 	target := r.toObject(call.Argument(0))
-	return r.newArrayValues(target.self.ownPropertyKeys(true, nil))
+	return r.newArrayValues(target.self.keys(true, nil))
 }
 
 func (r *Runtime) builtin_reflect_preventExtensions(call FunctionCall) Value {

+ 18 - 12
builtin_regexp.go

@@ -501,9 +501,10 @@ func (r *Runtime) regexpproto_getSource(call FunctionCall) Value {
 			return sb.String()
 		}
 		return this.source
+	} else if call.This == r.global.RegExpPrototype {
+		return asciiString("(?:)")
 	} else {
-		r.typeErrorResult(true, "Method RegExp.prototype.source getter called on incompatible receiver")
-		return nil
+		panic(r.NewTypeError("Method RegExp.prototype.source getter called on incompatible receiver"))
 	}
 }
 
@@ -514,9 +515,10 @@ func (r *Runtime) regexpproto_getGlobal(call FunctionCall) Value {
 		} else {
 			return valueFalse
 		}
+	} else if call.This == r.global.RegExpPrototype {
+		return _undefined
 	} else {
-		r.typeErrorResult(true, "Method RegExp.prototype.global getter called on incompatible receiver %s", call.This.toString())
-		return nil
+		panic(r.NewTypeError("Method RegExp.prototype.global getter called on incompatible receiver %s", call.This.toString()))
 	}
 }
 
@@ -527,9 +529,10 @@ func (r *Runtime) regexpproto_getMultiline(call FunctionCall) Value {
 		} else {
 			return valueFalse
 		}
+	} else if call.This == r.global.RegExpPrototype {
+		return _undefined
 	} else {
-		r.typeErrorResult(true, "Method RegExp.prototype.multiline getter called on incompatible receiver %s", call.This.toString())
-		return nil
+		panic(r.NewTypeError("Method RegExp.prototype.multiline getter called on incompatible receiver %s", call.This.toString()))
 	}
 }
 
@@ -540,9 +543,10 @@ func (r *Runtime) regexpproto_getIgnoreCase(call FunctionCall) Value {
 		} else {
 			return valueFalse
 		}
+	} else if call.This == r.global.RegExpPrototype {
+		return _undefined
 	} else {
-		r.typeErrorResult(true, "Method RegExp.prototype.ignoreCase getter called on incompatible receiver %s", call.This.toString())
-		return nil
+		panic(r.NewTypeError("Method RegExp.prototype.ignoreCase getter called on incompatible receiver %s", call.This.toString()))
 	}
 }
 
@@ -553,9 +557,10 @@ func (r *Runtime) regexpproto_getUnicode(call FunctionCall) Value {
 		} else {
 			return valueFalse
 		}
+	} else if call.This == r.global.RegExpPrototype {
+		return _undefined
 	} else {
-		r.typeErrorResult(true, "Method RegExp.prototype.unicode getter called on incompatible receiver %s", call.This.toString())
-		return nil
+		panic(r.NewTypeError("Method RegExp.prototype.unicode getter called on incompatible receiver %s", call.This.toString()))
 	}
 }
 
@@ -566,9 +571,10 @@ func (r *Runtime) regexpproto_getSticky(call FunctionCall) Value {
 		} else {
 			return valueFalse
 		}
+	} else if call.This == r.global.RegExpPrototype {
+		return _undefined
 	} else {
-		r.typeErrorResult(true, "Method RegExp.prototype.sticky getter called on incompatible receiver %s", call.This.toString())
-		return nil
+		panic(r.NewTypeError("Method RegExp.prototype.sticky getter called on incompatible receiver %s", call.This.toString()))
 	}
 }
 

+ 2 - 2
builtin_set.go

@@ -140,7 +140,7 @@ func (r *Runtime) builtin_newSet(args []Value, newTarget *Object) *Object {
 			adder := so.getStr("add", nil)
 			iter := r.getIterator(arg, nil)
 			if adder == r.global.setAdder {
-				r.iterate(iter, func(item Value) {
+				iter.iterate(func(item Value) {
 					so.m.set(item, nil)
 				})
 			} else {
@@ -148,7 +148,7 @@ func (r *Runtime) builtin_newSet(args []Value, newTarget *Object) *Object {
 				if adderFn == nil {
 					panic(r.NewTypeError("Set.add in missing"))
 				}
-				r.iterate(iter, func(item Value) {
+				iter.iterate(func(item Value) {
 					adderFn(FunctionCall{This: o, Arguments: []Value{item}})
 				})
 			}

+ 24 - 1
builtin_symbol.go

@@ -87,6 +87,20 @@ func (r *Runtime) symbol_keyfor(call FunctionCall) Value {
 	return _undefined
 }
 
+func (r *Runtime) thisSymbolValue(v Value) *Symbol {
+	if sym, ok := v.(*Symbol); ok {
+		return sym
+	}
+	if obj, ok := v.(*Object); ok {
+		if pVal, ok := obj.self.(*primitiveValueObject); ok {
+			if sym, ok := pVal.pValue.(*Symbol); ok {
+				return sym
+			}
+		}
+	}
+	panic(r.NewTypeError("Value is not a Symbol"))
+}
+
 func (r *Runtime) createSymbolProto(val *Object) objectImpl {
 	o := &baseObject{
 		class:      classObject,
@@ -97,6 +111,13 @@ func (r *Runtime) createSymbolProto(val *Object) objectImpl {
 	o.init()
 
 	o._putProp("constructor", r.global.Symbol, true, false, true)
+	o.setOwnStr("description", &valueProperty{
+		configurable: true,
+		getterFunc: r.newNativeFunc(func(call FunctionCall) Value {
+			return r.thisSymbolValue(call.This).desc
+		}, nil, "get description", nil, 0),
+		accessor: true,
+	}, false)
 	o._putProp("toString", r.newNativeFunc(r.symbolproto_tostring, nil, "toString", nil, 0), true, false, true)
 	o._putProp("valueOf", r.newNativeFunc(r.symbolproto_valueOf, nil, "valueOf", nil, 0), true, false, true)
 	o._putSym(SymToPrimitive, valueProp(r.newNativeFunc(r.symbolproto_valueOf, nil, "[Symbol.toPrimitive]", nil, 1), false, false, true))
@@ -106,7 +127,9 @@ func (r *Runtime) createSymbolProto(val *Object) objectImpl {
 }
 
 func (r *Runtime) createSymbol(val *Object) objectImpl {
-	o := r.newNativeFuncObj(val, r.builtin_symbol, nil, "Symbol", r.global.SymbolPrototype, 0)
+	o := r.newNativeFuncObj(val, r.builtin_symbol, func(args []Value, proto *Object) *Object {
+		panic(r.NewTypeError("Symbol is not a constructor"))
+	}, "Symbol", r.global.SymbolPrototype, _positiveZero)
 
 	o._putProp("for", r.newNativeFunc(r.symbol_for, nil, "for", nil, 1), true, false, true)
 	o._putProp("keyFor", r.newNativeFunc(r.symbol_keyfor, nil, "keyFor", nil, 1), true, false, true)

+ 220 - 125
builtin_typedarrays.go

@@ -218,21 +218,21 @@ func (r *Runtime) dataViewProto_getByteOffset(call FunctionCall) Value {
 
 func (r *Runtime) dataViewProto_getFloat32(call FunctionCall) Value {
 	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
-		return floatToValue(float64(dv.viewedArrayBuf.getFloat32(dv.getIdxAndByteOrder(call.Argument(0), call.Argument(1), 4))))
+		return floatToValue(float64(dv.viewedArrayBuf.getFloat32(dv.getIdxAndByteOrder(r.toIndex(call.Argument(0)), call.Argument(1), 4))))
 	}
 	panic(r.NewTypeError("Method DataView.prototype.getFloat32 called on incompatible receiver %s", call.This.String()))
 }
 
 func (r *Runtime) dataViewProto_getFloat64(call FunctionCall) Value {
 	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
-		return floatToValue(dv.viewedArrayBuf.getFloat64(dv.getIdxAndByteOrder(call.Argument(0), call.Argument(1), 8)))
+		return floatToValue(dv.viewedArrayBuf.getFloat64(dv.getIdxAndByteOrder(r.toIndex(call.Argument(0)), call.Argument(1), 8)))
 	}
 	panic(r.NewTypeError("Method DataView.prototype.getFloat64 called on incompatible receiver %s", call.This.String()))
 }
 
 func (r *Runtime) dataViewProto_getInt8(call FunctionCall) Value {
 	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
-		idx, _ := dv.getIdxAndByteOrder(call.Argument(0), call.Argument(1), 1)
+		idx, _ := dv.getIdxAndByteOrder(r.toIndex(call.Argument(0)), call.Argument(1), 1)
 		return intToValue(int64(dv.viewedArrayBuf.getInt8(idx)))
 	}
 	panic(r.NewTypeError("Method DataView.prototype.getInt8 called on incompatible receiver %s", call.This.String()))
@@ -240,21 +240,21 @@ func (r *Runtime) dataViewProto_getInt8(call FunctionCall) Value {
 
 func (r *Runtime) dataViewProto_getInt16(call FunctionCall) Value {
 	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
-		return intToValue(int64(dv.viewedArrayBuf.getInt16(dv.getIdxAndByteOrder(call.Argument(0), call.Argument(1), 2))))
+		return intToValue(int64(dv.viewedArrayBuf.getInt16(dv.getIdxAndByteOrder(r.toIndex(call.Argument(0)), call.Argument(1), 2))))
 	}
 	panic(r.NewTypeError("Method DataView.prototype.getInt16 called on incompatible receiver %s", call.This.String()))
 }
 
 func (r *Runtime) dataViewProto_getInt32(call FunctionCall) Value {
 	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
-		return intToValue(int64(dv.viewedArrayBuf.getInt32(dv.getIdxAndByteOrder(call.Argument(0), call.Argument(1), 4))))
+		return intToValue(int64(dv.viewedArrayBuf.getInt32(dv.getIdxAndByteOrder(r.toIndex(call.Argument(0)), call.Argument(1), 4))))
 	}
 	panic(r.NewTypeError("Method DataView.prototype.getInt32 called on incompatible receiver %s", call.This.String()))
 }
 
 func (r *Runtime) dataViewProto_getUint8(call FunctionCall) Value {
 	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
-		idx, _ := dv.getIdxAndByteOrder(call.Argument(0), call.Argument(1), 1)
+		idx, _ := dv.getIdxAndByteOrder(r.toIndex(call.Argument(0)), call.Argument(1), 1)
 		return intToValue(int64(dv.viewedArrayBuf.getUint8(idx)))
 	}
 	panic(r.NewTypeError("Method DataView.prototype.getUint8 called on incompatible receiver %s", call.This.String()))
@@ -262,22 +262,23 @@ func (r *Runtime) dataViewProto_getUint8(call FunctionCall) Value {
 
 func (r *Runtime) dataViewProto_getUint16(call FunctionCall) Value {
 	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
-		return intToValue(int64(dv.viewedArrayBuf.getUint16(dv.getIdxAndByteOrder(call.Argument(0), call.Argument(1), 2))))
+		return intToValue(int64(dv.viewedArrayBuf.getUint16(dv.getIdxAndByteOrder(r.toIndex(call.Argument(0)), call.Argument(1), 2))))
 	}
 	panic(r.NewTypeError("Method DataView.prototype.getUint16 called on incompatible receiver %s", call.This.String()))
 }
 
 func (r *Runtime) dataViewProto_getUint32(call FunctionCall) Value {
 	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
-		return intToValue(int64(dv.viewedArrayBuf.getUint32(dv.getIdxAndByteOrder(call.Argument(0), call.Argument(1), 4))))
+		return intToValue(int64(dv.viewedArrayBuf.getUint32(dv.getIdxAndByteOrder(r.toIndex(call.Argument(0)), call.Argument(1), 4))))
 	}
 	panic(r.NewTypeError("Method DataView.prototype.getUint32 called on incompatible receiver %s", call.This.String()))
 }
 
 func (r *Runtime) dataViewProto_setFloat32(call FunctionCall) Value {
 	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		idxVal := r.toIndex(call.Argument(0))
 		val := toFloat32(call.Argument(1))
-		idx, bo := dv.getIdxAndByteOrder(call.Argument(0), call.Argument(2), 4)
+		idx, bo := dv.getIdxAndByteOrder(idxVal, call.Argument(2), 4)
 		dv.viewedArrayBuf.setFloat32(idx, val, bo)
 		return _undefined
 	}
@@ -286,8 +287,9 @@ func (r *Runtime) dataViewProto_setFloat32(call FunctionCall) Value {
 
 func (r *Runtime) dataViewProto_setFloat64(call FunctionCall) Value {
 	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		idxVal := r.toIndex(call.Argument(0))
 		val := call.Argument(1).ToFloat()
-		idx, bo := dv.getIdxAndByteOrder(call.Argument(0), call.Argument(2), 8)
+		idx, bo := dv.getIdxAndByteOrder(idxVal, call.Argument(2), 8)
 		dv.viewedArrayBuf.setFloat64(idx, val, bo)
 		return _undefined
 	}
@@ -296,8 +298,9 @@ func (r *Runtime) dataViewProto_setFloat64(call FunctionCall) Value {
 
 func (r *Runtime) dataViewProto_setInt8(call FunctionCall) Value {
 	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		idxVal := r.toIndex(call.Argument(0))
 		val := toInt8(call.Argument(1))
-		idx, _ := dv.getIdxAndByteOrder(call.Argument(0), call.Argument(2), 1)
+		idx, _ := dv.getIdxAndByteOrder(idxVal, call.Argument(2), 1)
 		dv.viewedArrayBuf.setInt8(idx, val)
 		return _undefined
 	}
@@ -306,8 +309,9 @@ func (r *Runtime) dataViewProto_setInt8(call FunctionCall) Value {
 
 func (r *Runtime) dataViewProto_setInt16(call FunctionCall) Value {
 	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		idxVal := r.toIndex(call.Argument(0))
 		val := toInt16(call.Argument(1))
-		idx, bo := dv.getIdxAndByteOrder(call.Argument(0), call.Argument(2), 2)
+		idx, bo := dv.getIdxAndByteOrder(idxVal, call.Argument(2), 2)
 		dv.viewedArrayBuf.setInt16(idx, val, bo)
 		return _undefined
 	}
@@ -316,8 +320,9 @@ func (r *Runtime) dataViewProto_setInt16(call FunctionCall) Value {
 
 func (r *Runtime) dataViewProto_setInt32(call FunctionCall) Value {
 	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		idxVal := r.toIndex(call.Argument(0))
 		val := toInt32(call.Argument(1))
-		idx, bo := dv.getIdxAndByteOrder(call.Argument(0), call.Argument(2), 4)
+		idx, bo := dv.getIdxAndByteOrder(idxVal, call.Argument(2), 4)
 		dv.viewedArrayBuf.setInt32(idx, val, bo)
 		return _undefined
 	}
@@ -326,8 +331,9 @@ func (r *Runtime) dataViewProto_setInt32(call FunctionCall) Value {
 
 func (r *Runtime) dataViewProto_setUint8(call FunctionCall) Value {
 	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		idxVal := r.toIndex(call.Argument(0))
 		val := toUint8(call.Argument(1))
-		idx, _ := dv.getIdxAndByteOrder(call.Argument(0), call.Argument(2), 1)
+		idx, _ := dv.getIdxAndByteOrder(idxVal, call.Argument(2), 1)
 		dv.viewedArrayBuf.setUint8(idx, val)
 		return _undefined
 	}
@@ -336,8 +342,9 @@ func (r *Runtime) dataViewProto_setUint8(call FunctionCall) Value {
 
 func (r *Runtime) dataViewProto_setUint16(call FunctionCall) Value {
 	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		idxVal := r.toIndex(call.Argument(0))
 		val := toUint16(call.Argument(1))
-		idx, bo := dv.getIdxAndByteOrder(call.Argument(0), call.Argument(2), 2)
+		idx, bo := dv.getIdxAndByteOrder(idxVal, call.Argument(2), 2)
 		dv.viewedArrayBuf.setUint16(idx, val, bo)
 		return _undefined
 	}
@@ -346,8 +353,9 @@ func (r *Runtime) dataViewProto_setUint16(call FunctionCall) Value {
 
 func (r *Runtime) dataViewProto_setUint32(call FunctionCall) Value {
 	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		idxVal := r.toIndex(call.Argument(0))
 		val := toUint32(call.Argument(1))
-		idx, bo := dv.getIdxAndByteOrder(call.Argument(0), call.Argument(2), 4)
+		idx, bo := dv.getIdxAndByteOrder(idxVal, call.Argument(2), 4)
 		dv.viewedArrayBuf.setUint32(idx, val, bo)
 		return _undefined
 	}
@@ -433,8 +441,11 @@ func (r *Runtime) typedArrayProto_every(call FunctionCall) Value {
 			Arguments: []Value{nil, nil, call.This},
 		}
 		for k := 0; k < ta.length; k++ {
-			ta.viewedArrayBuf.ensureNotDetached(true)
-			fc.Arguments[0] = ta.typedArray.get(ta.offset + k)
+			if ta.isValidIntegerIndex(k) {
+				fc.Arguments[0] = ta.typedArray.get(ta.offset + k)
+			} else {
+				fc.Arguments[0] = _undefined
+			}
 			fc.Arguments[1] = intToValue(int64(k))
 			if !callbackFn(fc).ToBoolean() {
 				return valueFalse
@@ -479,13 +490,21 @@ func (r *Runtime) typedArrayProto_filter(call FunctionCall) Value {
 		}
 		buf := make([]byte, 0, ta.length*ta.elemSize)
 		captured := 0
+		rawVal := make([]byte, ta.elemSize)
 		for k := 0; k < ta.length; k++ {
-			ta.viewedArrayBuf.ensureNotDetached(true)
-			fc.Arguments[0] = ta.typedArray.get(k)
+			if ta.isValidIntegerIndex(k) {
+				fc.Arguments[0] = ta.typedArray.get(ta.offset + k)
+				i := (ta.offset + k) * ta.elemSize
+				copy(rawVal, ta.viewedArrayBuf.data[i:])
+			} else {
+				fc.Arguments[0] = _undefined
+				for i := range rawVal {
+					rawVal[i] = 0
+				}
+			}
 			fc.Arguments[1] = intToValue(int64(k))
 			if callbackFn(fc).ToBoolean() {
-				i := (ta.offset + k) * ta.elemSize
-				buf = append(buf, ta.viewedArrayBuf.data[i:i+ta.elemSize]...)
+				buf = append(buf, rawVal...)
 				captured++
 			}
 		}
@@ -496,10 +515,10 @@ func (r *Runtime) typedArrayProto_filter(call FunctionCall) Value {
 		if c == ta.defaultCtor {
 			return kept
 		} else {
-			ret := r.typedArrayCreate(c, []Value{intToValue(int64(captured))})
+			ret := r.typedArrayCreate(c, intToValue(int64(captured)))
 			keptTa := kept.self.(*typedArrayObject)
 			for i := 0; i < captured; i++ {
-				ret.typedArray.set(i, keptTa.typedArray.get(i))
+				ret.typedArray.set(i, keptTa.typedArray.get(keptTa.offset+i))
 			}
 			return ret.val
 		}
@@ -516,8 +535,10 @@ func (r *Runtime) typedArrayProto_find(call FunctionCall) Value {
 			Arguments: []Value{nil, nil, call.This},
 		}
 		for k := 0; k < ta.length; k++ {
-			ta.viewedArrayBuf.ensureNotDetached(true)
-			val := ta.typedArray.get(ta.offset + k)
+			var val Value
+			if ta.isValidIntegerIndex(k) {
+				val = ta.typedArray.get(ta.offset + k)
+			}
 			fc.Arguments[0] = val
 			fc.Arguments[1] = intToValue(int64(k))
 			if predicate(fc).ToBoolean() {
@@ -538,8 +559,11 @@ func (r *Runtime) typedArrayProto_findIndex(call FunctionCall) Value {
 			Arguments: []Value{nil, nil, call.This},
 		}
 		for k := 0; k < ta.length; k++ {
-			ta.viewedArrayBuf.ensureNotDetached(true)
-			fc.Arguments[0] = ta.typedArray.get(ta.offset + k)
+			if ta.isValidIntegerIndex(k) {
+				fc.Arguments[0] = ta.typedArray.get(ta.offset + k)
+			} else {
+				fc.Arguments[0] = _undefined
+			}
 			fc.Arguments[1] = intToValue(int64(k))
 			if predicate(fc).ToBoolean() {
 				return fc.Arguments[1]
@@ -559,12 +583,13 @@ func (r *Runtime) typedArrayProto_forEach(call FunctionCall) Value {
 			Arguments: []Value{nil, nil, call.This},
 		}
 		for k := 0; k < ta.length; k++ {
-			ta.viewedArrayBuf.ensureNotDetached(true)
-			if val := ta.typedArray.get(k); val != nil {
-				fc.Arguments[0] = val
-				fc.Arguments[1] = intToValue(int64(k))
-				callbackFn(fc)
+			var val Value
+			if ta.isValidIntegerIndex(k) {
+				val = ta.typedArray.get(ta.offset + k)
 			}
+			fc.Arguments[0] = val
+			fc.Arguments[1] = intToValue(int64(k))
+			callbackFn(fc)
 		}
 		return _undefined
 	}
@@ -588,14 +613,20 @@ func (r *Runtime) typedArrayProto_includes(call FunctionCall) Value {
 			n = max(length+n, 0)
 		}
 
-		ta.viewedArrayBuf.ensureNotDetached(true)
 		searchElement := call.Argument(0)
 		if searchElement == _negativeZero {
 			searchElement = _positiveZero
 		}
+		startIdx := toIntStrict(n)
+		if !ta.viewedArrayBuf.ensureNotDetached(false) {
+			if searchElement == _undefined && startIdx < ta.length {
+				return valueTrue
+			}
+			return valueFalse
+		}
 		if ta.typedArray.typeMatch(searchElement) {
 			se := ta.typedArray.toRaw(searchElement)
-			for k := toIntStrict(n); k < ta.length; k++ {
+			for k := startIdx; k < ta.length; k++ {
 				if ta.typedArray.getRaw(ta.offset+k) == se {
 					return valueTrue
 				}
@@ -623,16 +654,17 @@ func (r *Runtime) typedArrayProto_indexOf(call FunctionCall) Value {
 			n = max(length+n, 0)
 		}
 
-		ta.viewedArrayBuf.ensureNotDetached(true)
-		searchElement := call.Argument(0)
-		if searchElement == _negativeZero {
-			searchElement = _positiveZero
-		}
-		if !IsNaN(searchElement) && ta.typedArray.typeMatch(searchElement) {
-			se := ta.typedArray.toRaw(searchElement)
-			for k := toIntStrict(n); k < ta.length; k++ {
-				if ta.typedArray.getRaw(ta.offset+k) == se {
-					return intToValue(int64(k))
+		if ta.viewedArrayBuf.ensureNotDetached(false) {
+			searchElement := call.Argument(0)
+			if searchElement == _negativeZero {
+				searchElement = _positiveZero
+			}
+			if !IsNaN(searchElement) && ta.typedArray.typeMatch(searchElement) {
+				se := ta.typedArray.toRaw(searchElement)
+				for k := toIntStrict(n); k < ta.length; k++ {
+					if ta.typedArray.getRaw(ta.offset+k) == se {
+						return intToValue(int64(k))
+					}
 				}
 			}
 		}
@@ -658,18 +690,21 @@ func (r *Runtime) typedArrayProto_join(call FunctionCall) Value {
 
 		var buf valueStringBuilder
 
-		ta.viewedArrayBuf.ensureNotDetached(true)
-		element0 := ta.typedArray.get(0)
+		var element0 Value
+		if ta.isValidIntegerIndex(0) {
+			element0 = ta.typedArray.get(ta.offset + 0)
+		}
 		if element0 != nil && element0 != _undefined && element0 != _null {
 			buf.WriteString(element0.toString())
 		}
 
 		for i := 1; i < l; i++ {
-			ta.viewedArrayBuf.ensureNotDetached(true)
 			buf.WriteString(sep)
-			element := ta.typedArray.get(i)
-			if element != nil && element != _undefined && element != _null {
-				buf.WriteString(element.toString())
+			if ta.isValidIntegerIndex(i) {
+				element := ta.typedArray.get(ta.offset + i)
+				if element != nil && element != _undefined && element != _null {
+					buf.WriteString(element.toString())
+				}
 			}
 		}
 
@@ -710,16 +745,17 @@ func (r *Runtime) typedArrayProto_lastIndexOf(call FunctionCall) Value {
 			}
 		}
 
-		ta.viewedArrayBuf.ensureNotDetached(true)
-		searchElement := call.Argument(0)
-		if searchElement == _negativeZero {
-			searchElement = _positiveZero
-		}
-		if !IsNaN(searchElement) && ta.typedArray.typeMatch(searchElement) {
-			se := ta.typedArray.toRaw(searchElement)
-			for k := toIntStrict(fromIndex); k >= 0; k-- {
-				if ta.typedArray.getRaw(ta.offset+k) == se {
-					return intToValue(int64(k))
+		if ta.viewedArrayBuf.ensureNotDetached(false) {
+			searchElement := call.Argument(0)
+			if searchElement == _negativeZero {
+				searchElement = _positiveZero
+			}
+			if !IsNaN(searchElement) && ta.typedArray.typeMatch(searchElement) {
+				se := ta.typedArray.toRaw(searchElement)
+				for k := toIntStrict(fromIndex); k >= 0; k-- {
+					if ta.typedArray.getRaw(ta.offset+k) == se {
+						return intToValue(int64(k))
+					}
 				}
 			}
 		}
@@ -739,8 +775,11 @@ func (r *Runtime) typedArrayProto_map(call FunctionCall) Value {
 		}
 		dst := r.typedArraySpeciesCreate(ta, []Value{intToValue(int64(ta.length))})
 		for i := 0; i < ta.length; i++ {
-			ta.viewedArrayBuf.ensureNotDetached(true)
-			fc.Arguments[0] = ta.typedArray.get(ta.offset + i)
+			if ta.isValidIntegerIndex(i) {
+				fc.Arguments[0] = ta.typedArray.get(ta.offset + i)
+			} else {
+				fc.Arguments[0] = _undefined
+			}
 			fc.Arguments[1] = intToValue(int64(i))
 			dst.typedArray.set(i, callbackFn(fc))
 		}
@@ -770,9 +809,12 @@ func (r *Runtime) typedArrayProto_reduce(call FunctionCall) Value {
 			panic(r.NewTypeError("Reduce of empty array with no initial value"))
 		}
 		for ; k < ta.length; k++ {
-			ta.viewedArrayBuf.ensureNotDetached(true)
+			if ta.isValidIntegerIndex(k) {
+				fc.Arguments[1] = ta.typedArray.get(ta.offset + k)
+			} else {
+				fc.Arguments[1] = _undefined
+			}
 			idx := valueInt(k)
-			fc.Arguments[1] = ta.typedArray.get(ta.offset + k)
 			fc.Arguments[2] = idx
 			fc.Arguments[0] = callbackFn(fc)
 		}
@@ -802,9 +844,12 @@ func (r *Runtime) typedArrayProto_reduceRight(call FunctionCall) Value {
 			panic(r.NewTypeError("Reduce of empty array with no initial value"))
 		}
 		for ; k >= 0; k-- {
-			ta.viewedArrayBuf.ensureNotDetached(true)
+			if ta.isValidIntegerIndex(k) {
+				fc.Arguments[1] = ta.typedArray.get(ta.offset + k)
+			} else {
+				fc.Arguments[1] = _undefined
+			}
 			idx := valueInt(k)
-			fc.Arguments[1] = ta.typedArray.get(ta.offset + k)
 			fc.Arguments[2] = idx
 			fc.Arguments[0] = callbackFn(fc)
 		}
@@ -895,7 +940,9 @@ func (r *Runtime) typedArrayProto_set(call FunctionCall) Value {
 			for i := 0; i < srcLen; i++ {
 				val := nilSafe(srcObj.self.getIdx(valueInt(i), nil))
 				ta.viewedArrayBuf.ensureNotDetached(true)
-				ta.typedArray.set(targetOffset+i, val)
+				if ta.isValidIntegerIndex(i) {
+					ta.typedArray.set(targetOffset+i, val)
+				}
 			}
 		}
 		return _undefined
@@ -948,8 +995,11 @@ func (r *Runtime) typedArrayProto_some(call FunctionCall) Value {
 			Arguments: []Value{nil, nil, call.This},
 		}
 		for k := 0; k < ta.length; k++ {
-			ta.viewedArrayBuf.ensureNotDetached(true)
-			fc.Arguments[0] = ta.typedArray.get(ta.offset + k)
+			if ta.isValidIntegerIndex(k) {
+				fc.Arguments[0] = ta.typedArray.get(ta.offset + k)
+			} else {
+				fc.Arguments[0] = _undefined
+			}
 			fc.Arguments[1] = intToValue(int64(k))
 			if callbackFn(fc).ToBoolean() {
 				return valueTrue
@@ -1009,7 +1059,7 @@ func (r *Runtime) typedArrayProto_toLocaleString(call FunctionCall) Value {
 			if i > 0 {
 				buf.WriteRune(',')
 			}
-			item := ta.typedArray.get(i)
+			item := ta.typedArray.get(ta.offset + i)
 			r.writeItemLocaleString(item, &buf)
 		}
 		return buf.String()
@@ -1040,35 +1090,76 @@ func (r *Runtime) newTypedArray([]Value, *Object) *Object {
 }
 
 func (r *Runtime) typedArray_from(call FunctionCall) Value {
-	mapFn := call.Argument(1)
-	if mapFn == _undefined {
-		mapFn = nil
+	c := r.toObject(call.This)
+	var mapFc func(call FunctionCall) Value
+	thisValue := call.Argument(2)
+	if mapFn := call.Argument(1); mapFn != _undefined {
+		mapFc = r.toCallable(mapFn)
 	}
-	return r.typedArrayFrom(r.toObject(call.This), call.Argument(0).ToObject(r), mapFn, call.Argument(2))
+	source := r.toObject(call.Argument(0))
+	usingIter := toMethod(source.self.getSym(SymIterator, nil))
+	if usingIter != nil {
+		values := r.iterableToList(source, usingIter)
+		ta := r.typedArrayCreate(c, intToValue(int64(len(values))))
+		if mapFc == nil {
+			for idx, val := range values {
+				ta.typedArray.set(idx, val)
+			}
+		} else {
+			fc := FunctionCall{
+				This:      thisValue,
+				Arguments: []Value{nil, nil},
+			}
+			for idx, val := range values {
+				fc.Arguments[0], fc.Arguments[1] = val, intToValue(int64(idx))
+				val = mapFc(fc)
+				ta.typedArray.set(idx, val)
+			}
+		}
+		return ta.val
+	}
+	length := toIntStrict(toLength(source.self.getStr("length", nil)))
+	ta := r.typedArrayCreate(c, intToValue(int64(length)))
+	if mapFc == nil {
+		for i := 0; i < length; i++ {
+			ta.typedArray.set(i, nilSafe(source.self.getIdx(valueInt(i), nil)))
+		}
+	} else {
+		fc := FunctionCall{
+			This:      thisValue,
+			Arguments: []Value{nil, nil},
+		}
+		for i := 0; i < length; i++ {
+			idx := valueInt(i)
+			fc.Arguments[0], fc.Arguments[1] = source.self.getIdx(idx, nil), idx
+			ta.typedArray.set(i, mapFc(fc))
+		}
+	}
+	return ta.val
 }
 
 func (r *Runtime) typedArray_of(call FunctionCall) Value {
-	ta := r.typedArrayCreate(r.toObject(call.This), []Value{intToValue(int64(len(call.Arguments)))})
+	ta := r.typedArrayCreate(r.toObject(call.This), intToValue(int64(len(call.Arguments))))
 	for i, val := range call.Arguments {
 		ta.typedArray.set(i, val)
 	}
 	return ta.val
 }
 
-func (r *Runtime) allocateTypedArray(newTarget *Object, length int, taCtor typedArrayObjectCtor) *Object {
+func (r *Runtime) allocateTypedArray(newTarget *Object, length int, taCtor typedArrayObjectCtor, proto *Object) *typedArrayObject {
 	buf := r._newArrayBuffer(r.global.ArrayBufferPrototype, nil)
-	ta := taCtor(buf, 0, length, r.getPrototypeFromCtor(newTarget, nil, r.global.TypedArrayPrototype))
+	ta := taCtor(buf, 0, length, r.getPrototypeFromCtor(newTarget, nil, proto))
 	if length > 0 {
 		buf.data = allocByteSlice(length * ta.elemSize)
 	}
-	return ta.val
+	return ta
 }
 
 func (r *Runtime) typedArraySpeciesCreate(ta *typedArrayObject, args []Value) *typedArrayObject {
-	return r.typedArrayCreate(r.speciesConstructorObj(ta.val, ta.defaultCtor), args)
+	return r.typedArrayCreate(r.speciesConstructorObj(ta.val, ta.defaultCtor), args...)
 }
 
-func (r *Runtime) typedArrayCreate(ctor *Object, args []Value) *typedArrayObject {
+func (r *Runtime) typedArrayCreate(ctor *Object, args ...Value) *typedArrayObject {
 	o := r.toConstructor(ctor)(args, ctor)
 	if ta, ok := o.self.(*typedArrayObject); ok {
 		ta.viewedArrayBuf.ensureNotDetached(true)
@@ -1084,7 +1175,7 @@ func (r *Runtime) typedArrayCreate(ctor *Object, args []Value) *typedArrayObject
 	panic(r.NewTypeError("Invalid TypedArray: %s", o))
 }
 
-func (r *Runtime) typedArrayFrom(ctor, items *Object, mapFn, thisValue Value) *Object {
+func (r *Runtime) typedArrayFrom(ctor, items *Object, mapFn, thisValue Value, taCtor typedArrayObjectCtor, proto *Object) *Object {
 	var mapFc func(call FunctionCall) Value
 	if mapFn != nil {
 		mapFc = r.toCallable(mapFn)
@@ -1095,7 +1186,7 @@ func (r *Runtime) typedArrayFrom(ctor, items *Object, mapFn, thisValue Value) *O
 	usingIter := toMethod(items.self.getSym(SymIterator, nil))
 	if usingIter != nil {
 		values := r.iterableToList(items, usingIter)
-		ta := r.typedArrayCreate(ctor, []Value{intToValue(int64(len(values)))})
+		ta := r.allocateTypedArray(ctor, len(values), taCtor, proto)
 		if mapFc == nil {
 			for idx, val := range values {
 				ta.typedArray.set(idx, val)
@@ -1114,7 +1205,7 @@ func (r *Runtime) typedArrayFrom(ctor, items *Object, mapFn, thisValue Value) *O
 		return ta.val
 	}
 	length := toIntStrict(toLength(items.self.getStr("length", nil)))
-	ta := r.typedArrayCreate(ctor, []Value{intToValue(int64(length))})
+	ta := r.allocateTypedArray(ctor, length, taCtor, proto)
 	if mapFc == nil {
 		for i := 0; i < length; i++ {
 			ta.typedArray.set(i, nilSafe(items.self.getIdx(valueInt(i), nil)))
@@ -1133,8 +1224,8 @@ func (r *Runtime) typedArrayFrom(ctor, items *Object, mapFn, thisValue Value) *O
 	return ta.val
 }
 
-func (r *Runtime) _newTypedArrayFromArrayBuffer(ab *arrayBufferObject, args []Value, newTarget *Object, taCtor typedArrayObjectCtor) *Object {
-	ta := taCtor(ab, 0, 0, r.getPrototypeFromCtor(newTarget, nil, r.global.TypedArrayPrototype))
+func (r *Runtime) _newTypedArrayFromArrayBuffer(ab *arrayBufferObject, args []Value, newTarget *Object, taCtor typedArrayObjectCtor, proto *Object) *Object {
+	ta := taCtor(ab, 0, 0, r.getPrototypeFromCtor(newTarget, nil, proto))
 	var byteOffset int
 	if len(args) > 1 && args[1] != nil && args[1] != _undefined {
 		byteOffset = r.toIndex(args[1])
@@ -1142,30 +1233,36 @@ func (r *Runtime) _newTypedArrayFromArrayBuffer(ab *arrayBufferObject, args []Va
 			panic(r.newError(r.global.RangeError, "Start offset of %s should be a multiple of %d", newTarget.self.getStr("name", nil), ta.elemSize))
 		}
 	}
-	ab.ensureNotDetached(true)
 	var length int
 	if len(args) > 2 && args[2] != nil && args[2] != _undefined {
 		length = r.toIndex(args[2])
+		ab.ensureNotDetached(true)
 		if byteOffset+length*ta.elemSize > len(ab.data) {
 			panic(r.newError(r.global.RangeError, "Invalid typed array length: %d", length))
 		}
 	} else {
+		ab.ensureNotDetached(true)
 		if len(ab.data)%ta.elemSize != 0 {
 			panic(r.newError(r.global.RangeError, "Byte length of %s should be a multiple of %d", newTarget.self.getStr("name", nil), ta.elemSize))
 		}
 		length = (len(ab.data) - byteOffset) / ta.elemSize
+		if length < 0 {
+			panic(r.newError(r.global.RangeError, "Start offset %d is outside the bounds of the buffer", byteOffset))
+		}
 	}
 	ta.offset = byteOffset / ta.elemSize
 	ta.length = length
 	return ta.val
 }
 
-func (r *Runtime) _newTypedArrayFromTypedArray(src *typedArrayObject, newTarget *Object) *Object {
-	dst := r.typedArrayCreate(newTarget, []Value{_positiveZero})
+func (r *Runtime) _newTypedArrayFromTypedArray(src *typedArrayObject, newTarget *Object, taCtor typedArrayObjectCtor, proto *Object) *Object {
+	dst := r.allocateTypedArray(newTarget, 0, taCtor, proto)
 	src.viewedArrayBuf.ensureNotDetached(true)
 	l := src.length
-	dst.viewedArrayBuf.prototype = r.getPrototypeFromCtor(r.toObject(src.viewedArrayBuf.getStr("constructor", nil)), r.global.ArrayBuffer, r.global.ArrayBufferPrototype)
+
+	dst.viewedArrayBuf.prototype = r.getPrototypeFromCtor(r.speciesConstructorObj(src.viewedArrayBuf.val, r.global.ArrayBuffer), r.global.ArrayBuffer, r.global.ArrayBufferPrototype)
 	dst.viewedArrayBuf.data = allocByteSlice(toIntStrict(int64(l) * int64(dst.elemSize)))
+	src.viewedArrayBuf.ensureNotDetached(true)
 	if src.defaultCtor == dst.defaultCtor {
 		copy(dst.viewedArrayBuf.data, src.viewedArrayBuf.data[src.offset*src.elemSize:])
 		dst.length = src.length
@@ -1178,7 +1275,7 @@ func (r *Runtime) _newTypedArrayFromTypedArray(src *typedArrayObject, newTarget
 	return dst.val
 }
 
-func (r *Runtime) _newTypedArray(args []Value, newTarget *Object, taCtor typedArrayObjectCtor) *Object {
+func (r *Runtime) _newTypedArray(args []Value, newTarget *Object, taCtor typedArrayObjectCtor, proto *Object) *Object {
 	if newTarget == nil {
 		panic(r.needNew("TypedArray"))
 	}
@@ -1186,11 +1283,11 @@ func (r *Runtime) _newTypedArray(args []Value, newTarget *Object, taCtor typedAr
 		if obj, ok := args[0].(*Object); ok {
 			switch o := obj.self.(type) {
 			case *arrayBufferObject:
-				return r._newTypedArrayFromArrayBuffer(o, args, newTarget, taCtor)
+				return r._newTypedArrayFromArrayBuffer(o, args, newTarget, taCtor, proto)
 			case *typedArrayObject:
-				return r._newTypedArrayFromTypedArray(o, newTarget)
+				return r._newTypedArrayFromTypedArray(o, newTarget, taCtor, proto)
 			default:
-				return r.typedArrayFrom(newTarget, obj, nil, nil)
+				return r.typedArrayFrom(newTarget, obj, nil, nil, taCtor, proto)
 			}
 		}
 	}
@@ -1200,43 +1297,43 @@ func (r *Runtime) _newTypedArray(args []Value, newTarget *Object, taCtor typedAr
 			l = r.toIndex(arg0)
 		}
 	}
-	return r.allocateTypedArray(newTarget, l, taCtor)
+	return r.allocateTypedArray(newTarget, l, taCtor, proto).val
 }
 
-func (r *Runtime) newUint8Array(args []Value, newTarget *Object) *Object {
-	return r._newTypedArray(args, newTarget, r.newUint8ArrayObject)
+func (r *Runtime) newUint8Array(args []Value, newTarget, proto *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newUint8ArrayObject, proto)
 }
 
-func (r *Runtime) newUint8ClampedArray(args []Value, newTarget *Object) *Object {
-	return r._newTypedArray(args, newTarget, r.newUint8ClampedArrayObject)
+func (r *Runtime) newUint8ClampedArray(args []Value, newTarget, proto *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newUint8ClampedArrayObject, proto)
 }
 
-func (r *Runtime) newInt8Array(args []Value, newTarget *Object) *Object {
-	return r._newTypedArray(args, newTarget, r.newInt8ArrayObject)
+func (r *Runtime) newInt8Array(args []Value, newTarget, proto *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newInt8ArrayObject, proto)
 }
 
-func (r *Runtime) newUint16Array(args []Value, newTarget *Object) *Object {
-	return r._newTypedArray(args, newTarget, r.newUint16ArrayObject)
+func (r *Runtime) newUint16Array(args []Value, newTarget, proto *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newUint16ArrayObject, proto)
 }
 
-func (r *Runtime) newInt16Array(args []Value, newTarget *Object) *Object {
-	return r._newTypedArray(args, newTarget, r.newInt16ArrayObject)
+func (r *Runtime) newInt16Array(args []Value, newTarget, proto *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newInt16ArrayObject, proto)
 }
 
-func (r *Runtime) newUint32Array(args []Value, newTarget *Object) *Object {
-	return r._newTypedArray(args, newTarget, r.newUint32ArrayObject)
+func (r *Runtime) newUint32Array(args []Value, newTarget, proto *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newUint32ArrayObject, proto)
 }
 
-func (r *Runtime) newInt32Array(args []Value, newTarget *Object) *Object {
-	return r._newTypedArray(args, newTarget, r.newInt32ArrayObject)
+func (r *Runtime) newInt32Array(args []Value, newTarget, proto *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newInt32ArrayObject, proto)
 }
 
-func (r *Runtime) newFloat32Array(args []Value, newTarget *Object) *Object {
-	return r._newTypedArray(args, newTarget, r.newFloat32ArrayObject)
+func (r *Runtime) newFloat32Array(args []Value, newTarget, proto *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newFloat32ArrayObject, proto)
 }
 
-func (r *Runtime) newFloat64Array(args []Value, newTarget *Object) *Object {
-	return r._newTypedArray(args, newTarget, r.newFloat64ArrayObject)
+func (r *Runtime) newFloat64Array(args []Value, newTarget, proto *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newFloat64ArrayObject, proto)
 }
 
 func (r *Runtime) createArrayBufferProto(val *Object) objectImpl {
@@ -1301,7 +1398,7 @@ func (r *Runtime) createDataViewProto(val *Object) objectImpl {
 }
 
 func (r *Runtime) createDataView(val *Object) objectImpl {
-	o := r.newNativeConstructOnly(val, r.newDataView, r.global.DataViewPrototype, "DataView", 3)
+	o := r.newNativeConstructOnly(val, r.newDataView, r.global.DataViewPrototype, "DataView", 1)
 	return o
 }
 
@@ -1373,21 +1470,19 @@ func (r *Runtime) createTypedArray(val *Object) objectImpl {
 	return o
 }
 
-func (r *Runtime) addPrototype(ctor *Object, proto *Object) *baseObject {
-	p := r.newBaseObject(proto, classObject)
-	p._putProp("constructor", ctor, true, false, true)
-	ctor.self._putProp("prototype", p.val, false, false, false)
-	return p
-}
-
-func (r *Runtime) typedArrayCreator(ctor func(args []Value, newTarget *Object) *Object, name unistring.String, bytesPerElement int) func(val *Object) objectImpl {
+func (r *Runtime) typedArrayCreator(ctor func(args []Value, newTarget, proto *Object) *Object, name unistring.String, bytesPerElement int) func(val *Object) objectImpl {
 	return func(val *Object) objectImpl {
-		o := r.newNativeConstructOnly(val, ctor, nil, name, 3)
+		p := r.newBaseObject(r.global.TypedArrayPrototype, classObject)
+		o := r.newNativeConstructOnly(val, func(args []Value, newTarget *Object) *Object {
+			return ctor(args, newTarget, p.val)
+		}, p.val, name, 3)
+
+		p._putProp("constructor", o.val, true, false, true)
+
 		o.prototype = r.global.TypedArray
-		proto := r.addPrototype(o.val, r.global.TypedArrayPrototype)
 		bpe := intToValue(int64(bytesPerElement))
 		o._putProp("BYTES_PER_ELEMENT", bpe, false, false, false)
-		proto._putProp("BYTES_PER_ELEMENT", bpe, false, false, false)
+		p._putProp("BYTES_PER_ELEMENT", bpe, false, false, false)
 		return o
 	}
 }

+ 2 - 2
builtin_weakmap.go

@@ -122,7 +122,7 @@ func (r *Runtime) builtin_newWeakMap(args []Value, newTarget *Object) *Object {
 			i0 := valueInt(0)
 			i1 := valueInt(1)
 			if adder == r.global.weakMapAdder {
-				r.iterate(iter, func(item Value) {
+				iter.iterate(func(item Value) {
 					itemObj := r.toObject(item)
 					k := itemObj.self.getIdx(i0, nil)
 					v := nilSafe(itemObj.self.getIdx(i1, nil))
@@ -133,7 +133,7 @@ func (r *Runtime) builtin_newWeakMap(args []Value, newTarget *Object) *Object {
 				if adderFn == nil {
 					panic(r.NewTypeError("WeakMap.set in missing"))
 				}
-				r.iterate(iter, func(item Value) {
+				iter.iterate(func(item Value) {
 					itemObj := r.toObject(item)
 					k := itemObj.self.getIdx(i0, nil)
 					v := itemObj.self.getIdx(i1, nil)

+ 1 - 1
builtin_weakset.go

@@ -52,7 +52,7 @@ func (r *Runtime) populateWeakSetGeneric(s *Object, adderValue Value, iterable V
 		panic(r.NewTypeError("WeakSet.add is not set"))
 	}
 	iter := r.getIterator(iterable, nil)
-	r.iterate(iter, func(val Value) {
+	iter.iterate(func(val Value) {
 		adder(FunctionCall{This: s, Arguments: []Value{val}})
 	})
 }

+ 26 - 55
compiler_expr.go

@@ -125,6 +125,7 @@ type compiledFunctionLiteral struct {
 	strict          *ast.StringLiteral
 	isExpr          bool
 	isArrow         bool
+	isMethod        bool
 }
 
 type compiledBracketExpr struct {
@@ -977,7 +978,7 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 			hasInits = true
 		}
 
-		if firstDupIdx >= 0 && (hasPatterns || hasInits || s.strict || e.isArrow) {
+		if firstDupIdx >= 0 && (hasPatterns || hasInits || s.strict || e.isArrow || e.isMethod) {
 			e.c.throwSyntaxError(firstDupIdx, "Duplicate parameter name not allowed in this context")
 			return
 		}
@@ -1254,9 +1255,13 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 	e.c.popScope()
 	e.c.p = savedPrg
 	if e.isArrow {
-		e.c.emit(&newArrowFunc{newFunc: newFunc{prg: p, length: uint32(length), name: name, source: e.source, strict: strict}})
+		e.c.emit(&newArrowFunc{newFunc: newFunc{prg: p, length: length, name: name, source: e.source, strict: strict}})
 	} else {
-		e.c.emit(&newFunc{prg: p, length: uint32(length), name: name, source: e.source, strict: strict})
+		if e.isMethod {
+			e.c.emit(&newMethod{prg: p, length: length, name: name, source: e.source, strict: strict})
+		} else {
+			e.c.emit(&newFunc{prg: p, length: length, name: name, source: e.source, strict: strict})
+		}
 	}
 	if !putOnStack {
 		e.c.emit(pop)
@@ -1770,6 +1775,7 @@ func (c *compiler) compileLogicalAnd(left, right ast.Expression, idx file.Idx) c
 func (e *compiledObjectLiteral) emitGetter(putOnStack bool) {
 	e.addSrcMap()
 	e.c.emit(newObject)
+	hasProto := false
 	for _, prop := range e.expr.Value {
 		switch prop := prop.(type) {
 		case *ast.PropertyKeyed:
@@ -1788,7 +1794,10 @@ func (e *compiledObjectLiteral) emitGetter(putOnStack bool) {
 			if fn, ok := valueExpr.(*compiledFunctionLiteral); ok {
 				if fn.name == nil {
 					anonFn = fn
-					fn.lhsName = key
+				}
+				switch prop.Kind {
+				case ast.PropertyKindMethod, ast.PropertyKindGet, ast.PropertyKindSet:
+					fn.isMethod = true
 				}
 			}
 			if computed {
@@ -1809,13 +1818,21 @@ func (e *compiledObjectLiteral) emitGetter(putOnStack bool) {
 					panic(fmt.Errorf("unknown property kind: %s", prop.Kind))
 				}
 			} else {
-				if anonFn != nil {
+				isProto := key == __proto__ && !prop.Computed
+				if isProto {
+					if hasProto {
+						e.c.throwSyntaxError(int(prop.Idx0())-1, "Duplicate __proto__ fields are not allowed in object literals")
+					} else {
+						hasProto = true
+					}
+				}
+				if anonFn != nil && !isProto {
 					anonFn.lhsName = key
 				}
 				valueExpr.emitGetter(true)
 				switch prop.Kind {
 				case ast.PropertyKindValue:
-					if key == __proto__ {
+					if isProto {
 						e.c.emit(setProto)
 					} else {
 						e.c.emit(setProp1(key))
@@ -2229,22 +2246,18 @@ func (c *compiler) emitObjectPattern(pattern *ast.ObjectPattern, emitAssign func
 }
 
 func (c *compiler) emitArrayPattern(pattern *ast.ArrayPattern, emitAssign func(target, init compiledExpr), putOnStack bool) {
-	var marks []int
 	c.emit(iterate)
 	for _, elt := range pattern.Elements {
 		switch elt := elt.(type) {
 		case nil:
-			marks = append(marks, len(c.p.code))
-			c.emit(nil)
+			c.emit(iterGetNextOrUndef{}, pop)
 		case *ast.AssignExpression:
 			c.emitAssign(elt.Left, c.compilePatternInitExpr(func() {
-				marks = append(marks, len(c.p.code))
-				c.emit(nil, enumGet)
+				c.emit(iterGetNextOrUndef{})
 			}, elt.Right, elt.Idx0()), emitAssign)
 		default:
 			c.emitAssign(elt, c.compileEmitterExpr(func() {
-				marks = append(marks, len(c.p.code))
-				c.emit(nil, enumGet)
+				c.emit(iterGetNextOrUndef{})
 			}, elt.Idx0()), emitAssign)
 		}
 	}
@@ -2255,40 +2268,6 @@ func (c *compiler) emitArrayPattern(pattern *ast.ArrayPattern, emitAssign func(t
 	} else {
 		c.emit(enumPopClose)
 	}
-	mark1 := len(c.p.code)
-	c.emit(nil)
-
-	for i, elt := range pattern.Elements {
-		switch elt := elt.(type) {
-		case nil:
-			c.p.code[marks[i]] = iterNext(len(c.p.code) - marks[i])
-		case *ast.Identifier:
-			emitAssign(c.compileIdentifierExpression(elt), c.compileEmitterExpr(func() {
-				c.p.code[marks[i]] = iterNext(len(c.p.code) - marks[i])
-				c.emit(loadUndef)
-			}, elt.Idx0()))
-		case *ast.AssignExpression:
-			c.emitAssign(elt.Left, c.compileNamedEmitterExpr(func(name unistring.String) {
-				c.p.code[marks[i]] = iterNext(len(c.p.code) - marks[i])
-				c.emitNamed(c.compileExpression(elt.Right), name)
-			}, elt.Idx0()), emitAssign)
-		default:
-			c.emitAssign(elt, c.compileEmitterExpr(
-				func() {
-					c.p.code[marks[i]] = iterNext(len(c.p.code) - marks[i])
-					c.emit(loadUndef)
-				}, elt.Idx0()), emitAssign)
-		}
-	}
-	c.emit(enumPop)
-	if pattern.Rest != nil {
-		c.emitAssign(pattern.Rest, c.compileExpression(
-			&ast.ArrayLiteral{
-				LeftBracket:  pattern.Rest.Idx0(),
-				RightBracket: pattern.Rest.Idx0(),
-			}), emitAssign)
-	}
-	c.p.code[mark1] = jump(len(c.p.code) - mark1)
 
 	if !putOnStack {
 		c.emit(pop)
@@ -2376,14 +2355,6 @@ func (c *compiler) compileEmitterExpr(emitter func(), idx file.Idx) *compiledEmi
 	return r
 }
 
-func (c *compiler) compileNamedEmitterExpr(namedEmitter func(unistring.String), idx file.Idx) *compiledEmitterExpr {
-	r := &compiledEmitterExpr{
-		namedEmitter: namedEmitter,
-	}
-	r.init(c, idx)
-	return r
-}
-
 func (e *compiledSpreadCallArgument) emitGetter(putOnStack bool) {
 	e.expr.emitGetter(putOnStack)
 	if putOnStack {

+ 5 - 2
compiler_stmt.go

@@ -189,6 +189,9 @@ func (c *compiler) compileTryStatement(v *ast.TryStatement, needResult bool) {
 		lbl1 := len(c.p.code)
 		c.emit(nil)
 		finallyOffset = len(c.p.code) - lbl
+		if bodyNeedResult && finallyBreaking != nil && lp == -1 {
+			c.emit(clearResult)
+		}
 		c.compileBlockStatement(v.Finally, false)
 		c.emit(halt, retFinally)
 
@@ -749,7 +752,7 @@ func (c *compiler) emitVarAssign(name unistring.String, offset int, init compile
 	if init != nil {
 		c.emitVarRef(name, offset)
 		c.emitNamed(init, name)
-		c.emit(putValueP)
+		c.emit(initValueP)
 	}
 }
 
@@ -802,7 +805,7 @@ func (c *compiler) emitPatternAssign(target, init compiledExpr) {
 	} else {
 		init.emitGetter(true)
 	}
-	c.emit(putValueP)
+	c.emit(initValueP)
 }
 
 func (c *compiler) compileLexicalBinding(expr *ast.Binding, isConst bool) {

+ 36 - 3
compiler_test.go

@@ -566,7 +566,7 @@ func TestTryBreakFinallyContinueWithResult(t *testing.T) {
 	  }
 	}
 	`
-	testScript1(SCRIPT, valueTrue, t)
+	testScript1(SCRIPT, _undefined, t)
 }
 
 func TestTryBreakFinallyContinueWithResult1(t *testing.T) {
@@ -581,7 +581,7 @@ func TestTryBreakFinallyContinueWithResult1(t *testing.T) {
 	  }
 	}
 	`
-	testScript1(SCRIPT, valueTrue, t)
+	testScript1(SCRIPT, _undefined, t)
 }
 
 func TestTryBreakFinallyContinueWithResultNested(t *testing.T) {
@@ -792,6 +792,14 @@ func TestTryReturnFromCatch(t *testing.T) {
 	testScript1(SCRIPT, valueInt(42), t)
 }
 
+func TestTryCompletionResult(t *testing.T) {
+	const SCRIPT = `
+	99; do { -99; try { 39 } catch (e) { -1 } finally { break; -2 }; } while (false);
+	`
+
+	testScript1(SCRIPT, _undefined, t)
+}
+
 func TestIfElse(t *testing.T) {
 	const SCRIPT = `
 	var rv;
@@ -1896,7 +1904,7 @@ func TestFunctionToString(t *testing.T) {
 	Function("arg1", "arg2", "return 42").toString();
 	`
 
-	testScript1(SCRIPT, asciiString("function anonymous(arg1,arg2){return 42}"), t)
+	testScript1(SCRIPT, asciiString("function anonymous(arg1,arg2\n) {\nreturn 42\n}"), t)
 }
 
 func TestObjectLiteral(t *testing.T) {
@@ -3028,6 +3036,31 @@ func TestEscapedObjectPropertyKeys(t *testing.T) {
 	}
 }
 
+func TestEscapedKeywords(t *testing.T) {
+	const SCRIPT = `r\u0065turn;`
+	_, err := Compile("", SCRIPT, false)
+	if err == nil {
+		t.Fatal("Expected error")
+	}
+}
+
+func TestEscapedLet(t *testing.T) {
+	const SCRIPT = `
+this.let = 0;
+
+l\u0065t // ASI
+a;
+
+// If the parser treated the previous escaped "let" as a lexical declaration,
+// this variable declaration will result an early syntax error.
+var a;
+`
+	_, err := Compile("", SCRIPT, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
 func TestObjectLiteralFuncProps(t *testing.T) {
 	const SCRIPT = `
 	(function() {

+ 26 - 11
destruct.go

@@ -206,16 +206,30 @@ func (i *destructKeyedSourceIter) next() (propIterItem, iterNextFunc) {
 			return item, nil
 		}
 		i.wrapped = next
-		if _, exists := i.d.usedKeys[stringValueFromRaw(item.name)]; !exists {
+		if _, exists := i.d.usedKeys[item.name]; !exists {
 			return item, i.next
 		}
 	}
 }
 
-func (d *destructKeyedSource) enumerateOwnKeys() iterNextFunc {
+func (d *destructKeyedSource) iterateStringKeys() iterNextFunc {
 	return (&destructKeyedSourceIter{
 		d:       d,
-		wrapped: d.w().enumerateOwnKeys(),
+		wrapped: d.w().iterateStringKeys(),
+	}).next
+}
+
+func (d *destructKeyedSource) iterateSymbols() iterNextFunc {
+	return (&destructKeyedSourceIter{
+		d:       d,
+		wrapped: d.w().iterateSymbols(),
+	}).next
+}
+
+func (d *destructKeyedSource) iterateKeys() iterNextFunc {
+	return (&destructKeyedSourceIter{
+		d:       d,
+		wrapped: d.w().iterateKeys(),
 	}).next
 }
 
@@ -231,17 +245,18 @@ func (d *destructKeyedSource) equal(impl objectImpl) bool {
 	return d.w().equal(impl)
 }
 
-func (d *destructKeyedSource) ownKeys(all bool, accum []Value) []Value {
+func (d *destructKeyedSource) stringKeys(all bool, accum []Value) []Value {
 	var next iterNextFunc
 	if all {
-		next = d.enumerateOwnKeys()
+		next = d.iterateStringKeys()
 	} else {
 		next = (&enumerableIter{
-			wrapped: d.enumerateOwnKeys(),
+			o:       d.wrapped.ToObject(d.r),
+			wrapped: d.iterateStringKeys(),
 		}).next
 	}
 	for item, next := next(); next != nil; item, next = next() {
-		accum = append(accum, stringValueFromRaw(item.name))
+		accum = append(accum, item.name)
 	}
 	return accum
 }
@@ -260,12 +275,12 @@ func (d *destructKeyedSource) filterUsedKeys(keys []Value) []Value {
 	return keys[:k]
 }
 
-func (d *destructKeyedSource) ownSymbols(all bool, accum []Value) []Value {
-	return d.filterUsedKeys(d.w().ownSymbols(all, accum))
+func (d *destructKeyedSource) symbols(all bool, accum []Value) []Value {
+	return d.filterUsedKeys(d.w().symbols(all, accum))
 }
 
-func (d *destructKeyedSource) ownPropertyKeys(all bool, accum []Value) []Value {
-	return d.filterUsedKeys(d.w().ownPropertyKeys(all, accum))
+func (d *destructKeyedSource) keys(all bool, accum []Value) []Value {
+	return d.filterUsedKeys(d.w().keys(all, accum))
 }
 
 func (d *destructKeyedSource) _putProp(name unistring.String, value Value, writable, enumerable, configurable bool) Value {

+ 27 - 10
func.go

@@ -25,6 +25,10 @@ type funcObject struct {
 	baseJsFuncObject
 }
 
+type methodFuncObject struct {
+	baseJsFuncObject
+}
+
 type arrowFuncObject struct {
 	baseJsFuncObject
 	this      Value
@@ -81,6 +85,11 @@ func (f *funcObject) setForeignStr(name unistring.String, val, receiver Value, t
 	return f._setForeignStr(name, f.getOwnPropStr(name), val, receiver, throw)
 }
 
+func (f *funcObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
+	f._addProto(name)
+	return f.baseObject.defineOwnPropertyStr(name, descr, throw)
+}
+
 func (f *funcObject) deleteStr(name unistring.String, throw bool) bool {
 	f._addProto(name)
 	return f.baseObject.deleteStr(name, throw)
@@ -103,13 +112,20 @@ func (f *funcObject) hasOwnPropertyStr(name unistring.String) bool {
 	return false
 }
 
-func (f *funcObject) ownKeys(all bool, accum []Value) []Value {
+func (f *funcObject) stringKeys(all bool, accum []Value) []Value {
 	if all {
 		if _, exists := f.values["prototype"]; !exists {
 			accum = append(accum, asciiString("prototype"))
 		}
 	}
-	return f.baseFuncObject.ownKeys(all, accum)
+	return f.baseFuncObject.stringKeys(all, accum)
+}
+
+func (f *funcObject) iterateStringKeys() iterNextFunc {
+	if _, exists := f.values["prototype"]; !exists {
+		f.addPrototype()
+	}
+	return f.baseFuncObject.iterateStringKeys()
 }
 
 func (f *funcObject) construct(args []Value, newTarget *Object) *Object {
@@ -136,7 +152,7 @@ func (f *funcObject) construct(args []Value, newTarget *Object) *Object {
 	return obj
 }
 
-func (f *funcObject) Call(call FunctionCall) Value {
+func (f *baseJsFuncObject) Call(call FunctionCall) Value {
 	return f.call(call, nil)
 }
 
@@ -176,7 +192,7 @@ func (f *baseJsFuncObject) _call(call FunctionCall, newTarget, this Value) Value
 
 }
 
-func (f *funcObject) call(call FunctionCall, newTarget Value) Value {
+func (f *baseJsFuncObject) call(call FunctionCall, newTarget Value) Value {
 	return f._call(call, newTarget, nilSafe(call.This))
 }
 
@@ -188,7 +204,7 @@ func (f *funcObject) exportType() reflect.Type {
 	return reflect.TypeOf(f.Call)
 }
 
-func (f *funcObject) assertCallable() (func(FunctionCall) Value, bool) {
+func (f *baseJsFuncObject) assertCallable() (func(FunctionCall) Value, bool) {
 	return f.Call, true
 }
 
@@ -204,14 +220,14 @@ func (f *arrowFuncObject) assertCallable() (func(FunctionCall) Value, bool) {
 	return f.Call, true
 }
 
-func (f *baseFuncObject) init(name unistring.String, length int) {
+func (f *baseFuncObject) init(name unistring.String, length Value) {
 	f.baseObject.init()
 
-	f._putProp("name", stringValueFromRaw(name), false, false, true)
-
 	f.lenProp.configurable = true
-	f.lenProp.value = valueInt(length)
+	f.lenProp.value = length
 	f._put("length", &f.lenProp)
+
+	f._putProp("name", stringValueFromRaw(name), false, false, true)
 }
 
 func (f *baseFuncObject) hasInstance(v Value) bool {
@@ -267,7 +283,7 @@ func (f *nativeFuncObject) assertConstructor() func(args []Value, newTarget *Obj
 	return f.construct
 }
 
-func (f *boundFuncObject) getStr(p unistring.String, receiver Value) Value {
+/*func (f *boundFuncObject) getStr(p unistring.String, receiver Value) Value {
 	return f.getStrWithOwnProp(f.getOwnPropStr(p), p, receiver)
 }
 
@@ -296,6 +312,7 @@ func (f *boundFuncObject) setOwnStr(name unistring.String, val Value, throw bool
 func (f *boundFuncObject) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) {
 	return f._setForeignStr(name, f.getOwnPropStr(name), val, receiver, throw)
 }
+*/
 
 func (f *boundFuncObject) hasInstance(v Value) bool {
 	return instanceOfOperator(v, f.wrapped)

+ 43 - 0
func_test.go

@@ -0,0 +1,43 @@
+package goja
+
+import "testing"
+
+func TestFuncProto(t *testing.T) {
+	const SCRIPT = `
+	"use strict";
+	function A() {}
+	A.__proto__ = Object;
+	A.prototype = {};
+
+	function B() {}
+	B.__proto__ = Object.create(null);
+	var thrown = false;
+	try {
+		delete B.prototype;
+	} catch (e) {
+		thrown = e instanceof TypeError;
+	}
+	thrown;
+	`
+	testScript1(SCRIPT, valueTrue, t)
+}
+
+func TestFuncPrototypeRedefine(t *testing.T) {
+	const SCRIPT = `
+	let thrown = false;
+	try {
+		Object.defineProperty(function() {}, "prototype", {
+			set: function(_value) {},
+		});
+	} catch (e) {
+		if (e instanceof TypeError) {
+			thrown = true;
+		} else {
+			throw e;
+		}
+	}
+	thrown;
+	`
+
+	testScript1(SCRIPT, valueTrue, t)
+}

+ 131 - 21
object.go

@@ -190,13 +190,18 @@ type objectImpl interface {
 	hasInstance(v Value) bool
 	isExtensible() bool
 	preventExtensions(throw bool) bool
-	enumerateOwnKeys() iterNextFunc
+
 	export(ctx *objectExportCtx) interface{}
 	exportType() reflect.Type
 	equal(objectImpl) bool
-	ownKeys(all bool, accum []Value) []Value
-	ownSymbols(all bool, accum []Value) []Value
-	ownPropertyKeys(all bool, accum []Value) []Value
+
+	iterateStringKeys() iterNextFunc
+	iterateSymbols() iterNextFunc
+	iterateKeys() iterNextFunc
+
+	stringKeys(all bool, accum []Value) []Value
+	symbols(all bool, accum []Value) []Value
+	keys(all bool, accum []Value) []Value
 
 	_putProp(name unistring.String, value Value, writable, enumerable, configurable bool) Value
 	_putSym(s *Symbol, prop Value)
@@ -938,7 +943,7 @@ func (o *baseObject) export(ctx *objectExportCtx) interface{} {
 	if v, exists := ctx.get(o); exists {
 		return v
 	}
-	keys := o.ownKeys(false, nil)
+	keys := o.stringKeys(false, nil)
 	m := make(map[string]interface{}, len(keys))
 	ctx.put(o, m)
 	for _, itemName := range keys {
@@ -967,8 +972,8 @@ const (
 )
 
 type propIterItem struct {
-	name       unistring.String
-	value      Value // set only when enumerable == _ENUM_UNKNOWN
+	name       Value
+	value      Value
 	enumerable enumerableFlag
 }
 
@@ -985,6 +990,7 @@ type recursivePropIter struct {
 }
 
 type enumerableIter struct {
+	o       *Object
 	wrapped iterNextFunc
 }
 
@@ -999,7 +1005,16 @@ func (i *enumerableIter) next() (propIterItem, iterNextFunc) {
 			continue
 		}
 		if item.enumerable == _ENUM_UNKNOWN {
-			if prop, ok := item.value.(*valueProperty); ok {
+			var prop Value
+			if item.value == nil {
+				prop = i.o.getOwnProp(item.name)
+			} else {
+				prop = item.value
+			}
+			if prop == nil {
+				continue
+			}
+			if prop, ok := prop.(*valueProperty); ok {
 				if !prop.enumerable {
 					continue
 				}
@@ -1015,14 +1030,15 @@ func (i *recursivePropIter) next() (propIterItem, iterNextFunc) {
 		item, i.cur = i.cur()
 		if i.cur == nil {
 			if proto := i.o.proto(); proto != nil {
-				i.cur = proto.self.enumerateOwnKeys()
+				i.cur = proto.self.iterateStringKeys()
 				i.o = proto.self
 				continue
 			}
 			return propIterItem{}, nil
 		}
-		if _, exists := i.seen[item.name]; !exists {
-			i.seen[item.name] = struct{}{}
+		name := item.name.string()
+		if _, exists := i.seen[name]; !exists {
+			i.seen[name] = struct{}{}
 			return item, i.next
 		}
 	}
@@ -1030,9 +1046,10 @@ func (i *recursivePropIter) next() (propIterItem, iterNextFunc) {
 
 func enumerateRecursive(o *Object) iterNextFunc {
 	return (&enumerableIter{
+		o: o,
 		wrapped: (&recursivePropIter{
 			o:    o.self,
-			cur:  o.self.enumerateOwnKeys(),
+			cur:  o.self.iterateStringKeys(),
 			seen: make(map[unistring.String]struct{}),
 		}).next,
 	}).next
@@ -1044,7 +1061,7 @@ func (i *objectPropIter) next() (propIterItem, iterNextFunc) {
 		i.idx++
 		prop := i.o.values[name]
 		if prop != nil {
-			return propIterItem{name: name, value: prop}, i.next
+			return propIterItem{name: stringValueFromRaw(name), value: prop}, i.next
 		}
 	}
 	clearNamesCopyMarker(i.propNames)
@@ -1107,7 +1124,7 @@ func copyNamesIfNeeded(names []unistring.String, extraCap int) []unistring.Strin
 	return names
 }
 
-func (o *baseObject) enumerateOwnKeys() iterNextFunc {
+func (o *baseObject) iterateStringKeys() iterNextFunc {
 	if len(o.propNames) > o.lastSortedPropLen {
 		o.fixPropOrder()
 	}
@@ -1119,6 +1136,53 @@ func (o *baseObject) enumerateOwnKeys() iterNextFunc {
 	}).next
 }
 
+type objectSymbolIter struct {
+	iter *orderedMapIter
+}
+
+func (i *objectSymbolIter) next() (propIterItem, iterNextFunc) {
+	entry := i.iter.next()
+	if entry != nil {
+		return propIterItem{
+			name:  entry.key,
+			value: entry.value,
+		}, i.next
+	}
+	return propIterItem{}, nil
+}
+
+func (o *baseObject) iterateSymbols() iterNextFunc {
+	if o.symValues != nil {
+		return (&objectSymbolIter{
+			iter: o.symValues.newIter(),
+		}).next
+	}
+	return func() (propIterItem, iterNextFunc) {
+		return propIterItem{}, nil
+	}
+}
+
+type objectAllPropIter struct {
+	o      *Object
+	curStr iterNextFunc
+}
+
+func (i *objectAllPropIter) next() (propIterItem, iterNextFunc) {
+	item, next := i.curStr()
+	if next != nil {
+		i.curStr = next
+		return item, i.next
+	}
+	return i.o.self.iterateSymbols()()
+}
+
+func (o *baseObject) iterateKeys() iterNextFunc {
+	return (&objectAllPropIter{
+		o:      o.val,
+		curStr: o.val.self.iterateStringKeys(),
+	}).next
+}
+
 func (o *baseObject) equal(objectImpl) bool {
 	// Rely on parent reference comparison
 	return false
@@ -1158,7 +1222,7 @@ func (o *baseObject) fixPropOrder() {
 	o.lastSortedPropLen = len(names)
 }
 
-func (o *baseObject) ownKeys(all bool, keys []Value) []Value {
+func (o *baseObject) stringKeys(all bool, keys []Value) []Value {
 	if len(o.propNames) > o.lastSortedPropLen {
 		o.fixPropOrder()
 	}
@@ -1178,7 +1242,7 @@ func (o *baseObject) ownKeys(all bool, keys []Value) []Value {
 	return keys
 }
 
-func (o *baseObject) ownSymbols(all bool, accum []Value) []Value {
+func (o *baseObject) symbols(all bool, accum []Value) []Value {
 	if o.symValues != nil {
 		iter := o.symValues.newIter()
 		if all {
@@ -1208,8 +1272,8 @@ func (o *baseObject) ownSymbols(all bool, accum []Value) []Value {
 	return accum
 }
 
-func (o *baseObject) ownPropertyKeys(all bool, accum []Value) []Value {
-	return o.ownSymbols(all, o.val.self.ownKeys(all, accum))
+func (o *baseObject) keys(all bool, accum []Value) []Value {
+	return o.symbols(all, o.val.self.stringKeys(all, accum))
 }
 
 func (o *baseObject) hasInstance(Value) bool {
@@ -1300,9 +1364,9 @@ func (o *Object) setStr(name unistring.String, val, receiver Value, throw bool)
 							return false
 						}
 					}
-					robj.self.defineOwnPropertyStr(name, PropertyDescriptor{Value: val}, throw)
+					return robj.self.defineOwnPropertyStr(name, PropertyDescriptor{Value: val}, throw)
 				} else {
-					robj.self.defineOwnPropertyStr(name, PropertyDescriptor{
+					return robj.self.defineOwnPropertyStr(name, PropertyDescriptor{
 						Value:        val,
 						Writable:     FLAG_TRUE,
 						Configurable: FLAG_TRUE,
@@ -1317,7 +1381,6 @@ func (o *Object) setStr(name unistring.String, val, receiver Value, throw bool)
 			return res
 		}
 	}
-	return true
 }
 
 func (o *Object) set(name Value, val, receiver Value, throw bool) bool {
@@ -1552,3 +1615,50 @@ func (ctx *objectExportCtx) putTyped(key objectImpl, typ reflect.Type, value int
 		ctx.cache[key] = m
 	}
 }
+
+type enumPropertiesIter struct {
+	o       *Object
+	wrapped iterNextFunc
+}
+
+func (i *enumPropertiesIter) next() (propIterItem, iterNextFunc) {
+	for i.wrapped != nil {
+		item, next := i.wrapped()
+		i.wrapped = next
+		if next == nil {
+			break
+		}
+		if item.value == nil {
+			item.value = i.o.get(item.name, nil)
+			if item.value == nil {
+				continue
+			}
+		} else {
+			if prop, ok := item.value.(*valueProperty); ok {
+				item.value = prop.get(i.o)
+			}
+		}
+		return item, i.next
+	}
+	return propIterItem{}, nil
+}
+
+func iterateEnumerableProperties(o *Object) iterNextFunc {
+	return (&enumPropertiesIter{
+		o: o,
+		wrapped: (&enumerableIter{
+			o:       o,
+			wrapped: o.self.iterateKeys(),
+		}).next,
+	}).next
+}
+
+func iterateEnumerableStringProperties(o *Object) iterNextFunc {
+	return (&enumPropertiesIter{
+		o: o,
+		wrapped: (&enumerableIter{
+			o:       o,
+			wrapped: o.self.iterateStringKeys(),
+		}).next,
+	}).next
+}

+ 2 - 2
object_args.go

@@ -81,9 +81,9 @@ func (i *argumentsPropIter) next() (propIterItem, iterNextFunc) {
 	return item, i.next
 }
 
-func (a *argumentsObject) enumerateOwnKeys() iterNextFunc {
+func (a *argumentsObject) iterateStringKeys() iterNextFunc {
 	return (&argumentsPropIter{
-		wrapped: a.baseObject.enumerateOwnKeys(),
+		wrapped: a.baseObject.iterateStringKeys(),
 	}).next
 }
 

+ 25 - 11
object_dynamic.go

@@ -451,13 +451,13 @@ func (i *dynamicObjectPropIter) next() (propIterItem, iterNextFunc) {
 		name := i.propNames[i.idx]
 		i.idx++
 		if i.o.d.Has(name) {
-			return propIterItem{name: unistring.NewFromString(name), enumerable: _ENUM_TRUE}, i.next
+			return propIterItem{name: newStringValue(name), enumerable: _ENUM_TRUE}, i.next
 		}
 	}
 	return propIterItem{}, nil
 }
 
-func (o *dynamicObject) enumerateOwnKeys() iterNextFunc {
+func (o *dynamicObject) iterateStringKeys() iterNextFunc {
 	keys := o.d.Keys()
 	return (&dynamicObjectPropIter{
 		o:         o,
@@ -465,6 +465,16 @@ func (o *dynamicObject) enumerateOwnKeys() iterNextFunc {
 	}).next
 }
 
+func (o *baseDynamicObject) iterateSymbols() iterNextFunc {
+	return func() (propIterItem, iterNextFunc) {
+		return propIterItem{}, nil
+	}
+}
+
+func (o *dynamicObject) iterateKeys() iterNextFunc {
+	return o.iterateStringKeys()
+}
+
 func (o *dynamicObject) export(ctx *objectExportCtx) interface{} {
 	return o.d
 }
@@ -480,7 +490,7 @@ func (o *dynamicObject) equal(impl objectImpl) bool {
 	return false
 }
 
-func (o *dynamicObject) ownKeys(all bool, accum []Value) []Value {
+func (o *dynamicObject) stringKeys(all bool, accum []Value) []Value {
 	keys := o.d.Keys()
 	if l := len(accum) + len(keys); l > cap(accum) {
 		oldAccum := accum
@@ -493,12 +503,12 @@ func (o *dynamicObject) ownKeys(all bool, accum []Value) []Value {
 	return accum
 }
 
-func (*baseDynamicObject) ownSymbols(all bool, accum []Value) []Value {
+func (*baseDynamicObject) symbols(all bool, accum []Value) []Value {
 	return accum
 }
 
-func (o *dynamicObject) ownPropertyKeys(all bool, accum []Value) []Value {
-	return o.ownKeys(all, accum)
+func (o *dynamicObject) keys(all bool, accum []Value) []Value {
+	return o.stringKeys(all, accum)
 }
 
 func (*baseDynamicObject) _putProp(name unistring.String, value Value, writable, enumerable, configurable bool) Value {
@@ -686,19 +696,23 @@ func (i *dynArrayPropIter) next() (propIterItem, iterNextFunc) {
 	if i.idx < i.limit && i.idx < i.a.Len() {
 		name := strconv.Itoa(i.idx)
 		i.idx++
-		return propIterItem{name: unistring.String(name), enumerable: _ENUM_TRUE}, i.next
+		return propIterItem{name: asciiString(name), enumerable: _ENUM_TRUE}, i.next
 	}
 
 	return propIterItem{}, nil
 }
 
-func (a *dynamicArray) enumerateOwnKeys() iterNextFunc {
+func (a *dynamicArray) iterateStringKeys() iterNextFunc {
 	return (&dynArrayPropIter{
 		a:     a.a,
 		limit: a.a.Len(),
 	}).next
 }
 
+func (a *dynamicArray) iterateKeys() iterNextFunc {
+	return a.iterateStringKeys()
+}
+
 func (a *dynamicArray) export(ctx *objectExportCtx) interface{} {
 	return a.a
 }
@@ -714,7 +728,7 @@ func (a *dynamicArray) equal(impl objectImpl) bool {
 	return false
 }
 
-func (a *dynamicArray) ownKeys(all bool, accum []Value) []Value {
+func (a *dynamicArray) stringKeys(all bool, accum []Value) []Value {
 	al := a.a.Len()
 	l := len(accum) + al
 	if all {
@@ -734,6 +748,6 @@ func (a *dynamicArray) ownKeys(all bool, accum []Value) []Value {
 	return accum
 }
 
-func (a *dynamicArray) ownPropertyKeys(all bool, accum []Value) []Value {
-	return a.ownKeys(all, accum)
+func (a *dynamicArray) keys(all bool, accum []Value) []Value {
+	return a.stringKeys(all, accum)
 }

+ 3 - 3
object_gomap.go

@@ -131,14 +131,14 @@ func (i *gomapPropIter) next() (propIterItem, iterNextFunc) {
 		name := i.propNames[i.idx]
 		i.idx++
 		if _, exists := i.o.data[name]; exists {
-			return propIterItem{name: unistring.NewFromString(name), enumerable: _ENUM_TRUE}, i.next
+			return propIterItem{name: newStringValue(name), enumerable: _ENUM_TRUE}, i.next
 		}
 	}
 
 	return propIterItem{}, nil
 }
 
-func (o *objectGoMapSimple) enumerateOwnKeys() iterNextFunc {
+func (o *objectGoMapSimple) iterateStringKeys() iterNextFunc {
 	propNames := make([]string, len(o.data))
 	i := 0
 	for key := range o.data {
@@ -152,7 +152,7 @@ func (o *objectGoMapSimple) enumerateOwnKeys() iterNextFunc {
 	}).next
 }
 
-func (o *objectGoMapSimple) ownKeys(_ bool, accum []Value) []Value {
+func (o *objectGoMapSimple) stringKeys(_ bool, accum []Value) []Value {
 	// all own keys are enumerable
 	for key := range o.data {
 		accum = append(accum, newStringValue(key))

+ 3 - 3
object_gomap_reflect.go

@@ -242,21 +242,21 @@ func (i *gomapReflectPropIter) next() (propIterItem, iterNextFunc) {
 		v := i.o.value.MapIndex(key)
 		i.idx++
 		if v.IsValid() {
-			return propIterItem{name: unistring.NewFromString(key.String()), enumerable: _ENUM_TRUE}, i.next
+			return propIterItem{name: newStringValue(key.String()), enumerable: _ENUM_TRUE}, i.next
 		}
 	}
 
 	return propIterItem{}, nil
 }
 
-func (o *objectGoMapReflect) enumerateOwnKeys() iterNextFunc {
+func (o *objectGoMapReflect) iterateStringKeys() iterNextFunc {
 	return (&gomapReflectPropIter{
 		o:    o,
 		keys: o.value.MapKeys(),
 	}).next
 }
 
-func (o *objectGoMapReflect) ownKeys(_ bool, accum []Value) []Value {
+func (o *objectGoMapReflect) stringKeys(_ bool, accum []Value) []Value {
 	// all own keys are enumerable
 	for _, key := range o.value.MapKeys() {
 		accum = append(accum, newStringValue(key.String()))

+ 4 - 4
object_goreflect.go

@@ -362,7 +362,7 @@ func (i *goreflectPropIter) nextField() (propIterItem, iterNextFunc) {
 	if i.idx < len(names) {
 		name := names[i.idx]
 		i.idx++
-		return propIterItem{name: unistring.NewFromString(name), enumerable: _ENUM_TRUE}, i.nextField
+		return propIterItem{name: newStringValue(name), enumerable: _ENUM_TRUE}, i.nextField
 	}
 
 	i.idx = 0
@@ -374,13 +374,13 @@ func (i *goreflectPropIter) nextMethod() (propIterItem, iterNextFunc) {
 	if i.idx < len(names) {
 		name := names[i.idx]
 		i.idx++
-		return propIterItem{name: unistring.NewFromString(name), enumerable: _ENUM_TRUE}, i.nextMethod
+		return propIterItem{name: newStringValue(name), enumerable: _ENUM_TRUE}, i.nextMethod
 	}
 
 	return propIterItem{}, nil
 }
 
-func (o *objectGoReflect) enumerateOwnKeys() iterNextFunc {
+func (o *objectGoReflect) iterateStringKeys() iterNextFunc {
 	r := &goreflectPropIter{
 		o: o,
 	}
@@ -391,7 +391,7 @@ func (o *objectGoReflect) enumerateOwnKeys() iterNextFunc {
 	return r.nextMethod
 }
 
-func (o *objectGoReflect) ownKeys(_ bool, accum []Value) []Value {
+func (o *objectGoReflect) stringKeys(_ bool, accum []Value) []Value {
 	// all own keys are enumerable
 	for _, name := range o.valueTypeInfo.FieldNames {
 		accum = append(accum, newStringValue(name))

+ 11 - 6
object_goslice.go

@@ -1,6 +1,8 @@
 package goja
 
 import (
+	"math"
+	"math/bits"
 	"reflect"
 	"strconv"
 
@@ -115,8 +117,11 @@ func (o *objectGoSlice) putIdx(idx int, v Value, throw bool) {
 	(*o.data)[idx] = v.Export()
 }
 
-func (o *objectGoSlice) putLength(v Value, throw bool) bool {
-	newLen := toIntStrict(toLength(v))
+func (o *objectGoSlice) putLength(v uint32, throw bool) bool {
+	if bits.UintSize == 32 && v > math.MaxInt32 {
+		panic(rangeError("Integer value overflows 32-bit int"))
+	}
+	newLen := int(v)
 	curLen := len(*o.data)
 	if newLen > curLen {
 		o.grow(newLen)
@@ -156,7 +161,7 @@ func (o *objectGoSlice) setOwnStr(name unistring.String, val Value, throw bool)
 		o.putIdx(idx, val, throw)
 	} else {
 		if name == "length" {
-			return o.putLength(val, throw)
+			return o.putLength(o.val.runtime.toLengthUint32(val), throw)
 		}
 		if res, ok := o._setForeignStr(name, nil, val, o.val, throw); !ok {
 			o.val.runtime.typeErrorResult(throw, "Can't set property '%s' on Go slice", name)
@@ -270,20 +275,20 @@ func (i *goslicePropIter) next() (propIterItem, iterNextFunc) {
 	if i.idx < i.limit && i.idx < len(*i.o.data) {
 		name := strconv.Itoa(i.idx)
 		i.idx++
-		return propIterItem{name: unistring.String(name), enumerable: _ENUM_TRUE}, i.next
+		return propIterItem{name: newStringValue(name), enumerable: _ENUM_TRUE}, i.next
 	}
 
 	return propIterItem{}, nil
 }
 
-func (o *objectGoSlice) enumerateOwnKeys() iterNextFunc {
+func (o *objectGoSlice) iterateStringKeys() iterNextFunc {
 	return (&goslicePropIter{
 		o:     o,
 		limit: len(*o.data),
 	}).next
 }
 
-func (o *objectGoSlice) ownKeys(_ bool, accum []Value) []Value {
+func (o *objectGoSlice) stringKeys(_ bool, accum []Value) []Value {
 	for i := range *o.data {
 		accum = append(accum, asciiString(strconv.Itoa(i)))
 	}

+ 16 - 8
object_goslice_reflect.go

@@ -1,6 +1,8 @@
 package goja
 
 import (
+	"math"
+	"math/bits"
 	"reflect"
 	"strconv"
 
@@ -138,8 +140,11 @@ func (o *objectGoSliceReflect) shrink(size int) {
 	o.updateLen()
 }
 
-func (o *objectGoSliceReflect) putLength(v Value, throw bool) bool {
-	newLen := toIntStrict(toLength(v))
+func (o *objectGoSliceReflect) putLength(v uint32, throw bool) bool {
+	if bits.UintSize == 32 && v > math.MaxInt32 {
+		panic(rangeError("Integer value overflows 32-bit int"))
+	}
+	newLen := int(v)
 	curLen := o.value.Len()
 	if newLen > curLen {
 		o.grow(newLen)
@@ -179,7 +184,7 @@ func (o *objectGoSliceReflect) setOwnStr(name unistring.String, val Value, throw
 		o.putIdx(idx, val, throw)
 	} else {
 		if name == "length" {
-			return o.putLength(val, throw)
+			return o.putLength(o.val.runtime.toLengthUint32(val), throw)
 		}
 		if res, ok := o._setForeignStr(name, nil, val, o.val, throw); !ok {
 			o.val.runtime.typeErrorResult(throw, "Can't set property '%s' on Go slice", name)
@@ -238,6 +243,9 @@ func (o *objectGoSliceReflect) defineOwnPropertyStr(name unistring.String, descr
 		o.putIdx(idx, val, throw)
 		return true
 	}
+	if name == "length" {
+		return o.val.runtime.defineArrayLength(&o.lengthProp, descr, o.putLength, throw)
+	}
 	o.val.runtime.typeErrorResult(throw, "Cannot define property '%s' on a Go slice", name)
 	return false
 }
@@ -288,21 +296,21 @@ func (i *gosliceReflectPropIter) next() (propIterItem, iterNextFunc) {
 	if i.idx < i.limit && i.idx < i.o.value.Len() {
 		name := strconv.Itoa(i.idx)
 		i.idx++
-		return propIterItem{name: unistring.String(name), enumerable: _ENUM_TRUE}, i.next
+		return propIterItem{name: asciiString(name), enumerable: _ENUM_TRUE}, i.next
 	}
 
-	return i.o.objectGoReflect.enumerateOwnKeys()()
+	return i.o.objectGoReflect.iterateStringKeys()()
 }
 
-func (o *objectGoSliceReflect) ownKeys(all bool, accum []Value) []Value {
+func (o *objectGoSliceReflect) stringKeys(all bool, accum []Value) []Value {
 	for i := 0; i < o.value.Len(); i++ {
 		accum = append(accum, asciiString(strconv.Itoa(i)))
 	}
 
-	return o.objectGoReflect.ownKeys(all, accum)
+	return o.objectGoReflect.stringKeys(all, accum)
 }
 
-func (o *objectGoSliceReflect) enumerateOwnKeys() iterNextFunc {
+func (o *objectGoSliceReflect) iterateStringKeys() iterNextFunc {
 	return (&gosliceReflectPropIter{
 		o:     o,
 		limit: o.value.Len(),

+ 20 - 8
object_lazy.go

@@ -223,10 +223,22 @@ func (o *lazyObject) preventExtensions(throw bool) bool {
 	return obj.preventExtensions(throw)
 }
 
-func (o *lazyObject) enumerateOwnKeys() iterNextFunc {
+func (o *lazyObject) iterateStringKeys() iterNextFunc {
 	obj := o.create(o.val)
 	o.val.self = obj
-	return obj.enumerateOwnKeys()
+	return obj.iterateStringKeys()
+}
+
+func (o *lazyObject) iterateSymbols() iterNextFunc {
+	obj := o.create(o.val)
+	o.val.self = obj
+	return obj.iterateSymbols()
+}
+
+func (o *lazyObject) iterateKeys() iterNextFunc {
+	obj := o.create(o.val)
+	o.val.self = obj
+	return obj.iterateKeys()
 }
 
 func (o *lazyObject) export(ctx *objectExportCtx) interface{} {
@@ -247,22 +259,22 @@ func (o *lazyObject) equal(other objectImpl) bool {
 	return obj.equal(other)
 }
 
-func (o *lazyObject) ownKeys(all bool, accum []Value) []Value {
+func (o *lazyObject) stringKeys(all bool, accum []Value) []Value {
 	obj := o.create(o.val)
 	o.val.self = obj
-	return obj.ownKeys(all, accum)
+	return obj.stringKeys(all, accum)
 }
 
-func (o *lazyObject) ownSymbols(all bool, accum []Value) []Value {
+func (o *lazyObject) symbols(all bool, accum []Value) []Value {
 	obj := o.create(o.val)
 	o.val.self = obj
-	return obj.ownSymbols(all, accum)
+	return obj.symbols(all, accum)
 }
 
-func (o *lazyObject) ownPropertyKeys(all bool, accum []Value) []Value {
+func (o *lazyObject) keys(all bool, accum []Value) []Value {
 	obj := o.create(o.val)
 	o.val.self = obj
-	return obj.ownPropertyKeys(all, accum)
+	return obj.keys(all, accum)
 }
 
 func (o *lazyObject) setProto(proto *Object, throw bool) bool {

+ 35 - 0
object_test.go

@@ -297,6 +297,41 @@ func TestExportToWrappedMapCustom(t *testing.T) {
 	}
 }
 
+func TestSetForeignReturnValue(t *testing.T) {
+	const SCRIPT = `
+	var array = [1, 2, 3];
+	var arrayTarget = new Proxy(array, {});
+
+	Object.preventExtensions(array);
+
+	!Reflect.set(arrayTarget, "foo", 2);
+	`
+
+	testScript1(SCRIPT, valueTrue, t)
+}
+
+func TestDefinePropertiesUndefinedVal(t *testing.T) {
+	const SCRIPT = `
+var target = {};
+var sym = Symbol();
+target[sym] = 1;
+target.foo = 2;
+target[0] = 3;
+
+var getOwnKeys = [];
+var proxy = new Proxy(target, {
+  getOwnPropertyDescriptor: function(_target, key) {
+    getOwnKeys.push(key);
+  },
+});
+
+Object.defineProperties({}, proxy);
+	true;
+	`
+
+	testScript1(SCRIPT, valueTrue, t)
+}
+
 func ExampleObject_Delete() {
 	vm := New()
 	obj := vm.NewObject()

+ 52 - 44
parser/expression.go

@@ -183,7 +183,7 @@ func (self *_parser) parseRegExpLiteral() *ast.RegExpLiteral {
 	pattern, _, err := self.scanString(offset, false)
 	endOffset := self.chrOffset
 
-	if err == nil {
+	if err == "" {
 		pattern = pattern[1 : len(pattern)-1]
 	}
 
@@ -274,12 +274,12 @@ func (self *_parser) parseVarDeclarationList(var_ file.Idx) []*ast.Binding {
 	return declarationList
 }
 
-func (self *_parser) parseObjectPropertyKey() (unistring.String, ast.Expression, token.Token) {
+func (self *_parser) parseObjectPropertyKey() (string, unistring.String, ast.Expression, token.Token) {
 	if self.token == token.LEFT_BRACKET {
 		self.next()
 		expr := self.parseAssignmentExpression()
 		self.expect(token.RIGHT_BRACKET)
-		return "", expr, token.ILLEGAL
+		return "", "", expr, token.ILLEGAL
 	}
 	idx, tkn, literal, parsedLiteral := self.idx, self.token, self.literal, self.parsedLiteral
 	var value ast.Expression
@@ -289,7 +289,7 @@ func (self *_parser) parseObjectPropertyKey() (unistring.String, ast.Expression,
 		value = &ast.StringLiteral{
 			Idx:     idx,
 			Literal: literal,
-			Value:   unistring.String(literal),
+			Value:   parsedLiteral,
 		}
 	case token.NUMBER:
 		num, err := parseNumberLiteral(literal)
@@ -302,7 +302,7 @@ func (self *_parser) parseObjectPropertyKey() (unistring.String, ast.Expression,
 				Value:   num,
 			}
 		}
-	case token.STRING:
+	case token.STRING, token.KEYWORD:
 		value = &ast.StringLiteral{
 			Idx:     idx,
 			Literal: literal,
@@ -319,7 +319,7 @@ func (self *_parser) parseObjectPropertyKey() (unistring.String, ast.Expression,
 			tkn = token.KEYWORD
 		}
 	}
-	return parsedLiteral, value, tkn
+	return literal, parsedLiteral, value, tkn
 }
 
 func (self *_parser) parseObjectProperty() ast.Property {
@@ -329,18 +329,19 @@ func (self *_parser) parseObjectProperty() ast.Property {
 			Expression: self.parseAssignmentExpression(),
 		}
 	}
-	literal, value, tkn := self.parseObjectPropertyKey()
+	keyStartIdx := self.idx
+	literal, parsedLiteral, value, tkn := self.parseObjectPropertyKey()
 	if tkn == token.IDENTIFIER || tkn == token.STRING || tkn == token.KEYWORD || tkn == token.ILLEGAL {
 		switch {
 		case self.token == token.LEFT_PARENTHESIS:
-			idx := self.idx
 			parameterList := self.parseFunctionParameterList()
 
 			node := &ast.FunctionLiteral{
-				Function:      idx,
+				Function:      keyStartIdx,
 				ParameterList: parameterList,
 			}
 			node.Body, node.DeclarationList = self.parseFunctionBlock()
+			node.Source = self.slice(keyStartIdx, node.Body.Idx1())
 
 			return &ast.PropertyKeyed{
 				Key:   value,
@@ -358,44 +359,44 @@ func (self *_parser) parseObjectProperty() ast.Property {
 				}
 				return &ast.PropertyShort{
 					Name: ast.Identifier{
-						Name: literal,
+						Name: parsedLiteral,
 						Idx:  value.Idx0(),
 					},
 					Initializer: initializer,
 				}
 			}
 		case literal == "get" && self.token != token.COLON:
-			idx := self.idx
-			_, value, _ := self.parseObjectPropertyKey()
+			_, _, keyValue, _ := self.parseObjectPropertyKey()
 			idx1 := self.idx
 			parameterList := self.parseFunctionParameterList()
 			if len(parameterList.List) > 0 || parameterList.Rest != nil {
 				self.error(idx1, "Getter must not have any formal parameters.")
 			}
 			node := &ast.FunctionLiteral{
-				Function:      idx,
+				Function:      keyStartIdx,
 				ParameterList: parameterList,
 			}
 			node.Body, node.DeclarationList = self.parseFunctionBlock()
+			node.Source = self.slice(keyStartIdx, node.Body.Idx1())
 			return &ast.PropertyKeyed{
-				Key:   value,
+				Key:   keyValue,
 				Kind:  ast.PropertyKindGet,
 				Value: node,
 			}
 		case literal == "set" && self.token != token.COLON:
-			idx := self.idx
-			_, value, _ := self.parseObjectPropertyKey()
+			_, _, keyValue, _ := self.parseObjectPropertyKey()
 			parameterList := self.parseFunctionParameterList()
 
 			node := &ast.FunctionLiteral{
-				Function:      idx,
+				Function:      keyStartIdx,
 				ParameterList: parameterList,
 			}
 
 			node.Body, node.DeclarationList = self.parseFunctionBlock()
+			node.Source = self.slice(keyStartIdx, node.Body.Idx1())
 
 			return &ast.PropertyKeyed{
-				Key:   value,
+				Key:   keyValue,
 				Kind:  ast.PropertyKindSet,
 				Value: node,
 			}
@@ -405,9 +406,10 @@ func (self *_parser) parseObjectProperty() ast.Property {
 	self.expect(token.COLON)
 
 	return &ast.PropertyKeyed{
-		Key:   value,
-		Kind:  ast.PropertyKindValue,
-		Value: self.parseAssignmentExpression(),
+		Key:      value,
+		Kind:     ast.PropertyKindValue,
+		Value:    self.parseAssignmentExpression(),
+		Computed: tkn == token.ILLEGAL,
 	}
 }
 
@@ -470,17 +472,17 @@ func (self *_parser) parseTemplateLiteral(tagged bool) *ast.TemplateLiteral {
 	for self.chr != -1 {
 		start := self.idx + 1
 		literal, parsed, finished, parseErr, err := self.parseTemplateCharacters()
-		if err != nil {
-			self.error(self.idx, err.Error())
+		if err != "" {
+			self.error(self.idx, err)
 		}
 		res.Elements = append(res.Elements, &ast.TemplateElement{
 			Idx:     start,
 			Literal: literal,
 			Parsed:  parsed,
-			Valid:   parseErr == nil,
+			Valid:   parseErr == "",
 		})
-		if !tagged && parseErr != nil {
-			self.error(self.idx, parseErr.Error())
+		if !tagged && parseErr != "" {
+			self.error(self.idx, parseErr)
 		}
 		end := self.idx + 1
 		self.next()
@@ -505,23 +507,21 @@ func (self *_parser) parseTaggedTemplateLiteral(tag ast.Expression) *ast.Templat
 
 func (self *_parser) parseArgumentList() (argumentList []ast.Expression, idx0, idx1 file.Idx) {
 	idx0 = self.expect(token.LEFT_PARENTHESIS)
-	if self.token != token.RIGHT_PARENTHESIS {
-		for {
-			var item ast.Expression
-			if self.token == token.ELLIPSIS {
-				self.next()
-				item = &ast.SpreadElement{
-					Expression: self.parseAssignmentExpression(),
-				}
-			} else {
-				item = self.parseAssignmentExpression()
-			}
-			argumentList = append(argumentList, item)
-			if self.token != token.COMMA {
-				break
-			}
+	for self.token != token.RIGHT_PARENTHESIS {
+		var item ast.Expression
+		if self.token == token.ELLIPSIS {
 			self.next()
+			item = &ast.SpreadElement{
+				Expression: self.parseAssignmentExpression(),
+			}
+		} else {
+			item = self.parseAssignmentExpression()
 		}
+		argumentList = append(argumentList, item)
+		if self.token != token.COMMA {
+			break
+		}
+		self.next()
 	}
 	idx1 = self.expect(token.RIGHT_PARENTHESIS)
 	return
@@ -549,6 +549,11 @@ func (self *_parser) parseDotMember(left ast.Expression) ast.Expression {
 		return &ast.BadExpression{From: period, To: self.idx}
 	}
 
+	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()
 
 	return &ast.DotExpression{
@@ -576,8 +581,7 @@ func (self *_parser) parseNewExpression() ast.Expression {
 	idx := self.expect(token.NEW)
 	if self.token == token.PERIOD {
 		self.next()
-		prop := self.parseIdentifier()
-		if prop.Name == "target" {
+		if self.literal == "target" {
 			if !self.scope.inFunction {
 				self.error(idx, "new.target expression is not allowed here")
 			}
@@ -586,12 +590,16 @@ func (self *_parser) parseNewExpression() ast.Expression {
 					Name: unistring.String(token.NEW.String()),
 					Idx:  idx,
 				},
-				Property: prop,
+				Property: self.parseIdentifier(),
 			}
 		}
 		self.errorUnexpectedToken(token.IDENTIFIER)
 	}
 	callee := self.parseLeftHandSideExpression()
+	if bad, ok := callee.(*ast.BadExpression); ok {
+		bad.From = idx
+		return bad
+	}
 	node := &ast.NewExpression{
 		New:    idx,
 		Callee: callee,

+ 102 - 59
parser/lexer.go

@@ -3,26 +3,43 @@ package parser
 import (
 	"errors"
 	"fmt"
-	"regexp"
 	"strconv"
 	"strings"
 	"unicode"
 	"unicode/utf16"
 	"unicode/utf8"
 
+	"golang.org/x/text/unicode/rangetable"
+
 	"github.com/dop251/goja/file"
 	"github.com/dop251/goja/token"
 	"github.com/dop251/goja/unistring"
 )
 
-var matchIdentifier = regexp.MustCompile(`^[$_\p{L}][$_\p{L}\d}]*$`)
+var (
+	unicodeRangeIdNeg      = rangetable.Merge(unicode.Pattern_Syntax, unicode.Pattern_White_Space)
+	unicodeRangeIdStartPos = rangetable.Merge(unicode.Letter, unicode.Nl, unicode.Other_ID_Start)
+	unicodeRangeIdContPos  = rangetable.Merge(unicodeRangeIdStartPos, unicode.Mn, unicode.Mc, unicode.Nd, unicode.Pc, unicode.Other_ID_Continue)
+)
 
 func isDecimalDigit(chr rune) bool {
 	return '0' <= chr && chr <= '9'
 }
 
 func IsIdentifier(s string) bool {
-	return matchIdentifier.MatchString(s)
+	if s == "" {
+		return false
+	}
+	r, size := utf8.DecodeRuneInString(s)
+	if !isIdentifierStart(r) {
+		return false
+	}
+	for _, r := range s[size:] {
+		if !isIdentifierPart(r) {
+			return false
+		}
+	}
+	return true
 }
 
 func digitValue(chr rune) int {
@@ -41,20 +58,28 @@ func isDigit(chr rune, base int) bool {
 	return digitValue(chr) < base
 }
 
+func isIdStartUnicode(r rune) bool {
+	return unicode.Is(unicodeRangeIdStartPos, r) && !unicode.Is(unicodeRangeIdNeg, r)
+}
+
+func isIdPartUnicode(r rune) bool {
+	return unicode.Is(unicodeRangeIdContPos, r) && !unicode.Is(unicodeRangeIdNeg, r) || r == '\u200C' || r == '\u200D'
+}
+
 func isIdentifierStart(chr rune) bool {
 	return chr == '$' || chr == '_' || chr == '\\' ||
 		'a' <= chr && chr <= 'z' || 'A' <= chr && chr <= 'Z' ||
-		chr >= utf8.RuneSelf && unicode.IsLetter(chr)
+		chr >= utf8.RuneSelf && isIdStartUnicode(chr)
 }
 
 func isIdentifierPart(chr rune) bool {
 	return chr == '$' || chr == '_' || chr == '\\' ||
 		'a' <= chr && chr <= 'z' || 'A' <= chr && chr <= 'Z' ||
 		'0' <= chr && chr <= '9' ||
-		chr >= utf8.RuneSelf && (unicode.IsLetter(chr) || unicode.IsDigit(chr))
+		chr >= utf8.RuneSelf && isIdPartUnicode(chr)
 }
 
-func (self *_parser) scanIdentifier() (string, unistring.String, bool, error) {
+func (self *_parser) scanIdentifier() (string, unistring.String, bool, string) {
 	offset := self.chrOffset
 	hasEscape := false
 	isUnicode := false
@@ -67,26 +92,49 @@ func (self *_parser) scanIdentifier() (string, unistring.String, bool, error) {
 			distance := self.chrOffset - offset
 			self.read()
 			if self.chr != 'u' {
-				return "", "", false, fmt.Errorf("Invalid identifier escape character: %c (%s)", self.chr, string(self.chr))
+				return "", "", false, fmt.Sprintf("Invalid identifier escape character: %c (%s)", self.chr, string(self.chr))
 			}
 			var value rune
-			for j := 0; j < 4; j++ {
+			if self._peek() == '{' {
 				self.read()
-				decimal, ok := hex2decimal(byte(self.chr))
-				if !ok {
-					return "", "", false, fmt.Errorf("Invalid identifier escape character: %c (%s)", self.chr, string(self.chr))
+				value = -1
+				for value <= utf8.MaxRune {
+					self.read()
+					if self.chr == '}' {
+						break
+					}
+					decimal, ok := hex2decimal(byte(self.chr))
+					if !ok {
+						return "", "", false, "Invalid Unicode escape sequence"
+					}
+					if value == -1 {
+						value = decimal
+					} else {
+						value = value<<4 | decimal
+					}
+				}
+				if value == -1 {
+					return "", "", false, "Invalid Unicode escape sequence"
+				}
+			} else {
+				for j := 0; j < 4; j++ {
+					self.read()
+					decimal, ok := hex2decimal(byte(self.chr))
+					if !ok {
+						return "", "", false, fmt.Sprintf("Invalid identifier escape character: %c (%s)", self.chr, string(self.chr))
+					}
+					value = value<<4 | decimal
 				}
-				value = value<<4 | decimal
 			}
 			if value == '\\' {
-				return "", "", false, fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value))
+				return "", "", false, fmt.Sprintf("Invalid identifier escape value: %c (%s)", value, string(value))
 			} else if distance == 0 {
 				if !isIdentifierStart(value) {
-					return "", "", false, fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value))
+					return "", "", false, fmt.Sprintf("Invalid identifier escape value: %c (%s)", value, string(value))
 				}
 			} else if distance > 0 {
 				if !isIdentifierPart(value) {
-					return "", "", false, fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value))
+					return "", "", false, fmt.Sprintf("Invalid identifier escape value: %c (%s)", value, string(value))
 				}
 			}
 			r = value
@@ -103,17 +151,17 @@ func (self *_parser) scanIdentifier() (string, unistring.String, bool, error) {
 	literal := self.str[offset:self.chrOffset]
 	var parsed unistring.String
 	if hasEscape || isUnicode {
-		var err error
+		var err string
 		// TODO strict
 		parsed, err = parseStringLiteral(literal, length, isUnicode, false)
-		if err != nil {
+		if err != "" {
 			return "", "", false, err
 		}
 	} else {
 		parsed = unistring.String(literal)
 	}
 
-	return literal, parsed, hasEscape, nil
+	return literal, parsed, hasEscape, ""
 }
 
 // 7.2
@@ -231,10 +279,10 @@ func (self *_parser) scan() (tkn token.Token, literal string, parsedLiteral unis
 
 		switch chr := self.chr; {
 		case isIdentifierStart(chr):
-			var err error
+			var err string
 			var hasEscape bool
 			literal, parsedLiteral, hasEscape, err = self.scanIdentifier()
-			if err != nil {
+			if err != "" {
 				tkn = token.ILLEGAL
 				break
 			}
@@ -242,34 +290,29 @@ func (self *_parser) scan() (tkn token.Token, literal string, parsedLiteral unis
 				// Keywords are longer than 1 character, avoid lookup otherwise
 				var strict bool
 				tkn, strict = token.IsKeyword(string(parsedLiteral))
-
+				if hasEscape {
+					self.insertSemicolon = true
+					if tkn != 0 && tkn != token.LET || parsedLiteral == "true" || parsedLiteral == "false" || parsedLiteral == "null" {
+						tkn = token.KEYWORD
+					} else {
+						tkn = token.IDENTIFIER
+					}
+					return
+				}
 				switch tkn {
 
 				case 0: // Not a keyword
 					if parsedLiteral == "true" || parsedLiteral == "false" {
-						if hasEscape {
-							tkn = token.STRING
-							return
-						}
 						self.insertSemicolon = true
 						tkn = token.BOOLEAN
 						return
 					} else if parsedLiteral == "null" {
-						if hasEscape {
-							tkn = token.STRING
-							return
-						}
 						self.insertSemicolon = true
 						tkn = token.NULL
 						return
 					}
 
 				case token.KEYWORD:
-					if hasEscape {
-						tkn = token.STRING
-						return
-					}
-					tkn = token.KEYWORD
 					if strict {
 						// TODO If strict and in strict mode, then this is not a break
 						break
@@ -283,17 +326,10 @@ func (self *_parser) scan() (tkn token.Token, literal string, parsedLiteral unis
 					token.RETURN,
 					token.CONTINUE,
 					token.DEBUGGER:
-					if hasEscape {
-						tkn = token.STRING
-						return
-					}
 					self.insertSemicolon = true
 					return
 
 				default:
-					if hasEscape {
-						tkn = token.STRING
-					}
 					return
 
 				}
@@ -420,9 +456,9 @@ func (self *_parser) scan() (tkn token.Token, literal string, parsedLiteral unis
 			case '"', '\'':
 				insertSemicolon = true
 				tkn = token.STRING
-				var err error
+				var err string
 				literal, parsedLiteral, err = self.scanString(self.chrOffset-1, true)
-				if err != nil {
+				if err != "" {
 					tkn = token.ILLEGAL
 				}
 			case '`':
@@ -660,7 +696,7 @@ func (self *_parser) scanEscape(quote rune) (int, bool) {
 	return 1, false
 }
 
-func (self *_parser) scanString(offset int, parse bool) (literal string, parsed unistring.String, err error) {
+func (self *_parser) scanString(offset int, parse bool) (literal string, parsed unistring.String, err string) {
 	// " ' /
 	quote := rune(self.str[offset])
 	length := 0
@@ -717,7 +753,7 @@ newline:
 		errStr = "Invalid regular expression: missing /"
 		self.error(self.idxOf(offset), errStr)
 	}
-	return "", "", errors.New(errStr)
+	return "", "", errStr
 }
 
 func (self *_parser) scanNewline() {
@@ -730,7 +766,7 @@ func (self *_parser) scanNewline() {
 	self.read()
 }
 
-func (self *_parser) parseTemplateCharacters() (literal string, parsed unistring.String, finished bool, parseErr, err error) {
+func (self *_parser) parseTemplateCharacters() (literal string, parsed unistring.String, finished bool, parseErr, err string) {
 	offset := self.chrOffset
 	var end int
 	length := 0
@@ -754,6 +790,11 @@ func (self *_parser) parseTemplateCharacters() (literal string, parsed unistring
 				}
 				self.scanNewline()
 			} else {
+				if self.chr == '8' || self.chr == '9' {
+					if parseErr == "" {
+						parseErr = "\\8 and \\9 are not allowed in template strings."
+					}
+				}
 				l, u := self.scanEscape('`')
 				length += l
 				if u {
@@ -784,11 +825,13 @@ func (self *_parser) parseTemplateCharacters() (literal string, parsed unistring
 	if hasCR {
 		literal = normaliseCRLF(literal)
 	}
-	parsed, parseErr = parseStringLiteral(literal, length, isUnicode, true)
+	if parseErr == "" {
+		parsed, parseErr = parseStringLiteral(literal, length, isUnicode, true)
+	}
 	self.insertSemicolon = true
 	return
 unterminated:
-	err = errors.New(err_UnexpectedEndOfInput)
+	err = err_UnexpectedEndOfInput
 	return
 }
 
@@ -862,7 +905,7 @@ error:
 	return nil, errors.New("Illegal numeric literal")
 }
 
-func parseStringLiteral(literal string, length int, unicode, strict bool) (unistring.String, error) {
+func parseStringLiteral(literal string, length int, unicode, strict bool) (unistring.String, string) {
 	var sb strings.Builder
 	var chars []uint16
 	if unicode {
@@ -937,12 +980,12 @@ func parseStringLiteral(literal string, length int, unicode, strict bool) (unist
 				}
 				if size > 0 {
 					if len(str) < size {
-						return "", fmt.Errorf("invalid escape: \\%s: len(%q) != %d", string(chr), str, size)
+						return "", fmt.Sprintf("invalid escape: \\%s: len(%q) != %d", string(chr), str, size)
 					}
 					for j := 0; j < size; j++ {
 						decimal, ok := hex2decimal(str[j])
 						if !ok {
-							return "", fmt.Errorf("invalid escape: \\%s: %q", string(chr), str[:size])
+							return "", fmt.Sprintf("invalid escape: \\%s: %q", string(chr), str[:size])
 						}
 						value = value<<4 | decimal
 					}
@@ -953,7 +996,7 @@ func parseStringLiteral(literal string, length int, unicode, strict bool) (unist
 					for ; size < len(str); size++ {
 						if str[size] == '}' {
 							if size == 0 {
-								return "", fmt.Errorf("invalid escape: \\%s", string(chr))
+								return "", fmt.Sprintf("invalid escape: \\%s", string(chr))
 							}
 							size++
 							value = val
@@ -961,15 +1004,15 @@ func parseStringLiteral(literal string, length int, unicode, strict bool) (unist
 						}
 						decimal, ok := hex2decimal(str[size])
 						if !ok {
-							return "", fmt.Errorf("invalid escape: \\%s: %q", string(chr), str[:size+1])
+							return "", fmt.Sprintf("invalid escape: \\%s: %q", string(chr), str[:size+1])
 						}
 						val = val<<4 | decimal
 						if val > utf8.MaxRune {
-							return "", fmt.Errorf("undefined Unicode code-point: %q", str[:size+1])
+							return "", fmt.Sprintf("undefined Unicode code-point: %q", str[:size+1])
 						}
 					}
 					if value == -1 {
-						return "", fmt.Errorf("unterminated \\u{: %q", str)
+						return "", fmt.Sprintf("unterminated \\u{: %q", str)
 					}
 				}
 				str = str[size:]
@@ -987,7 +1030,7 @@ func parseStringLiteral(literal string, length int, unicode, strict bool) (unist
 				fallthrough
 			case '1', '2', '3', '4', '5', '6', '7':
 				if strict {
-					return "", errors.New("Octal escape sequences are not allowed in this context")
+					return "", "Octal escape sequences are not allowed in this context"
 				}
 				value = rune(chr) - '0'
 				j := 0
@@ -1029,7 +1072,7 @@ func parseStringLiteral(literal string, length int, unicode, strict bool) (unist
 			}
 		} else {
 			if value >= utf8.RuneSelf {
-				return "", fmt.Errorf("Unexpected unicode character")
+				return "", "Unexpected unicode character"
 			}
 			sb.WriteByte(byte(value))
 		}
@@ -1039,12 +1082,12 @@ func parseStringLiteral(literal string, length int, unicode, strict bool) (unist
 		if len(chars) != length+1 {
 			panic(fmt.Errorf("unexpected unicode length while parsing '%s'", literal))
 		}
-		return unistring.FromUtf16(chars), nil
+		return unistring.FromUtf16(chars), ""
 	}
 	if sb.Len() != length {
 		panic(fmt.Errorf("unexpected length while parsing '%s'", literal))
 	}
-	return unistring.String(sb.String()), nil
+	return unistring.String(sb.String()), ""
 }
 
 func (self *_parser) scanNumericLiteral(decimalPoint bool) (token.Token, string) {

+ 7 - 4
parser/parser_test.go

@@ -450,6 +450,8 @@ func TestParserErr(t *testing.T) {
 			test("{a: 1,}", "(anonymous): Line 1:7 Unexpected token }")
 			test("{a: 1, b: 2}", "(anonymous): Line 1:9 Unexpected token :")
 			test("{a: 1, b: 2,}", "(anonymous): Line 1:9 Unexpected token :")
+			test(`let f = () => new import('');`, "(anonymous): Line 1:19 Unexpected reserved word")
+
 		}
 
 		{ // Reserved words (strict)
@@ -502,6 +504,7 @@ func TestParser(t *testing.T) {
 			is(firstErr(err), chk)
 			return program
 		}
+		test(`new (() => {});`, nil)
 
 		test(`
             abc
@@ -909,7 +912,7 @@ func Test_parseStringLiteral(t *testing.T) {
 			parser.read()
 			parser.read()
 			_, res, err := parser.scanString(0, true)
-			is(err, nil)
+			is(err, "")
 			is(res, want)
 		}
 
@@ -964,7 +967,7 @@ func Test_parseStringLiteral(t *testing.T) {
 			parser.read()
 			parser.read()
 			_, res, err := parser.scanString(0, true)
-			is(err.Error(), want)
+			is(err, want)
 			is(res, "")
 		}
 
@@ -1104,7 +1107,7 @@ func TestParseTemplateCharacters(t *testing.T) {
 	}
 	checkParseTemplateChars := func(expectedLiteral string, expectedParsed unistring.String, expectedFinished, expectParseErr, expectErr bool) {
 		literal, parsed, finished, parseErr, err := parser.parseTemplateCharacters()
-		if err != nil != expectErr {
+		if err != "" != expectErr {
 			t.Fatal(err)
 		}
 		if literal != expectedLiteral {
@@ -1116,7 +1119,7 @@ func TestParseTemplateCharacters(t *testing.T) {
 		if finished != expectedFinished {
 			t.Fatal(finished)
 		}
-		if parseErr != nil != expectParseErr {
+		if parseErr != "" != expectParseErr {
 			t.Fatalf("parseErr: %v", parseErr)
 		}
 	}

+ 3 - 2
parser/regexp.go

@@ -9,6 +9,7 @@ import (
 
 const (
 	WhitespaceChars = " \f\n\r\t\v\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000\ufeff"
+	Re2Dot          = "[^\r\n\u2028\u2029]"
 )
 
 type regexpParseError struct {
@@ -146,7 +147,7 @@ func (self *_RegExp_parser) scan() {
 			self.error(true, "Unmatched ')'")
 			return
 		case '.':
-			self.writeString("[^\\r\\n]")
+			self.writeString(Re2Dot)
 			self.read()
 		default:
 			self.pass()
@@ -184,7 +185,7 @@ func (self *_RegExp_parser) scanGroup() {
 		case '[':
 			self.scanBracket()
 		case '.':
-			self.writeString("[^\\r\\n]")
+			self.writeString(Re2Dot)
 			self.read()
 		default:
 			self.pass()

+ 2 - 2
parser/regexp_test.go

@@ -117,7 +117,7 @@ func TestRegExp(t *testing.T) {
 
 			test("\\04", "\\x04")
 
-			test(`(.)^`, "([^\\r\\n])^")
+			test(`(.)^`, "("+Re2Dot+")^")
 
 			test(`\$`, `\$`)
 
@@ -131,7 +131,7 @@ func TestRegExp(t *testing.T) {
 
 			test(`😊`, `😊`)
 
-			test(`^.*`, `^[^\r\n]*`)
+			test(`^.*`, `^`+Re2Dot+`*`)
 
 			test(`(\n)`, `(\n)`)
 

+ 3 - 3
parser/statement.go

@@ -458,14 +458,14 @@ func (self *_parser) parseForOrForInStatement() ast.Statement {
 				}
 			}
 			if forIn || forOf {
+				if list[0].Initializer != nil {
+					self.error(list[0].Initializer.Idx0(), "for-in loop variable declaration may not have an initializer")
+				}
 				if tok == token.VAR {
 					into = &ast.ForIntoVar{
 						Binding: list[0],
 					}
 				} else {
-					if list[0].Initializer != nil {
-						self.error(list[0].Initializer.Idx0(), "for-in loop variable declaration may not have an initializer")
-					}
 					into = &ast.ForDeclaration{
 						Idx:     idx,
 						IsConst: tok == token.CONST,

+ 54 - 48
proxy.go

@@ -28,24 +28,12 @@ func (i *proxyPropIter) next() (propIterItem, iterNextFunc) {
 	for i.idx < len(i.names) {
 		name := i.names[i.idx]
 		i.idx++
-		if prop := i.p.val.getOwnProp(name); prop != nil {
-			return propIterItem{name: name.string(), value: prop}, i.next
-		}
+		return propIterItem{name: name}, i.next
 	}
 	return propIterItem{}, nil
 }
 
 func (r *Runtime) newProxyObject(target, handler, proto *Object) *proxyObject {
-	if p, ok := target.self.(*proxyObject); ok {
-		if p.handler == nil {
-			panic(r.NewTypeError("Cannot create proxy with a revoked proxy as target"))
-		}
-	}
-	if p, ok := handler.self.(*proxyObject); ok {
-		if p.handler == nil {
-			panic(r.NewTypeError("Cannot create proxy with a revoked proxy as handler"))
-		}
-	}
 	return r._newProxyObject(target, &jsProxyHandler{handler: handler}, proto)
 }
 
@@ -726,7 +714,7 @@ func (p *proxyObject) setForeignSym(s *Symbol, v, receiver Value, throw bool) (b
 	return p.proxySetSym(s, v, receiver, throw), true
 }
 
-func (p *proxyObject) proxyDeleteCheck(trapResult bool, targetProp Value, name fmt.Stringer, target *Object) {
+func (p *proxyObject) proxyDeleteCheck(trapResult bool, targetProp Value, name fmt.Stringer, target *Object, throw bool) {
 	if trapResult {
 		if targetProp == nil {
 			return
@@ -739,13 +727,15 @@ func (p *proxyObject) proxyDeleteCheck(trapResult bool, targetProp Value, name f
 		if !target.self.isExtensible() {
 			panic(p.val.runtime.NewTypeError("'deleteProperty' on proxy: trap returned truish for property '%s' but the proxy target is non-extensible", name.String()))
 		}
+	} else {
+		p.val.runtime.typeErrorResult(throw, "'deleteProperty' on proxy: trap returned falsish for property '%s'", name.String())
 	}
 }
 
 func (p *proxyObject) deleteStr(name unistring.String, throw bool) bool {
 	target := p.target
 	if v, ok := p.checkHandler().deleteStr(target, name); ok {
-		p.proxyDeleteCheck(v, target.self.getOwnPropStr(name), name, target)
+		p.proxyDeleteCheck(v, target.self.getOwnPropStr(name), name, target, throw)
 		return v
 	}
 
@@ -755,7 +745,7 @@ func (p *proxyObject) deleteStr(name unistring.String, throw bool) bool {
 func (p *proxyObject) deleteIdx(idx valueInt, throw bool) bool {
 	target := p.target
 	if v, ok := p.checkHandler().deleteIdx(target, idx); ok {
-		p.proxyDeleteCheck(v, target.self.getOwnPropIdx(idx), idx, target)
+		p.proxyDeleteCheck(v, target.self.getOwnPropIdx(idx), idx, target, throw)
 		return v
 	}
 
@@ -765,20 +755,20 @@ func (p *proxyObject) deleteIdx(idx valueInt, throw bool) bool {
 func (p *proxyObject) deleteSym(s *Symbol, throw bool) bool {
 	target := p.target
 	if v, ok := p.checkHandler().deleteSym(target, s); ok {
-		p.proxyDeleteCheck(v, target.self.getOwnPropSym(s), s, target)
+		p.proxyDeleteCheck(v, target.self.getOwnPropSym(s), s, target, throw)
 		return v
 	}
 
 	return target.self.deleteSym(s, throw)
 }
 
-func (p *proxyObject) ownPropertyKeys(all bool, _ []Value) []Value {
+func (p *proxyObject) keys(all bool, _ []Value) []Value {
 	if v, ok := p.proxyOwnKeys(); ok {
 		if !all {
 			k := 0
 			for i, key := range v {
 				prop := p.val.getOwnProp(key)
-				if prop == nil {
+				if prop == nil || prop == _undefined {
 					continue
 				}
 				if prop, ok := prop.(*valueProperty); ok && !prop.enumerable {
@@ -793,7 +783,7 @@ func (p *proxyObject) ownPropertyKeys(all bool, _ []Value) []Value {
 		}
 		return v
 	}
-	return p.target.self.ownPropertyKeys(all, nil)
+	return p.target.self.keys(all, nil)
 }
 
 func (p *proxyObject) proxyOwnKeys() ([]Value, bool) {
@@ -817,16 +807,21 @@ func (p *proxyObject) proxyOwnKeys() ([]Value, bool) {
 			keySet[item] = struct{}{}
 		}
 		ext := target.self.isExtensible()
-		for _, itemName := range target.self.ownPropertyKeys(true, nil) {
-			if _, exists := keySet[itemName]; exists {
-				delete(keySet, itemName)
+		for item, next := target.self.iterateKeys()(); next != nil; item, next = next() {
+			if _, exists := keySet[item.name]; exists {
+				delete(keySet, item.name)
 			} else {
 				if !ext {
-					panic(p.val.runtime.NewTypeError("'ownKeys' on proxy: trap result did not include '%s'", itemName.String()))
+					panic(p.val.runtime.NewTypeError("'ownKeys' on proxy: trap result did not include '%s'", item.name.String()))
+				}
+				var prop Value
+				if item.value == nil {
+					prop = target.getOwnProp(item.name)
+				} else {
+					prop = item.value
 				}
-				prop := target.getOwnProp(itemName)
 				if prop, ok := prop.(*valueProperty); ok && !prop.configurable {
-					panic(p.val.runtime.NewTypeError("'ownKeys' on proxy: trap result did not include non-configurable '%s'", itemName.String()))
+					panic(p.val.runtime.NewTypeError("'ownKeys' on proxy: trap result did not include non-configurable '%s'", item.name.String()))
 				}
 			}
 		}
@@ -840,10 +835,24 @@ func (p *proxyObject) proxyOwnKeys() ([]Value, bool) {
 	return nil, false
 }
 
-func (p *proxyObject) enumerateOwnKeys() iterNextFunc {
+func (p *proxyObject) iterateStringKeys() iterNextFunc {
 	return (&proxyPropIter{
 		p:     p,
-		names: p.ownKeys(true, nil),
+		names: p.stringKeys(true, nil),
+	}).next
+}
+
+func (p *proxyObject) iterateSymbols() iterNextFunc {
+	return (&proxyPropIter{
+		p:     p,
+		names: p.symbols(true, nil),
+	}).next
+}
+
+func (p *proxyObject) iterateKeys() iterNextFunc {
+	return (&proxyPropIter{
+		p:     p,
+		names: p.keys(true, nil),
 	}).next
 }
 
@@ -891,14 +900,6 @@ func (p *proxyObject) __isCompatibleDescriptor(extensible bool, desc *PropertyDe
 		return extensible
 	}
 
-	/*if desc.Empty() {
-		return true
-	}*/
-
-	/*if p.__isEquivalentDescriptor(desc, current) {
-		return true
-	}*/
-
 	if !current.configurable {
 		if desc.Configurable == FLAG_TRUE {
 			return false
@@ -999,25 +1000,30 @@ func (p *proxyObject) filterKeys(vals []Value, all, symbols bool) []Value {
 	return vals
 }
 
-func (p *proxyObject) ownKeys(all bool, _ []Value) []Value { // we can assume accum is empty
+func (p *proxyObject) stringKeys(all bool, _ []Value) []Value { // we can assume accum is empty
+	var keys []Value
 	if vals, ok := p.proxyOwnKeys(); ok {
-		return p.filterKeys(vals, all, false)
+		keys = vals
+	} else {
+		keys = p.target.self.stringKeys(true, nil)
 	}
 
-	return p.target.self.ownKeys(all, nil)
+	return p.filterKeys(keys, all, false)
 }
 
-func (p *proxyObject) ownSymbols(all bool, accum []Value) []Value {
+func (p *proxyObject) symbols(all bool, accum []Value) []Value {
+	var symbols []Value
 	if vals, ok := p.proxyOwnKeys(); ok {
-		res := p.filterKeys(vals, all, true)
-		if accum == nil {
-			return res
-		}
-		accum = append(accum, res...)
-		return accum
+		symbols = vals
+	} else {
+		symbols = p.target.self.symbols(true, nil)
 	}
-
-	return p.target.self.ownSymbols(all, accum)
+	symbols = p.filterKeys(symbols, all, true)
+	if accum == nil {
+		return symbols
+	}
+	accum = append(accum, symbols...)
+	return accum
 }
 
 func (p *proxyObject) className() string {

+ 4 - 3
regexp.go

@@ -511,10 +511,11 @@ func (r *regexpObject) execResultToArray(target valueString, result []int) Value
 	captureCount := len(result) >> 1
 	valueArray := make([]Value, captureCount)
 	matchIndex := result[0]
-	lowerBound := matchIndex
-	for index := 0; index < captureCount; index++ {
+	valueArray[0] = target.substring(result[0], result[1])
+	lowerBound := 0
+	for index := 1; index < captureCount; index++ {
 		offset := index << 1
-		if result[offset] >= lowerBound {
+		if result[offset] >= 0 && result[offset+1] >= lowerBound {
 			valueArray[index] = target.substring(result[offset], result[offset+1])
 			lowerBound = result[offset]
 		} else {

+ 153 - 40
runtime.go

@@ -397,15 +397,26 @@ func (r *Runtime) init() {
 	r.initSet()
 	r.initPromise()
 
-	r.global.thrower = r.newNativeFunc(r.builtin_thrower, nil, "thrower", nil, 0)
+	r.global.thrower = r.newNativeFunc(r.builtin_thrower, nil, "", nil, 0)
 	r.global.throwerProperty = &valueProperty{
 		getterFunc: r.global.thrower,
 		setterFunc: r.global.thrower,
 		accessor:   true,
 	}
+	r.object_freeze(FunctionCall{Arguments: []Value{r.global.thrower}})
 
-	funcProtoObj._put("caller", r.global.throwerProperty)
-	funcProtoObj._put("arguments", r.global.throwerProperty)
+	funcProtoObj._put("caller", &valueProperty{
+		getterFunc:   r.global.thrower,
+		setterFunc:   r.global.thrower,
+		accessor:     true,
+		configurable: true,
+	})
+	funcProtoObj._put("arguments", &valueProperty{
+		getterFunc:   r.global.thrower,
+		setterFunc:   r.global.thrower,
+		accessor:     true,
+		configurable: true,
+	})
 }
 
 func (r *Runtime) typeErrorResult(throw bool, args ...interface{}) {
@@ -495,7 +506,7 @@ func (r *Runtime) NewGoError(err error) *Object {
 	return e
 }
 
-func (r *Runtime) newFunc(name unistring.String, len int, strict bool) (f *funcObject) {
+func (r *Runtime) newFunc(name unistring.String, length int, strict bool) (f *funcObject) {
 	v := &Object{runtime: r}
 
 	f = &funcObject{}
@@ -505,11 +516,25 @@ func (r *Runtime) newFunc(name unistring.String, len int, strict bool) (f *funcO
 	f.strict = strict
 	v.self = f
 	f.prototype = r.global.FunctionPrototype
-	f.init(name, len)
+	f.init(name, intToValue(int64(length)))
 	return
 }
 
-func (r *Runtime) newArrowFunc(name unistring.String, len int, strict bool) (f *arrowFuncObject) {
+func (r *Runtime) newMethod(name unistring.String, length int, strict bool) (f *methodFuncObject) {
+	v := &Object{runtime: r}
+
+	f = &methodFuncObject{}
+	f.class = classFunction
+	f.val = v
+	f.extensible = true
+	f.strict = strict
+	v.self = f
+	f.prototype = r.global.FunctionPrototype
+	f.init(name, intToValue(int64(length)))
+	return
+}
+
+func (r *Runtime) newArrowFunc(name unistring.String, length int, strict bool) (f *arrowFuncObject) {
 	v := &Object{runtime: r}
 
 	f = &arrowFuncObject{}
@@ -530,11 +555,11 @@ func (r *Runtime) newArrowFunc(name unistring.String, len int, strict bool) (f *
 	f.newTarget = vm.newTarget
 	v.self = f
 	f.prototype = r.global.FunctionPrototype
-	f.init(name, len)
+	f.init(name, intToValue(int64(length)))
 	return
 }
 
-func (r *Runtime) newNativeFuncObj(v *Object, call func(FunctionCall) Value, construct func(args []Value, proto *Object) *Object, name unistring.String, proto *Object, length int) *nativeFuncObject {
+func (r *Runtime) newNativeFuncObj(v *Object, call func(FunctionCall) Value, construct func(args []Value, proto *Object) *Object, name unistring.String, proto *Object, length Value) *nativeFuncObject {
 	f := &nativeFuncObject{
 		baseFuncObject: baseFuncObject{
 			baseObject: baseObject{
@@ -555,7 +580,7 @@ func (r *Runtime) newNativeFuncObj(v *Object, call func(FunctionCall) Value, con
 	return f
 }
 
-func (r *Runtime) newNativeConstructor(call func(ConstructorCall) *Object, name unistring.String, length int) *Object {
+func (r *Runtime) newNativeConstructor(call func(ConstructorCall) *Object, name unistring.String, length int64) *Object {
 	v := &Object{runtime: r}
 
 	f := &nativeFuncObject{
@@ -589,7 +614,7 @@ func (r *Runtime) newNativeConstructor(call func(ConstructorCall) *Object, name
 	}
 
 	v.self = f
-	f.init(name, length)
+	f.init(name, intToValue(length))
 
 	proto := r.NewObject()
 	proto.self._putProp("constructor", v, true, false, true)
@@ -598,7 +623,7 @@ func (r *Runtime) newNativeConstructor(call func(ConstructorCall) *Object, name
 	return v
 }
 
-func (r *Runtime) newNativeConstructOnly(v *Object, ctor func(args []Value, newTarget *Object) *Object, defaultProto *Object, name unistring.String, length int) *nativeFuncObject {
+func (r *Runtime) newNativeConstructOnly(v *Object, ctor func(args []Value, newTarget *Object) *Object, defaultProto *Object, name unistring.String, length int64) *nativeFuncObject {
 	if v == nil {
 		v = &Object{runtime: r}
 	}
@@ -623,7 +648,7 @@ func (r *Runtime) newNativeConstructOnly(v *Object, ctor func(args []Value, newT
 		},
 	}
 	v.self = f
-	f.init(name, length)
+	f.init(name, intToValue(length))
 	if defaultProto != nil {
 		f._putProp("prototype", defaultProto, false, false, false)
 	}
@@ -647,7 +672,7 @@ func (r *Runtime) newNativeFunc(call func(FunctionCall) Value, construct func(ar
 		construct: r.wrapNativeConstruct(construct, proto),
 	}
 	v.self = f
-	f.init(name, length)
+	f.init(name, intToValue(int64(length)))
 	if proto != nil {
 		f._putProp("prototype", proto, false, false, false)
 		proto.self._putProp("constructor", v, true, false, true)
@@ -669,18 +694,18 @@ func (r *Runtime) newNativeFuncConstructObj(v *Object, construct func(args []Val
 		construct: r.wrapNativeConstruct(construct, proto),
 	}
 
-	f.init(name, length)
+	f.init(name, intToValue(int64(length)))
 	if proto != nil {
 		f._putProp("prototype", proto, false, false, false)
 	}
 	return f
 }
 
-func (r *Runtime) newNativeFuncConstruct(construct func(args []Value, proto *Object) *Object, name unistring.String, prototype *Object, length int) *Object {
+func (r *Runtime) newNativeFuncConstruct(construct func(args []Value, proto *Object) *Object, name unistring.String, prototype *Object, length int64) *Object {
 	return r.newNativeFuncConstructProto(construct, name, prototype, r.global.FunctionPrototype, length)
 }
 
-func (r *Runtime) newNativeFuncConstructProto(construct func(args []Value, proto *Object) *Object, name unistring.String, prototype, proto *Object, length int) *Object {
+func (r *Runtime) newNativeFuncConstructProto(construct func(args []Value, proto *Object) *Object, name unistring.String, prototype, proto *Object, length int64) *Object {
 	v := &Object{runtime: r}
 
 	f := &nativeFuncObject{}
@@ -691,7 +716,7 @@ func (r *Runtime) newNativeFuncConstructProto(construct func(args []Value, proto
 	f.prototype = proto
 	f.f = r.constructToCall(construct, prototype)
 	f.construct = r.wrapNativeConstruct(construct, prototype)
-	f.init(name, length)
+	f.init(name, intToValue(length))
 	if prototype != nil {
 		f._putProp("prototype", prototype, false, false, false)
 		prototype.self._putProp("constructor", v, true, false, true)
@@ -759,14 +784,14 @@ func (r *Runtime) builtin_newBoolean(args []Value, proto *Object) *Object {
 
 func (r *Runtime) error_toString(call FunctionCall) Value {
 	var nameStr, msgStr valueString
-	obj := call.This.ToObject(r).self
-	name := obj.getStr("name", nil)
+	obj := r.toObject(call.This)
+	name := obj.self.getStr("name", nil)
 	if name == nil || name == _undefined {
 		nameStr = asciiString("Error")
 	} else {
 		nameStr = name.toString()
 	}
-	msg := obj.getStr("message", nil)
+	msg := obj.self.getStr("message", nil)
 	if msg == nil || msg == _undefined {
 		msgStr = stringEmpty
 	} else {
@@ -1095,6 +1120,45 @@ func toLength(v Value) int64 {
 	return i
 }
 
+func (r *Runtime) toLengthUint32(v Value) uint32 {
+	var intVal int64
+repeat:
+	switch num := v.(type) {
+	case valueInt:
+		intVal = int64(num)
+	case valueFloat:
+		if v != _negativeZero {
+			if i, ok := floatToInt(float64(num)); ok {
+				intVal = i
+			} else {
+				goto fail
+			}
+		}
+	case valueString:
+		v = num.ToNumber()
+		goto repeat
+	default:
+		// Legacy behaviour as specified in https://tc39.es/ecma262/#sec-arraysetlength (see the note)
+		n2 := toUint32(v)
+		n1 := v.ToNumber()
+		if f, ok := n1.(valueFloat); ok {
+			f := float64(f)
+			if f != 0 || !math.Signbit(f) {
+				goto fail
+			}
+		}
+		if n1.ToInteger() != int64(n2) {
+			goto fail
+		}
+		return n2
+	}
+	if intVal >= 0 && intVal <= math.MaxUint32 {
+		return uint32(intVal)
+	}
+fail:
+	panic(r.newError(r.global.RangeError, "Invalid array length"))
+}
+
 func toIntStrict(i int64) int {
 	if bits.UintSize == 32 {
 		if i > math.MaxInt32 || i < math.MinInt32 {
@@ -1117,12 +1181,12 @@ func toIntClamp(i int64) int {
 }
 
 func (r *Runtime) toIndex(v Value) int {
-	intIdx := v.ToInteger()
-	if intIdx >= 0 && intIdx < maxInt {
-		if bits.UintSize == 32 && intIdx >= math.MaxInt32 {
+	num := v.ToInteger()
+	if num >= 0 && num < maxInt {
+		if bits.UintSize == 32 && num >= math.MaxInt32 {
 			panic(r.newError(r.global.RangeError, "Index %s overflows int", v.String()))
 		}
-		return int(intIdx)
+		return int(num)
 	}
 	panic(r.newError(r.global.RangeError, "Invalid index %s", v.String()))
 }
@@ -1941,14 +2005,15 @@ func (r *Runtime) toReflectValue(v Value, dst reflect.Value, ctx *objectExportCt
 			elemTyp := typ.Elem()
 			needConvertKeys := !reflect.ValueOf("").Type().AssignableTo(keyTyp)
 			iter := &enumerableIter{
-				wrapped: o.self.enumerateOwnKeys(),
+				o:       o,
+				wrapped: o.self.iterateStringKeys(),
 			}
 			for item, next := iter.next(); next != nil; item, next = next() {
 				var kv reflect.Value
 				var err error
 				if needConvertKeys {
 					kv = reflect.New(keyTyp).Elem()
-					err = r.toReflectValue(stringValueFromRaw(item.name), kv, ctx)
+					err = r.toReflectValue(item.name, kv, ctx)
 					if err != nil {
 						return fmt.Errorf("could not convert map key %s to %v", item.name.String(), typ)
 					}
@@ -1956,7 +2021,7 @@ func (r *Runtime) toReflectValue(v Value, dst reflect.Value, ctx *objectExportCt
 					kv = reflect.ValueOf(item.name.String())
 				}
 
-				ival := o.self.getStr(item.name, nil)
+				ival := o.self.getStr(item.name.string(), nil)
 				if ival != nil {
 					vv := reflect.New(elemTyp).Elem()
 					err := r.toReflectValue(ival, vv, ctx)
@@ -2302,13 +2367,26 @@ func (r *Runtime) speciesConstructorObj(o, defaultConstructor *Object) *Object {
 	if c == nil || c == _undefined || c == _null {
 		return defaultConstructor
 	}
-	return r.toObject(c)
+	obj := r.toObject(c)
+	if obj.self.assertConstructor() == nil {
+		panic(r.NewTypeError("Value is not a constructor"))
+	}
+	return obj
 }
 
 func (r *Runtime) returnThis(call FunctionCall) Value {
 	return call.This
 }
 
+func createDataProperty(o *Object, p Value, v Value) {
+	o.defineOwnProperty(p, PropertyDescriptor{
+		Writable:     FLAG_TRUE,
+		Enumerable:   FLAG_TRUE,
+		Configurable: FLAG_TRUE,
+		Value:        v,
+	}, false)
+}
+
 func createDataPropertyOrThrow(o *Object, p Value, v Value) {
 	o.defineOwnProperty(p, PropertyDescriptor{
 		Writable:     FLAG_TRUE,
@@ -2332,7 +2410,12 @@ func (r *Runtime) getV(v Value, p Value) Value {
 	return o.get(p, v)
 }
 
-func (r *Runtime) getIterator(obj Value, method func(FunctionCall) Value) *Object {
+type iteratorRecord struct {
+	iterator *Object
+	next     func(FunctionCall) Value
+}
+
+func (r *Runtime) getIterator(obj Value, method func(FunctionCall) Value) *iteratorRecord {
 	if method == nil {
 		method = toMethod(r.getV(obj, SymIterator))
 		if method == nil {
@@ -2340,21 +2423,20 @@ func (r *Runtime) getIterator(obj Value, method func(FunctionCall) Value) *Objec
 		}
 	}
 
-	return r.toObject(method(FunctionCall{
+	iter := r.toObject(method(FunctionCall{
 		This: obj,
 	}))
-}
-
-func returnIter(iter *Object) {
-	retMethod := toMethod(iter.self.getStr("return", nil))
-	if retMethod != nil {
-		iter.runtime.toObject(retMethod(FunctionCall{This: iter}))
+	next := toMethod(iter.self.getStr("next", nil))
+	return &iteratorRecord{
+		iterator: iter,
+		next:     next,
 	}
 }
 
-func (r *Runtime) iterate(iter *Object, step func(Value)) {
+func (ir *iteratorRecord) iterate(step func(Value)) {
+	r := ir.iterator.runtime
 	for {
-		res := r.toObject(toMethod(iter.self.getStr("next", nil))(FunctionCall{This: iter}))
+		res := r.toObject(ir.next(FunctionCall{This: ir.iterator}))
 		if nilSafe(res.self.getStr("done", nil)).ToBoolean() {
 			break
 		}
@@ -2364,13 +2446,44 @@ func (r *Runtime) iterate(iter *Object, step func(Value)) {
 		})
 		if ret != nil {
 			_ = tryFunc(func() {
-				returnIter(iter)
+				ir.returnIter()
 			})
 			panic(ret)
 		}
 	}
 }
 
+func (ir *iteratorRecord) step() (value Value, ex *Exception) {
+	r := ir.iterator.runtime
+	ex = r.vm.try(func() {
+		res := r.toObject(ir.next(FunctionCall{This: ir.iterator}))
+		done := nilSafe(res.self.getStr("done", nil)).ToBoolean()
+		if !done {
+			value = nilSafe(res.self.getStr("value", nil))
+		} else {
+			ir.close()
+		}
+	})
+	return
+}
+
+func (ir *iteratorRecord) returnIter() {
+	if ir.iterator == nil {
+		return
+	}
+	retMethod := toMethod(ir.iterator.self.getStr("return", nil))
+	if retMethod != nil {
+		ir.iterator.runtime.toObject(retMethod(FunctionCall{This: ir.iterator}))
+	}
+	ir.iterator = nil
+	ir.next = nil
+}
+
+func (ir *iteratorRecord) close() {
+	ir.iterator = nil
+	ir.next = nil
+}
+
 func (r *Runtime) createIterResultObject(value Value, done bool) Value {
 	o := r.NewObject()
 	o.self.setOwnStr("value", value, false)
@@ -2534,7 +2647,7 @@ func (r *Runtime) invoke(v Value, p unistring.String, args ...Value) Value {
 func (r *Runtime) iterableToList(items Value, method func(FunctionCall) Value) []Value {
 	iter := r.getIterator(items, method)
 	var values []Value
-	r.iterate(iter, func(item Value) {
+	iter.iterate(func(item Value) {
 		values = append(values, item)
 	})
 	return values

+ 8 - 20
runtime_test.go

@@ -164,6 +164,14 @@ func TestNumberFormatRounding(t *testing.T) {
 	testScript1(TESTLIB+SCRIPT, _undefined, t)
 }
 
+func TestBinOctalNumbers(t *testing.T) {
+	const SCRIPT = `
+	0b111;
+	`
+
+	testScript1(SCRIPT, valueInt(7), t)
+}
+
 func TestSetFunc(t *testing.T) {
 	const SCRIPT = `
 	sum(40, 2);
@@ -1567,26 +1575,6 @@ func TestProtoGetter(t *testing.T) {
 	testScript1(SCRIPT, valueTrue, t)
 }
 
-func TestFuncProto(t *testing.T) {
-	const SCRIPT = `
-	"use strict";
-	function A() {}
-	A.__proto__ = Object;
-	A.prototype = {};
-
-	function B() {}
-	B.__proto__ = Object.create(null);
-	var thrown = false;
-	try {
-		delete B.prototype;
-	} catch (e) {
-		thrown = e instanceof TypeError;
-	}
-	thrown;
-	`
-	testScript1(SCRIPT, valueTrue, t)
-}
-
 func TestSymbol1(t *testing.T) {
 	const SCRIPT = `
 		Symbol.toPrimitive[Symbol.toPrimitive]() === Symbol.toPrimitive;

+ 10 - 13
string.go

@@ -190,12 +190,9 @@ func (s *stringObject) getStr(name unistring.String, receiver Value) Value {
 }
 
 func (s *stringObject) getIdx(idx valueInt, receiver Value) Value {
-	i := int64(idx)
-	if i >= 0 {
-		if i < int64(s.length) {
-			return s._getIdx(int(i))
-		}
-		return nil
+	i := int(idx)
+	if i >= 0 && i < s.length {
+		return s._getIdx(i)
 	}
 	return s.baseObject.getStr(idx.string(), receiver)
 }
@@ -261,8 +258,8 @@ func (s *stringObject) setForeignIdx(idx valueInt, val, receiver Value, throw bo
 
 func (s *stringObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
 	if i := strToGoIdx(name); i >= 0 && i < s.length {
-		s.val.runtime.typeErrorResult(throw, "Cannot redefine property: %d", i)
-		return false
+		_, ok := s._defineOwnProperty(name, &valueProperty{enumerable: true}, descr, throw)
+		return ok
 	}
 
 	return s.baseObject.defineOwnPropertyStr(name, descr, throw)
@@ -288,13 +285,13 @@ func (i *stringPropIter) next() (propIterItem, iterNextFunc) {
 	if i.idx < i.length {
 		name := strconv.Itoa(i.idx)
 		i.idx++
-		return propIterItem{name: unistring.String(name), enumerable: _ENUM_TRUE}, i.next
+		return propIterItem{name: asciiString(name), enumerable: _ENUM_TRUE}, i.next
 	}
 
-	return i.obj.baseObject.enumerateOwnKeys()()
+	return i.obj.baseObject.iterateStringKeys()()
 }
 
-func (s *stringObject) enumerateOwnKeys() iterNextFunc {
+func (s *stringObject) iterateStringKeys() iterNextFunc {
 	return (&stringPropIter{
 		str:    s.value,
 		obj:    s,
@@ -302,12 +299,12 @@ func (s *stringObject) enumerateOwnKeys() iterNextFunc {
 	}).next
 }
 
-func (s *stringObject) ownKeys(all bool, accum []Value) []Value {
+func (s *stringObject) stringKeys(all bool, accum []Value) []Value {
 	for i := 0; i < s.length; i++ {
 		accum = append(accum, asciiString(strconv.Itoa(i)))
 	}
 
-	return s.baseObject.ownKeys(all, accum)
+	return s.baseObject.stringKeys(all, accum)
 }
 
 func (s *stringObject) deleteStr(name unistring.String, throw bool) bool {

+ 14 - 0
string_test.go

@@ -0,0 +1,14 @@
+package goja
+
+import "testing"
+
+func TestStringOOBProperties(t *testing.T) {
+	const SCRIPT = `
+	var string = new String("str");
+	
+	string[4] = 1;
+	string[4];
+	`
+
+	testScript1(SCRIPT, valueInt(1), t)
+}

+ 280 - 397
tc39_test.go

@@ -33,214 +33,104 @@ var (
 )
 
 var (
-	skipList = map[string]bool{
+	skipPrefixes prefixList
 
-		// Obsolete tests (see https://github.com/tc39/test262/pull/2445)
-		//TODO: remove this after upgrading the test suite past the above PR
-		"test/language/statements/function/scope-param-rest-elem-var-open.js":         true,
-		"test/language/statements/function/scope-param-rest-elem-var-close.js":        true,
-		"test/language/statements/function/scope-param-elem-var-open.js":              true,
-		"test/language/function-code/eval-param-env-with-prop-initializer.js":         true,
-		"test/language/function-code/eval-param-env-with-computed-key.js":             true,
-		"test/language/expressions/object/scope-meth-param-rest-elem-var-open.js":     true,
-		"test/language/statements/function/scope-param-elem-var-close.js":             true,
-		"test/language/expressions/object/scope-meth-param-rest-elem-var-close.js":    true,
-		"test/language/expressions/object/scope-meth-param-elem-var-open.js":          true,
-		"test/language/expressions/object/scope-meth-param-elem-var-close.js":         true,
-		"test/language/expressions/function/scope-param-rest-elem-var-open.js":        true,
-		"test/language/expressions/function/scope-param-rest-elem-var-close.js":       true,
-		"test/language/expressions/function/scope-param-elem-var-open.js":             true,
-		"test/language/expressions/function/scope-param-elem-var-close.js":            true,
-		"test/language/expressions/arrow-function/scope-param-rest-elem-var-open.js":  true,
-		"test/language/expressions/arrow-function/scope-param-rest-elem-var-close.js": true,
-		"test/language/expressions/arrow-function/scope-param-elem-var-close.js":      true,
-		"test/language/expressions/arrow-function/scope-param-elem-var-open.js":       true,
-		"test/language/function-code/each-param-has-own-scope.js":                     true,
-		"test/language/function-code/each-param-has-own-non-shared-eval-scope.js":     true,
-
-		// These tests are out of date (fixed in https://github.com/tc39/test262/commit/7d998a098e5420cb4b6ee4a05eb8c386d750c596)
-		"test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/key-is-numericindex.js":                   true,
-		"test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/key-is-numericindex-desc-configurable.js": true,
-
-		// Fixed in https://github.com/tc39/test262/commit/7d998a098e5420cb4b6ee4a05eb8c386d750c596
-		"test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/detached-buffer.js": true,
-		// Fixed in https://github.com/tc39/test262/commit/0bb8fe8aba97765aa3a8d4dd8880cd8e3c238f68
-		"test/built-ins/TypedArrayConstructors/internals/Get/detached-buffer.js":                              true,
-		"test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/tonumber-value-detached-buffer.js": true,
-		// 36c2cd165f93e194b9bcad26e69e8571b1d0e6ed
-		"test/built-ins/ArrayBuffer/prototype/byteLength/detached-buffer.js": true,
-
-		// 96aff62fb25cf9ef27929a8ab822ee853d99b06e
-		"test/built-ins/TypedArrayConstructors/internals/Set/tonumber-value-detached-buffer.js": true,
-		"test/built-ins/TypedArrayConstructors/internals/Set/key-is-out-of-bounds.js":           true,
-
-		// 167e596a649ede35df11d03cb3c093941c9cf396
-		"test/built-ins/TypedArrayConstructors/internals/Set/detached-buffer.js": true,
-
-		// Anonymous function name property (now always present)
-		"test/language/expressions/function/name.js":                         true,
-		"test/built-ins/Proxy/revocable/revocation-function-name.js":         true,
-		"test/built-ins/Promise/all/resolve-element-function-name.js":        true,
-		"test/built-ins/Promise/allSettled/resolve-element-function-name.js": true,
-		"test/built-ins/Promise/allSettled/reject-element-function-name.js":  true,
-		"test/built-ins/Promise/resolve-function-name.js":                    true,
-		"test/built-ins/Promise/reject-function-name.js":                     true,
-
-		// obsolete tests (to remove)
-		"test/built-ins/Promise/race/invoke-resolve-get-error-close.js":       true,
-		"test/built-ins/Promise/allSettled/invoke-resolve-get-error-close.js": true,
-		"test/built-ins/Promise/all/invoke-resolve-get-error-close.js":        true,
+	skipList = map[string]bool{
 
 		// timezone
 		"test/built-ins/Date/prototype/toISOString/15.9.5.43-0-8.js":  true,
 		"test/built-ins/Date/prototype/toISOString/15.9.5.43-0-9.js":  true,
 		"test/built-ins/Date/prototype/toISOString/15.9.5.43-0-10.js": true,
 
-		// SharedArrayBuffer
-		"test/built-ins/ArrayBuffer/prototype/slice/this-is-sharedarraybuffer.js": true,
+		// floating point date calculations
+		"test/built-ins/Date/UTC/fp-evaluation-order.js": true,
+
+		// quantifier integer limit in regexp
+		"test/built-ins/RegExp/quantifier-integer-limit.js": true,
+
+		// 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/language/statements/class/subclass/builtin-objects/Symbol/symbol-valid-as-extends-value.js":            true,
-		"test/language/statements/class/subclass/builtin-objects/Symbol/new-symbol-with-super-throws.js":             true,
-		"test/language/statements/class/subclass/builtin-objects/WeakSet/super-must-be-called.js":                    true,
-		"test/language/statements/class/subclass/builtin-objects/WeakSet/regular-subclassing.js":                     true,
-		"test/language/statements/class/subclass/builtin-objects/WeakMap/super-must-be-called.js":                    true,
-		"test/language/statements/class/subclass/builtin-objects/WeakMap/regular-subclassing.js":                     true,
-		"test/language/statements/class/subclass/builtin-objects/Map/super-must-be-called.js":                        true,
-		"test/language/statements/class/subclass/builtin-objects/Map/regular-subclassing.js":                         true,
-		"test/language/statements/class/subclass/builtin-objects/Set/super-must-be-called.js":                        true,
-		"test/language/statements/class/subclass/builtin-objects/Set/regular-subclassing.js":                         true,
-		"test/language/statements/class/subclass/builtin-objects/Object/replacing-prototype.js":                      true,
-		"test/language/statements/class/subclass/builtin-objects/Object/regular-subclassing.js":                      true,
-		"test/built-ins/Array/prototype/concat/Array.prototype.concat_non-array.js":                                  true,
-		"test/language/statements/class/subclass/builtin-objects/Array/length.js":                                    true,
-		"test/language/statements/class/subclass/builtin-objects/TypedArray/super-must-be-called.js":                 true,
-		"test/language/statements/class/subclass/builtin-objects/TypedArray/regular-subclassing.js":                  true,
-		"test/language/statements/class/subclass/builtin-objects/DataView/super-must-be-called.js":                   true,
-		"test/language/statements/class/subclass/builtin-objects/DataView/regular-subclassing.js":                    true,
-		"test/language/statements/class/subclass/builtin-objects/String/super-must-be-called.js":                     true,
-		"test/language/statements/class/subclass/builtin-objects/String/regular-subclassing.js":                      true,
-		"test/language/statements/class/subclass/builtin-objects/String/length.js":                                   true,
-		"test/language/statements/class/subclass/builtin-objects/Date/super-must-be-called.js":                       true,
-		"test/language/statements/class/subclass/builtin-objects/Date/regular-subclassing.js":                        true,
-		"test/language/statements/class/subclass/builtin-objects/Number/super-must-be-called.js":                     true,
-		"test/language/statements/class/subclass/builtin-objects/Number/regular-subclassing.js":                      true,
-		"test/language/statements/class/subclass/builtin-objects/Function/super-must-be-called.js":                   true,
-		"test/language/statements/class/subclass/builtin-objects/Function/regular-subclassing.js":                    true,
-		"test/language/statements/class/subclass/builtin-objects/Function/instance-name.js":                          true,
-		"test/language/statements/class/subclass/builtin-objects/Function/instance-length.js":                        true,
-		"test/language/statements/class/subclass/builtin-objects/Boolean/super-must-be-called.js":                    true,
-		"test/language/statements/class/subclass/builtin-objects/Boolean/regular-subclassing.js":                     true,
-		"test/language/statements/class/subclass/builtin-objects/NativeError/URIError-super.js":                      true,
-		"test/language/statements/class/subclass/builtin-objects/NativeError/URIError-name.js":                       true,
-		"test/language/statements/class/subclass/builtin-objects/NativeError/URIError-message.js":                    true,
-		"test/language/statements/class/subclass/builtin-objects/NativeError/TypeError-super.js":                     true,
-		"test/language/statements/class/subclass/builtin-objects/NativeError/TypeError-name.js":                      true,
-		"test/language/statements/class/subclass/builtin-objects/NativeError/TypeError-message.js":                   true,
-		"test/language/statements/class/subclass/builtin-objects/NativeError/SyntaxError-super.js":                   true,
-		"test/language/statements/class/subclass/builtin-objects/NativeError/SyntaxError-name.js":                    true,
-		"test/language/statements/class/subclass/builtin-objects/NativeError/SyntaxError-message.js":                 true,
-		"test/language/statements/class/subclass/builtin-objects/NativeError/ReferenceError-super.js":                true,
-		"test/language/statements/class/subclass/builtin-objects/NativeError/ReferenceError-name.js":                 true,
-		"test/language/statements/class/subclass/builtin-objects/NativeError/ReferenceError-message.js":              true,
-		"test/language/statements/class/subclass/builtin-objects/NativeError/RangeError-super.js":                    true,
-		"test/language/statements/class/subclass/builtin-objects/NativeError/RangeError-name.js":                     true,
-		"test/language/statements/class/subclass/builtin-objects/NativeError/RangeError-message.js":                  true,
-		"test/language/statements/class/subclass/builtin-objects/NativeError/EvalError-super.js":                     true,
-		"test/language/statements/class/subclass/builtin-objects/NativeError/EvalError-name.js":                      true,
-		"test/language/statements/class/subclass/builtin-objects/NativeError/EvalError-message.js":                   true,
-		"test/language/statements/class/subclass/builtin-objects/Error/super-must-be-called.js":                      true,
-		"test/language/statements/class/subclass/builtin-objects/Error/regular-subclassing.js":                       true,
-		"test/language/statements/class/subclass/builtin-objects/Error/message-property-assignment.js":               true,
-		"test/language/statements/class/subclass/builtin-objects/Array/super-must-be-called.js":                      true,
-		"test/language/statements/class/subclass/builtin-objects/Array/regular-subclassing.js":                       true,
-		"test/language/statements/class/subclass/builtin-objects/Array/contructor-calls-super-single-argument.js":    true,
-		"test/language/statements/class/subclass/builtin-objects/Array/contructor-calls-super-multiple-arguments.js": true,
-		"test/language/statements/class/subclass/builtin-objects/ArrayBuffer/super-must-be-called.js":                true,
-		"test/language/statements/class/subclass/builtin-objects/ArrayBuffer/regular-subclassing.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/statements/class/subclass/builtin-objects/RegExp/super-must-be-called.js":                     true,
-		"test/language/statements/class/subclass/builtin-objects/RegExp/regular-subclassing.js":                      true,
-		"test/language/statements/class/subclass/builtin-objects/RegExp/lastIndex.js":                                true,
-		"TestTC39/tc39/test/language/statements/class/definition/fn-name-method.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/statements/class/scope-static-setter-paramsbody-var-open.js":                                  true,
-		"test/language/statements/class/scope-static-setter-paramsbody-var-close.js":                                 true,
-		"test/language/statements/class/scope-static-meth-paramsbody-var-open.js":                                    true,
-		"test/language/statements/class/scope-static-meth-paramsbody-var-close.js":                                   true,
-		"test/language/statements/class/scope-setter-paramsbody-var-open.js":                                         true,
-		"test/language/statements/class/scope-setter-paramsbody-var-close.js":                                        true,
-		"test/language/statements/class/scope-meth-paramsbody-var-open.js":                                           true,
-		"test/language/statements/class/scope-meth-paramsbody-var-close.js":                                          true,
-		"test/language/expressions/class/scope-static-setter-paramsbody-var-open.js":                                 true,
-		"test/language/expressions/class/scope-static-setter-paramsbody-var-close.js":                                true,
-		"test/language/expressions/class/scope-static-meth-paramsbody-var-open.js":                                   true,
-		"test/language/expressions/class/scope-static-meth-paramsbody-var-close.js":                                  true,
-		"test/language/expressions/class/scope-setter-paramsbody-var-open.js":                                        true,
-		"test/language/expressions/class/scope-setter-paramsbody-var-close.js":                                       true,
-		"test/language/expressions/class/scope-meth-paramsbody-var-open.js":                                          true,
-		"test/language/expressions/class/scope-meth-paramsbody-var-close.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/statements/class/static-method-length-dflt.js":                                                true,
-		"test/language/statements/class/setter-length-dflt.js":                                                       true,
-		"test/language/statements/class/restricted-properties.js":                                                    true,
-		"test/language/statements/class/method-length-dflt.js":                                                       true,
-		"test/language/statements/class/definition/methods-restricted-properties.js":                                 true,
-		"test/language/expressions/class/static-method-length-dflt.js":                                               true,
-		"test/language/expressions/class/setter-length-dflt.js":                                                      true,
-		"test/language/expressions/class/restricted-properties.js":                                                   true,
-		"test/language/expressions/class/method-length-dflt.js":                                                      true,
-		"test/language/expressions/arrow-function/lexical-super-property-from-within-constructor.js":                 true,
-		"test/language/expressions/arrow-function/lexical-super-property.js":                                         true,
-		"test/language/expressions/arrow-function/lexical-supercall-from-immediately-invoked-arrow.js":               true,
-		"test/language/statements/class/subclass/builtin-objects/Promise/super-must-be-called.js":                    true,
-		"test/language/statements/class/subclass/builtin-objects/Promise/regular-subclassing.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/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,
 
 		// restricted unicode regexp syntax
 		"test/built-ins/RegExp/unicode_restricted_quantifiable_assertion.js":         true,
@@ -256,17 +146,6 @@ var (
 		"test/built-ins/RegExp/unicode_restricted_character_class_escape.js":         true,
 		"test/annexB/built-ins/RegExp/prototype/compile/pattern-string-invalid-u.js": true,
 
-		// regexp named groups
-		"test/built-ins/RegExp/prototype/Symbol.replace/named-groups-fn.js":               true,
-		"test/built-ins/RegExp/prototype/Symbol.replace/result-coerce-groups-err.js":      true,
-		"test/built-ins/RegExp/prototype/Symbol.replace/result-coerce-groups-prop-err.js": true,
-		"test/built-ins/RegExp/prototype/Symbol.replace/result-coerce-groups-prop.js":     true,
-		"test/built-ins/RegExp/prototype/Symbol.replace/result-coerce-groups.js":          true,
-		"test/built-ins/RegExp/prototype/Symbol.replace/result-get-groups-err.js":         true,
-		"test/built-ins/RegExp/prototype/Symbol.replace/result-get-groups-prop-err.js":    true,
-		"test/built-ins/RegExp/named-groups/functional-replace-non-global.js":             true,
-		"test/built-ins/RegExp/named-groups/functional-replace-global.js":                 true,
-
 		// Because goja parser works in UTF-8 it is not possible to pass strings containing invalid UTF-16 code points.
 		// This is mitigated by escaping them as \uXXXX, however because of this the RegExp source becomes
 		// `\uXXXX` instead of `<the actual UTF-16 code point of XXXX>`.
@@ -274,172 +153,169 @@ var (
 		"test/annexB/built-ins/RegExp/RegExp-leading-escape-BMP.js":  true,
 		"test/annexB/built-ins/RegExp/RegExp-trailing-escape-BMP.js": true,
 
-		// Promise
-		"test/built-ins/Symbol/species/builtin-getter-name.js": true,
-
 		// x ** y
-		"test/built-ins/Array/prototype/pop/clamps-to-integer-limit.js":                           true,
-		"test/built-ins/Array/prototype/pop/length-near-integer-limit.js":                         true,
-		"test/built-ins/Array/prototype/push/clamps-to-integer-limit.js":                          true,
-		"test/built-ins/Array/prototype/push/length-near-integer-limit.js":                        true,
-		"test/built-ins/Array/prototype/push/throws-if-integer-limit-exceeded.js":                 true,
-		"test/built-ins/Array/prototype/reverse/length-exceeding-integer-limit-with-object.js":    true,
-		"test/built-ins/Array/prototype/reverse/length-exceeding-integer-limit-with-proxy.js":     true,
-		"test/built-ins/Array/prototype/slice/length-exceeding-integer-limit.js":                  true,
-		"test/built-ins/Array/prototype/splice/clamps-length-to-integer-limit.js":                 true,
-		"test/built-ins/Array/prototype/splice/length-and-deleteCount-exceeding-integer-limit.js": true,
-		"test/built-ins/Array/prototype/splice/length-exceeding-integer-limit-shrink-array.js":    true,
-		"test/built-ins/Array/prototype/splice/length-near-integer-limit-grow-array.js":           true,
-		"test/built-ins/Array/prototype/splice/throws-if-integer-limit-exceeded.js":               true,
-		"test/built-ins/Array/prototype/unshift/clamps-to-integer-limit.js":                       true,
-		"test/built-ins/Array/prototype/unshift/length-near-integer-limit.js":                     true,
-		"test/built-ins/Array/prototype/unshift/throws-if-integer-limit-exceeded.js":              true,
-		"test/built-ins/String/prototype/split/separator-undef-limit-custom.js":                   true,
-		"test/built-ins/Array/prototype/splice/create-species-length-exceeding-integer-limit.js":  true,
-		"test/built-ins/Array/prototype/slice/length-exceeding-integer-limit-proxied-array.js":    true,
+		"test/built-ins/Array/prototype/pop/clamps-to-integer-limit.js":                                        true,
+		"test/built-ins/Array/prototype/pop/length-near-integer-limit.js":                                      true,
+		"test/built-ins/Array/prototype/push/clamps-to-integer-limit.js":                                       true,
+		"test/built-ins/Array/prototype/push/length-near-integer-limit.js":                                     true,
+		"test/built-ins/Array/prototype/push/throws-if-integer-limit-exceeded.js":                              true,
+		"test/built-ins/Array/prototype/reverse/length-exceeding-integer-limit-with-object.js":                 true,
+		"test/built-ins/Array/prototype/reverse/length-exceeding-integer-limit-with-proxy.js":                  true,
+		"test/built-ins/Array/prototype/slice/length-exceeding-integer-limit.js":                               true,
+		"test/built-ins/Array/prototype/splice/clamps-length-to-integer-limit.js":                              true,
+		"test/built-ins/Array/prototype/splice/length-and-deleteCount-exceeding-integer-limit.js":              true,
+		"test/built-ins/Array/prototype/splice/length-exceeding-integer-limit-shrink-array.js":                 true,
+		"test/built-ins/Array/prototype/splice/length-near-integer-limit-grow-array.js":                        true,
+		"test/built-ins/Array/prototype/splice/throws-if-integer-limit-exceeded.js":                            true,
+		"test/built-ins/Array/prototype/unshift/clamps-to-integer-limit.js":                                    true,
+		"test/built-ins/Array/prototype/unshift/length-near-integer-limit.js":                                  true,
+		"test/built-ins/Array/prototype/unshift/throws-if-integer-limit-exceeded.js":                           true,
+		"test/built-ins/String/prototype/split/separator-undef-limit-custom.js":                                true,
+		"test/built-ins/Array/prototype/splice/create-species-length-exceeding-integer-limit.js":               true,
+		"test/built-ins/Array/prototype/slice/length-exceeding-integer-limit-proxied-array.js":                 true,
+		"test/built-ins/String/prototype/split/separator-undef-limit-zero.js":                                  true,
+		"test/language/expressions/object/cpn-obj-lit-computed-property-name-from-exponetiation-expression.js": true,
+		"test/language/expressions/object/cpn-obj-lit-computed-property-name-from-math.js":                     true,
+		"test/built-ins/RegExp/prototype/exec/failure-lastindex-set.js":                                        true,
 
 		// generators
-		"test/annexB/built-ins/RegExp/RegExp-control-escape-russian-letter.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,
+
+		// 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,
+
+		// 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,
+
+		// BigInt
+		"test/built-ins/Object/seal/seal-biguint64array.js": true,
+		"test/built-ins/Object/seal/seal-bigint64array.js":  true,
+
+		// 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,
+
+		// Left-hand side as a CoverParenthesizedExpression
+		"test/language/expressions/assignment/fn-name-lhs-cover.js": true,
 	}
 
 	featuresBlackList = []string{
 		"async-iteration",
+		"Symbol.asyncIterator",
+		"async-functions",
 		"BigInt",
 		"class",
+		"class-static-block",
+		"class-fields-private",
+		"class-fields-private-in",
+		"super",
 		"generators",
 		"String.prototype.replaceAll",
 		"String.prototype.at",
-		"super",
+		"resizable-arraybuffer",
+		"array-find-from-last",
+		"Array.prototype.at",
+		"TypedArray.prototype.at",
+		"regexp-named-groups",
+		"regexp-dotall",
+		"regexp-unicode-property-escapes",
+		"regexp-match-indices",
+		"legacy-regexp",
+		"tail-call-optimization",
+		"Temporal",
+		"import-assertions",
+		"dynamic-import",
+		"logical-assignment-operators",
+		"coalesce-expression",
+		"import.meta",
+		"optional-chaining",
+		"Atomics",
+		"Atomics.waitAsync",
+		"FinalizationRegistry",
+		"WeakRef",
+		"numeric-separator-literal",
+		"Object.fromEntries",
+		"Object.hasOwn",
+		"__getter__",
+		"__setter__",
+		"ShadowRealm",
+		"SharedArrayBuffer",
+		"error-cause",
 	}
+)
 
-	es6WhiteList = map[string]bool{}
-
-	es6IdWhiteList = []string{
-		"8.1.2.1",
-		"9.5",
-		"11.8.3",
-		"11.8.6",
-		"12.1",
-		"12.2.1",
-		"12.2.2",
-		"12.2.5",
-		"12.2.6.1",
-		"12.2.6.8",
-		"12.2.8",
-		"12.2.9",
-		"12.3.7",
-		"12.4",
-		"12.5",
-		"12.6",
-		"12.7",
-		"12.8",
-		"12.9",
-		"12.10",
-		"13.1",
-		"13.2",
-		"13.3",
-		"13.4",
-		"13.5",
-		"13.6",
-		"13.7",
-		"13.8",
-		"13.9",
-		"13.10",
-		"13.11",
-		"13.12",
-		"13.13",
-		"13.14",
-		"13.15",
-		"14.1",
-		"14.2",
-		"14.3.8",
-		"16.1",
-		"18",
-		"19",
-		"20",
-		"21",
-		"22",
-		"23",
-		"24",
-		"25.1",
-		"25.4",
-		"26",
-		"B.2.1",
-		"B.2.2",
-	}
-
-	esIdPrefixWhiteList = []string{
-		"sec-addition-*",
-		"sec-array",
-		"sec-%typedarray%",
-		"sec-%typedarray%-of",
-		"sec-@@iterator",
-		"sec-@@tostringtag",
-		"sec-string",
-		"sec-date",
-		"sec-json",
-		"sec-number",
-		"sec-math",
-		"sec-arraybuffer-length",
-		"sec-arraybuffer",
-		"sec-regexp",
-		"sec-string.prototype.trimLeft",
-		"sec-string.prototype.trimRight",
-		"sec-object.getownpropertydescriptor",
-		"sec-object.getownpropertydescriptors",
-		"sec-object.entries",
-		"sec-object.values",
-		"sec-object-initializer",
-		"sec-proxy-*",
-		"sec-for-statement-*",
-		"sec-for-in-and-for-of-statements",
-		"sec-for-in-and-for-of-statements-*",
-		"sec-do-while-statement",
-		"sec-if-statement",
-		"sec-while-statement",
-		"sec-with-statement*",
-		"sec-switch-*",
-		"sec-try-*",
-		"sec-runtime-semantics-catchclauseevaluation",
-		"sec-strict-mode-of-ecmascript",
-		"sec-let-and-const-declarations*",
-		"sec-arguments-exotic-objects-defineownproperty-p-desc",
-		"sec-other-properties-of-the-global-object-globalthis",
-		"sec-variable-statement-runtime-semantics-evaluation",
-		"sec-function-calls-runtime-semantics-evaluation",
-		"sec-function-definitions",
-		"sec-function-definitions-runtime-semantics-evaluation",
-		"sec-function-definitions-runtime-semantics-instantiatefunctionobject",
-		"sec-function-definitions-runtime-semantics-iteratorbindinginitialization",
-		"sec-function-definitions-static-semantics-early-errors",
-		"sec-functiondeclarationinstantiation",
-		"sec-functiondeclarations-in-ifstatement-statement-clauses",
-		"sec-arrow-function-definitions",
-		"sec-arrow-function-definitions-runtime-semantics-evaluation",
-		"sec-arrow-function-definitions-static-semantics-early-errors",
-		"sec-evaldeclarationinstantiation",
-		"sec-integer-indexed-exotic-objects-defineownproperty-p-desc",
-		"sec-integer-indexed-exotic-objects-get-p-receiver",
-		"sec-integer-indexed-exotic-objects-set-p-v-receiver",
-		"sec-destructuring-binding-patterns",
-		"sec-runtime-semantics-keyeddestructuringassignmentevaluation",
-		"sec-gettemplateobject",
-		"sec-tagged-templates-runtime-semantics-evaluation",
-		"sec-template-literal-lexical-components",
-		"sec-template-literals",
-		"sec-literals-numeric-literals",
-		"sec-literals-string-literals",
-		"sec-additional-syntax-numeric-literals",
-		"sec-promise",
-		"sec-promise-constructor",
-		"sec-promise-executor",
-		"sec-promise-reject-functions",
-		"sec-promise-resolve-functions",
-		"sec-performpromiseall",
-		"sec-performpromiseallsettled",
-		"sec-properties-of-the-promise-prototype-object",
+func init() {
+
+	skip := func(prefixes ...string) {
+		for _, prefix := range prefixes {
+			skipPrefixes.Add(prefix)
+		}
 	}
-)
+
+	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-",
+
+		// async
+		"test/language/eval-code/direct/async-",
+		"test/language/expressions/async-",
+		"test/language/expressions/await/",
+		"test/language/statements/async-function/",
+		"test/built-ins/Async",
+
+		// generators
+		"test/language/eval-code/direct/gen-",
+		"test/built-ins/GeneratorFunction/",
+		"test/built-ins/Function/prototype/toString/generator-",
+
+		// **
+		"test/language/expressions/exponentiation",
+
+		// BigInt
+		"test/built-ins/TypedArrayConstructors/BigUint64Array/",
+		"test/built-ins/TypedArrayConstructors/BigInt64Array/",
+	)
+
+}
 
 type tc39Test struct {
 	name string
@@ -478,6 +354,33 @@ type tc39Meta struct {
 	Esid     string
 }
 
+type prefixList struct {
+	prefixes map[int]map[string]struct{}
+}
+
+func (pl *prefixList) Add(prefix string) {
+	l := pl.prefixes[len(prefix)]
+	if l == nil {
+		l = make(map[string]struct{})
+		if pl.prefixes == nil {
+			pl.prefixes = make(map[int]map[string]struct{})
+		}
+		pl.prefixes[len(prefix)] = l
+	}
+	l[prefix] = struct{}{}
+}
+
+func (pl *prefixList) Match(s string) bool {
+	for l, prefixes := range pl.prefixes {
+		if len(s) >= l {
+			if _, exists := prefixes[s[:l]]; exists {
+				return true
+			}
+		}
+	}
+	return false
+}
+
 func (m *tc39Meta) hasFlag(flag string) bool {
 	for _, f := range m.Flags {
 		if f == flag {
@@ -548,6 +451,14 @@ func (ctx *tc39TestCtx) runTC39Test(name, src string, meta *tc39Meta, t testing.
 	_262 := vm.NewObject()
 	_262.Set("detachArrayBuffer", ctx.detachArrayBuffer)
 	_262.Set("createRealm", ctx.throwIgnorableTestError)
+	_262.Set("evalScript", func(call FunctionCall) Value {
+		script := call.Argument(0).String()
+		result, err := vm.RunString(script)
+		if err != nil {
+			panic(err)
+		}
+		return result
+	})
 	vm.Set("$262", _262)
 	vm.Set("IgnorableTestError", ignorableTestError)
 	vm.RunProgram(sabStub)
@@ -645,6 +556,9 @@ func (ctx *tc39TestCtx) runTC39File(name string, t testing.TB) {
 	if skipList[name] {
 		t.Skip("Excluded")
 	}
+	if skipPrefixes.Match(name) {
+		t.Skip("Excluded")
+	}
 	p := path.Join(ctx.base, name)
 	meta, src, err := parseTC39File(p)
 	if err != nil {
@@ -652,43 +566,12 @@ func (ctx *tc39TestCtx) runTC39File(name string, t testing.TB) {
 		t.Errorf("Could not parse %s: %v", name, err)
 		return
 	}
+	if meta.hasFlag("module") {
+		t.Skip("module")
+	}
 	if meta.Es5id == "" {
-		skip := true
-		//t.Logf("%s: Not ES5, skipped", name)
-		if es6WhiteList[name] {
-			skip = false
-		} else {
-			if meta.Es6id != "" {
-				for _, prefix := range es6IdWhiteList {
-					if strings.HasPrefix(meta.Es6id, prefix) &&
-						(len(meta.Es6id) == len(prefix) || meta.Es6id[len(prefix)] == '.') {
-
-						skip = false
-						break
-					}
-				}
-			}
-		}
-		if skip {
-			if meta.Esid != "" {
-				for _, prefix := range esIdPrefixWhiteList {
-					if strings.HasSuffix(prefix, "*") {
-						if strings.HasPrefix(meta.Esid, prefix[:len(prefix)-1]) {
-							skip = false
-							break
-						}
-					} else {
-						if strings.HasPrefix(meta.Esid, prefix) &&
-							(len(meta.Esid) == len(prefix) || meta.Esid[len(prefix)] == '.') {
-							skip = false
-							break
-						}
-					}
-				}
-			}
-		}
-		if skip {
-			t.Skip("Not ES5")
+		if meta.Es6id == "" && meta.Esid == "" {
+			t.Skip("No ids")
 		}
 
 		for _, feature := range meta.Features {

+ 31 - 16
typedarrays.go

@@ -505,25 +505,24 @@ func (a *typedArrayObject) getIdx(idx valueInt, receiver Value) Value {
 	return a._getIdx(toIntClamp(int64(idx)))
 }
 
-func (a *typedArrayObject) isValidIntegerIndex(idx int, throw bool) bool {
-	if a.viewedArrayBuf.ensureNotDetached(throw) {
+func (a *typedArrayObject) isValidIntegerIndex(idx int) bool {
+	if a.viewedArrayBuf.ensureNotDetached(false) {
 		if idx >= 0 && idx < a.length {
 			return true
 		}
-		a.val.runtime.typeErrorResult(throw, "Invalid typed array index")
 	}
 	return false
 }
 
 func (a *typedArrayObject) _putIdx(idx int, v Value) {
 	v = v.ToNumber()
-	if a.isValidIntegerIndex(idx, false) {
+	if a.isValidIntegerIndex(idx) {
 		a.typedArray.set(idx+a.offset, v)
 	}
 }
 
 func (a *typedArrayObject) _hasIdx(idx int) bool {
-	return a.isValidIntegerIndex(idx, false)
+	return a.isValidIntegerIndex(idx)
 }
 
 func (a *typedArrayObject) setOwnStr(p unistring.String, v Value, throw bool) bool {
@@ -534,7 +533,7 @@ func (a *typedArrayObject) setOwnStr(p unistring.String, v Value, throw bool) bo
 	}
 	if idx == 0 {
 		v.ToNumber() // make sure it throws
-		return false
+		return true
 	}
 	return a.baseObject.setOwnStr(p, v, throw)
 }
@@ -567,6 +566,21 @@ func (a *typedArrayObject) hasOwnPropertyIdx(idx valueInt) bool {
 	return a._hasIdx(toIntClamp(int64(idx)))
 }
 
+func (a *typedArrayObject) hasPropertyStr(name unistring.String) bool {
+	idx, ok := strToIntNum(name)
+	if ok {
+		return a._hasIdx(idx)
+	}
+	if idx == 0 {
+		return false
+	}
+	return a.baseObject.hasPropertyStr(name)
+}
+
+func (a *typedArrayObject) hasPropertyIdx(idx valueInt) bool {
+	return a.hasOwnPropertyIdx(idx)
+}
+
 func (a *typedArrayObject) _defineIdxProperty(idx int, desc PropertyDescriptor, throw bool) bool {
 	if desc.Configurable == FLAG_FALSE || desc.Enumerable == FLAG_FALSE || desc.IsAccessor() || desc.Writable == FLAG_FALSE {
 		a.val.runtime.typeErrorResult(throw, "Cannot redefine property: %d", idx)
@@ -574,7 +588,8 @@ func (a *typedArrayObject) _defineIdxProperty(idx int, desc PropertyDescriptor,
 	}
 	_, ok := a._defineOwnProperty(unistring.String(strconv.Itoa(idx)), a.getOwnPropIdx(valueInt(idx)), desc, throw)
 	if ok {
-		if !a.isValidIntegerIndex(idx, throw) {
+		if !a.isValidIntegerIndex(idx) {
+			a.val.runtime.typeErrorResult(throw, "Invalid typed array index")
 			return false
 		}
 		a._putIdx(idx, desc.Value)
@@ -603,7 +618,7 @@ func (a *typedArrayObject) defineOwnPropertyIdx(name valueInt, desc PropertyDesc
 func (a *typedArrayObject) deleteStr(name unistring.String, throw bool) bool {
 	idx, ok := strToIntNum(name)
 	if ok {
-		if !a.isValidIntegerIndex(idx, false) {
+		if a.isValidIntegerIndex(idx) {
 			a.val.runtime.typeErrorResult(throw, "Cannot delete property '%d' of %s", idx, a.val.String())
 			return false
 		}
@@ -616,7 +631,7 @@ func (a *typedArrayObject) deleteStr(name unistring.String, throw bool) bool {
 }
 
 func (a *typedArrayObject) deleteIdx(idx valueInt, throw bool) bool {
-	if a.viewedArrayBuf.ensureNotDetached(throw) && idx >= 0 && int64(idx) < int64(a.length) {
+	if a.viewedArrayBuf.ensureNotDetached(false) && idx >= 0 && int64(idx) < int64(a.length) {
 		a.val.runtime.typeErrorResult(throw, "Cannot delete property '%d' of %s", idx, a.val.String())
 		return false
 	}
@@ -624,14 +639,14 @@ func (a *typedArrayObject) deleteIdx(idx valueInt, throw bool) bool {
 	return true
 }
 
-func (a *typedArrayObject) ownKeys(all bool, accum []Value) []Value {
+func (a *typedArrayObject) stringKeys(all bool, accum []Value) []Value {
 	if accum == nil {
 		accum = make([]Value, 0, a.length)
 	}
 	for i := 0; i < a.length; i++ {
 		accum = append(accum, asciiString(strconv.Itoa(i)))
 	}
-	return a.baseObject.ownKeys(all, accum)
+	return a.baseObject.stringKeys(all, accum)
 }
 
 type typedArrayPropIter struct {
@@ -644,13 +659,13 @@ func (i *typedArrayPropIter) next() (propIterItem, iterNextFunc) {
 		name := strconv.Itoa(i.idx)
 		prop := i.a._getIdx(i.idx)
 		i.idx++
-		return propIterItem{name: unistring.String(name), value: prop}, i.next
+		return propIterItem{name: asciiString(name), value: prop}, i.next
 	}
 
-	return i.a.baseObject.enumerateOwnKeys()()
+	return i.a.baseObject.iterateStringKeys()()
 }
 
-func (a *typedArrayObject) enumerateOwnKeys() iterNextFunc {
+func (a *typedArrayObject) iterateStringKeys() iterNextFunc {
 	return (&typedArrayPropIter{
 		a: a,
 	}).next
@@ -714,8 +729,7 @@ func (r *Runtime) newFloat64ArrayObject(buf *arrayBufferObject, offset, length i
 	return r._newTypedArrayObject(buf, offset, length, 8, r.global.Float64Array, (*float64Array)(unsafe.Pointer(&buf.data)), proto)
 }
 
-func (o *dataViewObject) getIdxAndByteOrder(idxVal, littleEndianVal Value, size int) (int, byteOrder) {
-	getIdx := o.val.runtime.toIndex(idxVal)
+func (o *dataViewObject) getIdxAndByteOrder(getIdx int, littleEndianVal Value, size int) (int, byteOrder) {
 	o.viewedArrayBuf.ensureNotDetached(true)
 	if getIdx+size > o.byteLen {
 		panic(o.val.runtime.newError(o.val.runtime.global.RangeError, "Index %d is out of bounds", getIdx))
@@ -793,6 +807,7 @@ func (o *arrayBufferObject) getUint32(idx int, byteOrder byteOrder) uint32 {
 }
 
 func (o *arrayBufferObject) setUint32(idx int, val uint32, byteOrder byteOrder) {
+	o.ensureNotDetached(true)
 	if byteOrder == nativeEndian {
 		*(*uint32)(unsafe.Pointer(&o.data[idx])) = val
 	} else {

+ 3 - 2
value.go

@@ -767,7 +767,8 @@ func (o *Object) GetSymbol(sym *Symbol) Value {
 // This method will panic with an *Exception if a JavaScript exception is thrown in the process.
 func (o *Object) Keys() (keys []string) {
 	iter := &enumerableIter{
-		wrapped: o.self.enumerateOwnKeys(),
+		o:       o,
+		wrapped: o.self.iterateStringKeys(),
 	}
 	for item, next := iter.next(); next != nil; item, next = next() {
 		keys = append(keys, item.name.String())
@@ -779,7 +780,7 @@ func (o *Object) Keys() (keys []string) {
 // Symbols returns a list of Object's enumerable symbol properties.
 // This method will panic with an *Exception if a JavaScript exception is thrown in the process.
 func (o *Object) Symbols() []*Symbol {
-	symbols := o.self.ownSymbols(false, nil)
+	symbols := o.self.symbols(false, nil)
 	ret := make([]*Symbol, len(symbols))
 	for i, sym := range symbols {
 		ret[i], _ = sym.(*Symbol)

+ 136 - 60
vm.go

@@ -44,12 +44,13 @@ type context struct {
 type iterStackItem struct {
 	val  Value
 	f    iterNextFunc
-	iter *Object
+	iter *iteratorRecord
 }
 
 type ref interface {
 	get() Value
 	set(Value)
+	init(Value)
 	refname() unistring.String
 }
 
@@ -67,6 +68,10 @@ func (r *stashRef) set(v Value) {
 	(*r.v)[r.idx] = v
 }
 
+func (r *stashRef) init(v Value) {
+	r.set(v)
+}
+
 func (r *stashRef) refname() unistring.String {
 	return r.n
 }
@@ -91,6 +96,10 @@ func (r *stashRefLex) set(v Value) {
 	*p = v
 }
 
+func (r *stashRefLex) init(v Value) {
+	r.set(v)
+}
+
 type stashRefConst struct {
 	stashRefLex
 	strictConst bool
@@ -102,6 +111,10 @@ func (r *stashRefConst) set(v Value) {
 	}
 }
 
+func (r *stashRefConst) init(v Value) {
+	r.set(v)
+}
+
 type objRef struct {
 	base   objectImpl
 	name   unistring.String
@@ -113,6 +126,13 @@ func (r *objRef) get() Value {
 }
 
 func (r *objRef) set(v Value) {
+	if r.strict && !r.base.hasOwnPropertyStr(r.name) {
+		panic(referenceError(fmt.Sprintf("%s is not defined", r.name)))
+	}
+	r.base.setOwnStr(r.name, v, r.strict)
+}
+
+func (r *objRef) init(v Value) {
 	r.base.setOwnStr(r.name, v, r.strict)
 }
 
@@ -134,6 +154,10 @@ func (r *unresolvedRef) set(Value) {
 	r.get()
 }
 
+func (r *unresolvedRef) init(Value) {
+	r.get()
+}
+
 func (r *unresolvedRef) refname() unistring.String {
 	return r.name
 }
@@ -214,22 +238,6 @@ func assertInt64(v Value) (int64, bool) {
 	return 0, false
 }
 
-func toIntIgnoreNegZero(v Value) (int64, bool) {
-	num := v.ToNumber()
-	if i, ok := num.(valueInt); ok {
-		return int64(i), true
-	}
-	if f, ok := num.(valueFloat); ok {
-		if v == _negativeZero {
-			return 0, true
-		}
-		if i, ok := floatToInt(float64(f)); ok {
-			return i, true
-		}
-	}
-	return 0, false
-}
-
 func (s *valueStack) expand(idx int) {
 	if idx < len(*s) {
 		return
@@ -478,8 +486,8 @@ func (vm *vm) try(f func()) (ex *Exception) {
 				iterTail := vm.iterStack[iterLen:]
 				for i := range iterTail {
 					if iter := iterTail[i].iter; iter != nil {
-						vm.try(func() {
-							returnIter(iter)
+						_ = vm.try(func() {
+							iter.returnIter()
 						})
 					}
 					iterTail[i] = iterStackItem{}
@@ -1325,14 +1333,15 @@ type _setElem1Named struct{}
 var setElem1Named _setElem1Named
 
 func (_setElem1Named) exec(vm *vm) {
-	obj := vm.stack[vm.sp-3].ToObject(vm.r)
+	receiver := vm.stack[vm.sp-3]
+	base := receiver.ToObject(vm.r)
 	propName := vm.stack[vm.sp-2]
 	val := vm.stack[vm.sp-1]
 	vm.r.toObject(val).self.defineOwnPropertyStr("name", PropertyDescriptor{
 		Value:        funcName("", propName),
 		Configurable: FLAG_TRUE,
 	}, true)
-	obj.setOwn(propName, val, true)
+	base.set(propName, val, receiver, true)
 
 	vm.sp -= 2
 	vm.pc++
@@ -1358,11 +1367,15 @@ type _setElemStrict struct{}
 var setElemStrict _setElemStrict
 
 func (_setElemStrict) exec(vm *vm) {
-	obj := vm.r.toObject(vm.stack[vm.sp-3])
 	propName := toPropertyKey(vm.stack[vm.sp-2])
+	receiver := vm.stack[vm.sp-3]
 	val := vm.stack[vm.sp-1]
-
-	obj.setOwn(propName, val, true)
+	if receiverObj, ok := receiver.(*Object); ok {
+		receiverObj.setOwn(propName, val, true)
+	} else {
+		base := receiver.ToObject(vm.r)
+		base.set(propName, val, receiver, true)
+	}
 
 	vm.sp -= 2
 	vm.stack[vm.sp-1] = val
@@ -1374,11 +1387,15 @@ type _setElemStrictP struct{}
 var setElemStrictP _setElemStrictP
 
 func (_setElemStrictP) exec(vm *vm) {
-	obj := vm.r.toObject(vm.stack[vm.sp-3])
 	propName := toPropertyKey(vm.stack[vm.sp-2])
+	receiver := vm.stack[vm.sp-3]
 	val := vm.stack[vm.sp-1]
-
-	obj.setOwn(propName, val, true)
+	if receiverObj, ok := receiver.(*Object); ok {
+		receiverObj.setOwn(propName, val, true)
+	} else {
+		base := receiver.ToObject(vm.r)
+		base.set(propName, val, receiver, true)
+	}
 
 	vm.sp -= 3
 	vm.pc++
@@ -1389,7 +1406,7 @@ type _deleteElem struct{}
 var deleteElem _deleteElem
 
 func (_deleteElem) exec(vm *vm) {
-	obj := vm.r.toObject(vm.stack[vm.sp-2])
+	obj := vm.stack[vm.sp-2].ToObject(vm.r)
 	propName := toPropertyKey(vm.stack[vm.sp-1])
 	if obj.delete(propName, false) {
 		vm.stack[vm.sp-2] = valueTrue
@@ -1405,7 +1422,7 @@ type _deleteElemStrict struct{}
 var deleteElemStrict _deleteElemStrict
 
 func (_deleteElemStrict) exec(vm *vm) {
-	obj := vm.r.toObject(vm.stack[vm.sp-2])
+	obj := vm.stack[vm.sp-2].ToObject(vm.r)
 	propName := toPropertyKey(vm.stack[vm.sp-1])
 	obj.delete(propName, true)
 	vm.stack[vm.sp-2] = valueTrue
@@ -1479,11 +1496,16 @@ func (p setPropP) exec(vm *vm) {
 type setPropStrict unistring.String
 
 func (p setPropStrict) exec(vm *vm) {
-	obj := vm.stack[vm.sp-2]
+	receiver := vm.stack[vm.sp-2]
 	val := vm.stack[vm.sp-1]
+	propName := unistring.String(p)
+	if receiverObj, ok := receiver.(*Object); ok {
+		receiverObj.self.setOwnStr(propName, val, true)
+	} else {
+		base := receiver.ToObject(vm.r)
+		base.setStr(propName, val, receiver, true)
+	}
 
-	obj1 := vm.r.toObject(obj)
-	obj1.self.setOwnStr(unistring.String(p), val, true)
 	vm.stack[vm.sp-2] = val
 	vm.sp--
 	vm.pc++
@@ -1492,10 +1514,16 @@ func (p setPropStrict) exec(vm *vm) {
 type setPropStrictP unistring.String
 
 func (p setPropStrictP) exec(vm *vm) {
-	obj := vm.r.toObject(vm.stack[vm.sp-2])
+	receiver := vm.stack[vm.sp-2]
 	val := vm.stack[vm.sp-1]
+	propName := unistring.String(p)
+	if receiverObj, ok := receiver.(*Object); ok {
+		receiverObj.self.setOwnStr(propName, val, true)
+	} else {
+		base := receiver.ToObject(vm.r)
+		base.setStr(propName, val, receiver, true)
+	}
 
-	obj.self.setOwnStr(unistring.String(p), val, true)
 	vm.sp -= 2
 	vm.pc++
 }
@@ -1514,7 +1542,7 @@ type _setProto struct{}
 var setProto _setProto
 
 func (_setProto) exec(vm *vm) {
-	vm.r.toObject(vm.stack[vm.sp-2]).self.setProto(vm.r.toProto(vm.stack[vm.sp-1]), true)
+	vm.r.setObjectProto(vm.stack[vm.sp-2], vm.stack[vm.sp-1])
 
 	vm.sp--
 	vm.pc++
@@ -1771,7 +1799,7 @@ var pushArraySpread _pushArraySpread
 
 func (_pushArraySpread) exec(vm *vm) {
 	arr := vm.stack[vm.sp-2].(*Object).self.(*arrayObject)
-	vm.r.iterate(vm.r.getIterator(vm.stack[vm.sp-1], nil), func(val Value) {
+	vm.r.getIterator(vm.stack[vm.sp-1], nil).iterate(func(val Value) {
 		if arr.length < math.MaxUint32 {
 			arr.length++
 		} else {
@@ -1791,7 +1819,7 @@ var pushSpread _pushSpread
 func (_pushSpread) exec(vm *vm) {
 	vm.sp--
 	obj := vm.stack[vm.sp]
-	vm.r.iterate(vm.r.getIterator(obj, nil), func(val Value) {
+	vm.r.getIterator(obj, nil).iterate(func(val Value) {
 		vm.push(val)
 	})
 	vm.pc++
@@ -1807,7 +1835,7 @@ func (_newArrayFromIter) exec(vm *vm) {
 	iter := vm.iterStack[l].iter
 	vm.iterStack[l] = iterStackItem{}
 	vm.iterStack = vm.iterStack[:l]
-	vm.r.iterate(iter, func(val Value) {
+	iter.iterate(func(val Value) {
 		values = append(values, val)
 	})
 	vm.push(vm.r.newArrayValues(values))
@@ -2413,6 +2441,20 @@ func (_putValueP) exec(vm *vm) {
 	vm.pc++
 }
 
+type _initValueP struct{}
+
+var initValueP _initValueP
+
+func (_initValueP) exec(vm *vm) {
+	l := len(vm.refStack) - 1
+	ref := vm.refStack[l]
+	vm.refStack[l] = nil
+	vm.refStack = vm.refStack[:l]
+	ref.init(vm.stack[vm.sp-1])
+	vm.sp--
+	vm.pc++
+}
+
 type loadDynamic unistring.String
 
 func (n loadDynamic) exec(vm *vm) {
@@ -2614,6 +2656,15 @@ func (numargs call) exec(vm *vm) {
 	obj := vm.toCallee(v)
 repeat:
 	switch f := obj.self.(type) {
+	case *methodFuncObject:
+		vm.pc++
+		vm.pushCtx()
+		vm.args = n
+		vm.prg = f.prg
+		vm.stash = f.stash
+		vm.pc = 0
+		vm.stack[vm.sp-n-1], vm.stack[vm.sp-n-2] = vm.stack[vm.sp-n-2], vm.stack[vm.sp-n-1]
+		return
 	case *funcObject:
 		vm.pc++
 		vm.pushCtx()
@@ -2980,12 +3031,23 @@ type newFunc struct {
 	name   unistring.String
 	source string
 
-	length uint32
+	length int
 	strict bool
 }
 
 func (n *newFunc) exec(vm *vm) {
-	obj := vm.r.newFunc(n.name, int(n.length), n.strict)
+	obj := vm.r.newFunc(n.name, n.length, n.strict)
+	obj.prg = n.prg
+	obj.stash = vm.stash
+	obj.src = n.source
+	vm.push(obj.val)
+	vm.pc++
+}
+
+type newMethod newFunc
+
+func (n *newMethod) exec(vm *vm) {
+	obj := vm.r.newMethod(n.name, n.length, n.strict)
 	obj.prg = n.prg
 	obj.stash = vm.stash
 	obj.src = n.source
@@ -2998,7 +3060,7 @@ type newArrowFunc struct {
 }
 
 func (n *newArrowFunc) exec(vm *vm) {
-	obj := vm.r.newArrowFunc(n.name, int(n.length), n.strict)
+	obj := vm.r.newArrowFunc(n.name, n.length, n.strict)
 	obj.prg = n.prg
 	obj.stash = vm.stash
 	obj.src = n.source
@@ -3597,8 +3659,14 @@ func (_typeof) exec(vm *vm) {
 	case *Object:
 	repeat:
 		switch s := v.self.(type) {
-		case *funcObject, *nativeFuncObject, *boundFuncObject, *arrowFuncObject:
+		case *methodFuncObject, *funcObject, *nativeFuncObject, *boundFuncObject, *arrowFuncObject:
 			r = stringFunction
+		case *proxyObject:
+			if s.call == nil {
+				r = stringObjectC
+			} else {
+				r = stringFunction
+			}
 		case *lazyObject:
 			v.self = s.create(v)
 			goto repeat
@@ -3680,7 +3748,6 @@ func (formalArgs createArgsUnmapped) exec(vm *vm) {
 
 	args._putProp("length", intToValue(int64(vm.args)), true, false, true)
 	args._put("callee", vm.r.global.throwerProperty)
-	args._put("caller", vm.r.global.throwerProperty)
 	args._putSym(SymIterator, valueProp(vm.r.global.arrayValues, true, false, true))
 	vm.push(args.val)
 	vm.pc++
@@ -3731,7 +3798,7 @@ func (jmp enumNext) exec(vm *vm) {
 	l := len(vm.iterStack) - 1
 	item, n := vm.iterStack[l].f()
 	if n != nil {
-		vm.iterStack[l].val = stringValueFromRaw(item.name)
+		vm.iterStack[l].val = item.name
 		vm.iterStack[l].f = n
 		vm.pc++
 	} else {
@@ -3770,7 +3837,7 @@ func (_enumPopClose) exec(vm *vm) {
 	vm.iterStack[l] = iterStackItem{}
 	vm.iterStack = vm.iterStack[:l]
 	if iter := item.iter; iter != nil {
-		returnIter(iter)
+		iter.returnIter()
 	}
 	vm.pc++
 }
@@ -3801,19 +3868,9 @@ type iterNext int32
 func (jmp iterNext) exec(vm *vm) {
 	l := len(vm.iterStack) - 1
 	iter := vm.iterStack[l].iter
-	var res *Object
-	var done bool
-	var value Value
-	ex := vm.try(func() {
-		res = vm.r.toObject(toMethod(iter.self.getStr("next", nil))(FunctionCall{This: iter}))
-		done = nilSafe(res.self.getStr("done", nil)).ToBoolean()
-		if !done {
-			value = nilSafe(res.self.getStr("value", nil))
-			vm.iterStack[l].val = value
-		}
-	})
+	value, ex := iter.step()
 	if ex == nil {
-		if done {
+		if value == nil {
 			vm.pc += int(jmp)
 		} else {
 			vm.iterStack[l].val = value
@@ -3827,6 +3884,26 @@ func (jmp iterNext) exec(vm *vm) {
 	}
 }
 
+type iterGetNextOrUndef struct{}
+
+func (iterGetNextOrUndef) exec(vm *vm) {
+	l := len(vm.iterStack) - 1
+	iter := vm.iterStack[l].iter
+	var value Value
+	if iter.iterator != nil {
+		var ex *Exception
+		value, ex = iter.step()
+		if ex != nil {
+			l := len(vm.iterStack) - 1
+			vm.iterStack[l] = iterStackItem{}
+			vm.iterStack = vm.iterStack[:l]
+			panic(ex.val)
+		}
+	}
+	vm.push(nilSafe(value))
+	vm.pc++
+}
+
 type copyStash struct{}
 
 func (copyStash) exec(vm *vm) {
@@ -3855,9 +3932,8 @@ func (r *Runtime) copyDataProperties(target, source Value) {
 		return
 	}
 	sourceObj := source.ToObject(r)
-	for _, key := range sourceObj.self.ownPropertyKeys(false, nil) {
-		v := nilSafe(sourceObj.get(key, nil))
-		createDataPropertyOrThrow(targetObj, key, v)
+	for item, next := iterateEnumerableProperties(sourceObj)(); next != nil; item, next = next() {
+		createDataPropertyOrThrow(targetObj, item.name, item.value)
 	}
 }