Browse Source

Typedarrays (#137)

Implemented ArrayBuffer, DataView and the typed arrays.
Dmitry Panov 5 years ago
parent
commit
1236576b0d

+ 1 - 1
array.go

@@ -422,7 +422,7 @@ func (a *arrayObject) _defineIdxProperty(idx uint32, desc PropertyDescriptor, th
 	}
 	prop, ok := a.baseObject._defineOwnProperty(strconv.FormatUint(uint64(idx), 10), existing, desc, throw)
 	if ok {
-		if idx >= uint32(a.length) {
+		if idx >= a.length {
 			if !a.setLengthInt(int64(idx)+1, throw) {
 				return false
 			}

+ 49 - 5
builtin_array.go

@@ -176,7 +176,7 @@ func (r *Runtime) arrayproto_join(call FunctionCall) Value {
 	l := int(toLength(o.self.getStr("length", nil)))
 	sep := ""
 	if s := call.Argument(0); s != _undefined {
-		sep = s.String()
+		sep = s.toString().String()
 	} else {
 		sep = ","
 	}
@@ -519,14 +519,55 @@ func (r *Runtime) arrayproto_indexOf(call FunctionCall) Value {
 		}
 	}
 
-	return valueInt(-1)
+	return intToValue(-1)
+}
+
+func (r *Runtime) arrayproto_includes(call FunctionCall) Value {
+	o := call.This.ToObject(r)
+	length := toLength(o.self.getStr("length", nil))
+	if length == 0 {
+		return valueFalse
+	}
+
+	n := call.Argument(1).ToInteger()
+	if n >= length {
+		return valueFalse
+	}
+
+	if n < 0 {
+		n = max(length+n, 0)
+	}
+
+	searchElement := call.Argument(0)
+	if searchElement == _negativeZero {
+		searchElement = _positiveZero
+	}
+
+	if arr := r.checkStdArrayObj(o); arr != nil {
+		for _, val := range arr.values[n:] {
+			if searchElement.SameAs(val) {
+				return valueTrue
+			}
+		}
+		return valueFalse
+	}
+
+	for ; n < length; n++ {
+		idx := valueInt(n)
+		val := nilSafe(o.self.getIdx(idx, nil))
+		if searchElement.SameAs(val) {
+			return valueTrue
+		}
+	}
+
+	return valueFalse
 }
 
 func (r *Runtime) arrayproto_lastIndexOf(call FunctionCall) Value {
 	o := call.This.ToObject(r)
 	length := toLength(o.self.getStr("length", nil))
 	if length == 0 {
-		return valueInt(-1)
+		return intToValue(-1)
 	}
 
 	var fromIndex int64
@@ -975,7 +1016,7 @@ func (r *Runtime) arrayproto_findIndex(call FunctionCall) Value {
 		}
 	}
 
-	return valueInt(-1)
+	return intToValue(-1)
 }
 
 func (r *Runtime) checkStdArrayObj(obj *Object) *arrayObject {
@@ -1160,6 +1201,7 @@ func (r *Runtime) createArrayProto(val *Object) objectImpl {
 	o._putProp("find", r.newNativeFunc(r.arrayproto_find, nil, "find", nil, 1), true, false, true)
 	o._putProp("findIndex", r.newNativeFunc(r.arrayproto_findIndex, nil, "findIndex", nil, 1), true, false, true)
 	o._putProp("forEach", r.newNativeFunc(r.arrayproto_forEach, nil, "forEach", nil, 1), true, false, true)
+	o._putProp("includes", r.newNativeFunc(r.arrayproto_includes, nil, "includes", nil, 1), true, false, true)
 	o._putProp("indexOf", r.newNativeFunc(r.arrayproto_indexOf, nil, "indexOf", nil, 1), true, false, true)
 	o._putProp("join", r.newNativeFunc(r.arrayproto_join, nil, "join", nil, 1), true, false, true)
 	o._putProp("keys", r.newNativeFunc(r.arrayproto_keys, nil, "keys", nil, 0), true, false, true)
@@ -1176,7 +1218,7 @@ func (r *Runtime) createArrayProto(val *Object) objectImpl {
 	o._putProp("sort", r.newNativeFunc(r.arrayproto_sort, nil, "sort", nil, 1), true, false, true)
 	o._putProp("splice", r.newNativeFunc(r.arrayproto_splice, nil, "splice", nil, 2), true, false, true)
 	o._putProp("toLocaleString", r.newNativeFunc(r.arrayproto_toLocaleString, nil, "toLocaleString", nil, 0), true, false, true)
-	o._putProp("toString", r.newNativeFunc(r.arrayproto_toString, nil, "toString", nil, 0), true, false, true)
+	o._putProp("toString", r.global.arrayToString, true, false, true)
 	o._putProp("unshift", r.newNativeFunc(r.arrayproto_unshift, nil, "unshift", nil, 1), true, false, true)
 	valuesFunc := r.newNativeFunc(r.arrayproto_values, nil, "values", nil, 0)
 	r.global.arrayValues = valuesFunc
@@ -1221,6 +1263,8 @@ func (r *Runtime) createArrayIterProto(val *Object) objectImpl {
 }
 
 func (r *Runtime) initArray() {
+	r.global.arrayToString = r.newNativeFunc(r.arrayproto_toString, nil, "toString", nil, 0)
+
 	r.global.ArrayIteratorPrototype = r.newLazyObject(r.createArrayIterProto)
 	//r.global.ArrayPrototype = r.newArray(r.global.ObjectPrototype).val
 	//o := r.global.ArrayPrototype.self

+ 1 - 1
builtin_function.go

@@ -139,7 +139,7 @@ func (r *Runtime) functionproto_bind(call FunctionCall) Value {
 	fcall := r.toCallable(call.This)
 	construct := obj.self.assertConstructor()
 
-	l := int(toUInt32(obj.self.getStr("length", nil)))
+	l := int(toUint32(obj.self.getStr("length", nil)))
 	l -= len(call.Arguments) - 1
 	if l < 0 {
 		l = 0

+ 6 - 2
builtin_map.go

@@ -138,7 +138,11 @@ func (r *Runtime) mapProto_getSize(call FunctionCall) Value {
 	return intToValue(int64(mo.m.size))
 }
 
-func (r *Runtime) builtin_newMap(args []Value, proto *Object) *Object {
+func (r *Runtime) builtin_newMap(args []Value, newTarget *Object) *Object {
+	if newTarget == nil {
+		panic(r.needNew("Map"))
+	}
+	proto := r.getPrototypeFromCtor(newTarget, r.global.Map, r.global.MapPrototype)
 	o := &Object{runtime: r}
 
 	mo := &mapObject{}
@@ -238,7 +242,7 @@ func (r *Runtime) createMapProto(val *Object) objectImpl {
 }
 
 func (r *Runtime) createMap(val *Object) objectImpl {
-	o := r.newNativeFuncObj(val, r.constructorThrower("Map"), r.builtin_newMap, "Map", r.global.MapPrototype, 0)
+	o := r.newNativeConstructOnly(val, r.builtin_newMap, r.global.MapPrototype, "Map", 0)
 	o._putSym(symSpecies, &valueProperty{
 		getterFunc:   r.newNativeFunc(r.returnThis, nil, "get [Symbol.species]", nil, 0),
 		accessor:     true,

+ 43 - 1
builtin_map_test.go

@@ -1,6 +1,9 @@
 package goja
 
-import "testing"
+import (
+	"hash/maphash"
+	"testing"
+)
 
 func TestMapEvilIterator(t *testing.T) {
 	const SCRIPT = `
@@ -57,3 +60,42 @@ func TestMapEvilIterator(t *testing.T) {
 	`
 	testScript1(TESTLIB+SCRIPT, _undefined, t)
 }
+
+func BenchmarkMapDelete(b *testing.B) {
+	var key1 Value = asciiString("a")
+	var key2 Value = asciiString("b")
+	one := intToValue(1)
+	two := intToValue(2)
+	for i := 0; i < b.N; i++ {
+		m := newOrderedMap(&maphash.Hash{})
+		m.set(key1, one)
+		m.set(key2, two)
+		if !m.remove(key1) {
+			b.Fatal("remove() returned false")
+		}
+	}
+}
+
+func BenchmarkMapDeleteJS(b *testing.B) {
+	prg, err := Compile("test.js", `
+	var m = new Map([['a',1], ['b', 2]]);
+	
+	var result = m.delete('a');
+
+	if (!result || m.size !== 1) {
+		throw new Error("Fail!");
+	}
+	`,
+		false)
+	if err != nil {
+		b.Fatal(err)
+	}
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		vm := New()
+		_, err := vm.RunProgram(prg)
+		if err != nil {
+			b.Fatal(err)
+		}
+	}
+}

+ 9 - 6
builtin_proxy.go

@@ -239,14 +239,17 @@ func (r *Runtime) newProxy(args []Value, proto *Object) *Object {
 	panic(r.NewTypeError("Cannot create proxy with a non-object as target or handler"))
 }
 
-func (r *Runtime) builtin_newProxy(args []Value, proto *Object) *Object {
-	return r.newProxy(args, proto)
+func (r *Runtime) builtin_newProxy(args []Value, newTarget *Object) *Object {
+	if newTarget == nil {
+		panic(r.needNew("Proxy"))
+	}
+	return r.newProxy(args, r.getPrototypeFromCtor(newTarget, r.global.Proxy, r.global.ObjectPrototype))
 }
 
-func (r *Runtime) NewProxy(target *Object, nativeHandler *ProxyTrapConfig) *Proxy {
+func (r *Runtime) NewProxy(target *Object, nativeHandler *ProxyTrapConfig) Proxy {
 	handler := r.newNativeProxyHandler(nativeHandler)
-	proxy := r.newProxyObject(target, handler, r.global.Proxy)
-	return &Proxy{proxy: proxy}
+	proxy := r.newProxyObject(target, handler, nil)
+	return Proxy{proxy: proxy}
 }
 
 func (r *Runtime) builtin_proxy_revocable(call FunctionCall) Value {
@@ -269,7 +272,7 @@ func (r *Runtime) builtin_proxy_revocable(call FunctionCall) Value {
 }
 
 func (r *Runtime) createProxy(val *Object) objectImpl {
-	o := r.newNativeFuncObj(val, r.constructorThrower("Proxy"), r.builtin_newProxy, "Proxy", nil, 2)
+	o := r.newNativeConstructOnly(val, r.builtin_newProxy, nil, "Proxy", 2)
 
 	o._putProp("revocable", r.newNativeFunc(r.builtin_proxy_revocable, nil, "revocable", nil, 2), true, false, true)
 	return o

+ 17 - 2
builtin_proxy_test.go

@@ -58,7 +58,7 @@ func TestProxy_Object_native_proxy_getPrototypeOf(t *testing.T) {
 	}
 }
 
-/*func TestProxy_Object_target_setPrototypeOf(t *testing.T) {
+func TestProxy_Object_target_setPrototypeOf(t *testing.T) {
 	const SCRIPT = `
     var proto = {};
 	var obj = {};
@@ -82,12 +82,13 @@ func TestProxy_Object_proxy_setPrototypeOf(t *testing.T) {
 			return Object.setPrototypeOf(target, proto2);
 		}
 	});
+	Object.setPrototypeOf(proxy, null);
 	var p = Object.getPrototypeOf(proxy);
 	assert.sameValue(proto2, p);
 	`
 
 	testScript1(TESTLIB+SCRIPT, _undefined, t)
-}*/
+}
 
 func TestProxy_Object_target_isExtensible(t *testing.T) {
 	const SCRIPT = `
@@ -826,3 +827,17 @@ func TestProxy_proxy_forIn(t *testing.T) {
 
 	testScript1(SCRIPT, valueTrue, t)
 }
+
+func TestProxyExport(t *testing.T) {
+	vm := New()
+	v, err := vm.RunString(`
+	new Proxy({}, {});
+	`)
+	if err != nil {
+		t.Fatal(err)
+	}
+	v1 := v.Export()
+	if _, ok := v1.(Proxy); !ok {
+		t.Fatalf("Export returned unexpected type: %T", v1)
+	}
+}

+ 1 - 1
builtin_regexp.go

@@ -513,7 +513,7 @@ func (r *Runtime) regexpproto_stdSplitter(call FunctionCall) Value {
 
 	limit := -1
 	if limitValue != _undefined {
-		limit = int(toUInt32(limitValue))
+		limit = int(toUint32(limitValue))
 	}
 
 	if limit == 0 {

+ 6 - 2
builtin_set.go

@@ -121,7 +121,11 @@ func (r *Runtime) setProto_values(call FunctionCall) Value {
 	return r.createSetIterator(call.This, iterationKindValue)
 }
 
-func (r *Runtime) builtin_newSet(args []Value, proto *Object) *Object {
+func (r *Runtime) builtin_newSet(args []Value, newTarget *Object) *Object {
+	if newTarget == nil {
+		panic(r.needNew("Set"))
+	}
+	proto := r.getPrototypeFromCtor(newTarget, r.global.Set, r.global.SetPrototype)
 	o := &Object{runtime: r}
 
 	so := &setObject{}
@@ -213,7 +217,7 @@ func (r *Runtime) createSetProto(val *Object) objectImpl {
 }
 
 func (r *Runtime) createSet(val *Object) objectImpl {
-	o := r.newNativeFuncObj(val, r.constructorThrower("Set"), r.builtin_newSet, "Set", r.global.SetPrototype, 0)
+	o := r.newNativeConstructOnly(val, r.builtin_newSet, r.global.SetPrototype, "Set", 0)
 	o._putSym(symSpecies, &valueProperty{
 		getterFunc:   r.newNativeFunc(r.returnThis, nil, "get [Symbol.species]", nil, 0),
 		accessor:     true,

+ 3 - 3
builtin_string.go

@@ -102,7 +102,7 @@ func (r *Runtime) stringproto_valueOf(call FunctionCall) Value {
 func (r *Runtime) string_fromcharcode(call FunctionCall) Value {
 	b := make([]byte, len(call.Arguments))
 	for i, arg := range call.Arguments {
-		chr := toUInt16(arg)
+		chr := toUint16(arg)
 		if chr >= utf8.RuneSelf {
 			bb := make([]uint16, len(call.Arguments))
 			for j := 0; j < i; j++ {
@@ -111,7 +111,7 @@ func (r *Runtime) string_fromcharcode(call FunctionCall) Value {
 			bb[i] = chr
 			i++
 			for j, arg := range call.Arguments[i:] {
-				bb[i+j] = toUInt16(arg)
+				bb[i+j] = toUint16(arg)
 			}
 			return unicodeString(bb)
 		}
@@ -501,7 +501,7 @@ func (r *Runtime) stringproto_split(call FunctionCall) Value {
 
 	limit := -1
 	if limitValue != _undefined {
-		limit = int(toUInt32(limitValue))
+		limit = int(toUint32(limitValue))
 	}
 
 	if limit == 0 {

+ 1358 - 49
builtin_typedarrays.go

@@ -1,33 +1,55 @@
 package goja
 
-type objectArrayBuffer struct {
-	baseObject
-	data []byte
+import (
+	"sort"
+	"strings"
+	"unsafe"
+)
+
+type typedArraySortCtx struct {
+	ta           *typedArrayObject
+	compare      func(FunctionCall) Value
+	needValidate bool
 }
 
-func (o *objectArrayBuffer) export() interface{} {
-	return o.data
+func (ctx *typedArraySortCtx) Len() int {
+	return ctx.ta.length
 }
 
-func (r *Runtime) _newArrayBuffer(proto *Object, o *Object) *objectArrayBuffer {
-	if o == nil {
-		o = &Object{runtime: r}
+func (ctx *typedArraySortCtx) Less(i, j int) bool {
+	if ctx.needValidate {
+		ctx.ta.viewedArrayBuf.ensureNotDetached()
+		ctx.needValidate = false
 	}
-	b := &objectArrayBuffer{
-		baseObject: baseObject{
-			class:      classObject,
-			val:        o,
-			prototype:  proto,
-			extensible: true,
-		},
+	offset := ctx.ta.offset
+	if ctx.compare != nil {
+		x := ctx.ta.typedArray.get(offset + i)
+		y := ctx.ta.typedArray.get(offset + j)
+		res := ctx.compare(FunctionCall{
+			This:      _undefined,
+			Arguments: []Value{x, y},
+		}).ToInteger()
+		ctx.needValidate = true
+		return res < 0
 	}
-	o.self = b
-	b.init()
-	return b
+
+	return ctx.ta.typedArray.less(offset+i, offset+j)
+}
+
+func (ctx *typedArraySortCtx) Swap(i, j int) {
+	if ctx.needValidate {
+		ctx.ta.viewedArrayBuf.ensureNotDetached()
+		ctx.needValidate = false
+	}
+	offset := ctx.ta.offset
+	ctx.ta.typedArray.swap(offset+i, offset+j)
 }
 
-func (r *Runtime) builtin_ArrayBuffer(args []Value, proto *Object) *Object {
-	b := r._newArrayBuffer(proto, nil)
+func (r *Runtime) builtin_newArrayBuffer(args []Value, newTarget *Object) *Object {
+	if newTarget == nil {
+		panic(r.needNew("ArrayBuffer"))
+	}
+	b := r._newArrayBuffer(r.getPrototypeFromCtor(newTarget, r.global.ArrayBuffer, r.global.ArrayBufferPrototype), nil)
 	if len(args) > 0 {
 		b.data = make([]byte, toLength(args[0]))
 	}
@@ -36,70 +58,1357 @@ func (r *Runtime) builtin_ArrayBuffer(args []Value, proto *Object) *Object {
 
 func (r *Runtime) arrayBufferProto_getByteLength(call FunctionCall) Value {
 	o := r.toObject(call.This)
-	if b, ok := o.self.(*objectArrayBuffer); ok {
+	if b, ok := o.self.(*arrayBufferObject); ok {
+		if b.data == nil {
+			panic(r.NewTypeError("ArrayBuffer is detached"))
+		}
 		return intToValue(int64(len(b.data)))
 	}
-	r.typeErrorResult(true, "Object is not ArrayBuffer: %s", o)
-	panic("unreachable")
+	panic(r.NewTypeError("Object is not ArrayBuffer: %s", o))
 }
 
 func (r *Runtime) arrayBufferProto_slice(call FunctionCall) Value {
 	o := r.toObject(call.This)
-	if b, ok := o.self.(*objectArrayBuffer); ok {
+	if b, ok := o.self.(*arrayBufferObject); ok {
 		l := int64(len(b.data))
-		start := toLength(call.Argument(0))
-		if start < 0 {
-			start = l + start
-		}
-		if start < 0 {
-			start = 0
-		} else if start > l {
-			start = l
-		}
+		start := relToIdx(toLength(call.Argument(0)), l)
 		var stop int64
 		if arg := call.Argument(1); arg != _undefined {
 			stop = toLength(arg)
-			if stop < 0 {
-				stop = int64(len(b.data)) + stop
+		} else {
+			stop = l
+		}
+		stop = relToIdx(stop, l)
+		newLen := max(stop-start, 0)
+		ret := r.speciesConstructor(o, r.global.ArrayBuffer)([]Value{intToValue(newLen)}, nil)
+		if ab, ok := ret.self.(*arrayBufferObject); ok {
+			if ab.data == nil {
+				panic(r.NewTypeError("Species constructor returned a detached ArrayBuffer"))
 			}
-			if stop < 0 {
-				stop = 0
-			} else if stop > l {
-				stop = l
+			if ret == o {
+				panic(r.NewTypeError("Species constructor returned the same ArrayBuffer"))
 			}
+			if int64(len(ab.data)) < newLen {
+				panic(r.NewTypeError("Species constructor returned an ArrayBuffer that is too small: %d", len(ab.data)))
+			}
+			if b.data == nil {
+				panic(r.NewTypeError("Species constructor has detached the current ArrayBuffer"))
+			}
+
+			if stop > start {
+				copy(ab.data, b.data[start:stop])
+			}
+			return ret
+		}
+		panic(r.NewTypeError("Species constructor did not return an ArrayBuffer: %s", ret.String()))
+	}
+	panic(r.NewTypeError("Object is not ArrayBuffer: %s", o))
+}
+
+func (r *Runtime) arrayBuffer_isView(call FunctionCall) Value {
+	if o, ok := call.This.(*Object); ok {
+		if _, ok := o.self.(*dataViewObject); ok {
+			return valueTrue
+		}
+	}
+	return valueFalse
+}
+
+func (r *Runtime) newDataView(args []Value, newTarget *Object) *Object {
+	if newTarget == nil {
+		panic(r.needNew("DataView"))
+	}
+	proto := r.getPrototypeFromCtor(newTarget, r.global.DataView, r.global.DataViewPrototype)
+	var bufArg Value
+	if len(args) > 0 {
+		bufArg = args[0]
+	}
+	var buffer *arrayBufferObject
+	if o, ok := bufArg.(*Object); ok {
+		if b, ok := o.self.(*arrayBufferObject); ok {
+			buffer = b
+		}
+	}
+	if buffer == nil {
+		panic(r.NewTypeError("First argument to DataView constructor must be an ArrayBuffer"))
+	}
+	var byteOffset, byteLen int
+	if len(args) > 1 {
+		offsetArg := nilSafe(args[1])
+		byteOffset = r.toIndex(offsetArg)
+		buffer.ensureNotDetached()
+		if byteOffset > len(buffer.data) {
+			panic(r.newError(r.global.RangeError, "Start offset %s is outside the bounds of the buffer", offsetArg.String()))
+		}
+	}
+	if len(args) > 2 && args[2] != nil && args[2] != _undefined {
+		byteLen = r.toIndex(args[2])
+		if byteOffset+byteLen > len(buffer.data) {
+			panic(r.newError(r.global.RangeError, "Invalid DataView length %d", byteLen))
+		}
+	} else {
+		byteLen = len(buffer.data) - byteOffset
+	}
+	o := &Object{runtime: r}
+	b := &dataViewObject{
+		baseObject: baseObject{
+			class:      classObject,
+			val:        o,
+			prototype:  proto,
+			extensible: true,
+		},
+		viewedArrayBuf: buffer,
+		byteOffset:     byteOffset,
+		byteLen:        byteLen,
+	}
+	o.self = b
+	b.init()
+	return o
+}
+
+func (r *Runtime) dataViewProto_getBuffer(call FunctionCall) Value {
+	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		return dv.viewedArrayBuf.val
+	}
+	panic(r.NewTypeError("Method get DataView.prototype.buffer called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) dataViewProto_getByteLen(call FunctionCall) Value {
+	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		dv.viewedArrayBuf.ensureNotDetached()
+		return intToValue(int64(dv.byteLen))
+	}
+	panic(r.NewTypeError("Method get DataView.prototype.byteLength called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) dataViewProto_getByteOffset(call FunctionCall) Value {
+	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		dv.viewedArrayBuf.ensureNotDetached()
+		return intToValue(int64(dv.byteOffset))
+	}
+	panic(r.NewTypeError("Method get DataView.prototype.byteOffset called on incompatible receiver %s", call.This.String()))
+}
+
+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))))
+	}
+	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)))
+	}
+	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)
+		return intToValue(int64(dv.viewedArrayBuf.getInt8(idx)))
+	}
+	panic(r.NewTypeError("Method DataView.prototype.getInt8 called on incompatible receiver %s", call.This.String()))
+}
+
+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))))
+	}
+	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))))
+	}
+	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)
+		return intToValue(int64(dv.viewedArrayBuf.getUint8(idx)))
+	}
+	panic(r.NewTypeError("Method DataView.prototype.getUint8 called on incompatible receiver %s", call.This.String()))
+}
+
+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))))
+	}
+	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))))
+	}
+	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 {
+		val := toFloat32(call.Argument(1))
+		idx, bo := dv.getIdxAndByteOrder(call.Argument(0), call.Argument(2), 4)
+		dv.viewedArrayBuf.setFloat32(idx, val, bo)
+		return _undefined
+	}
+	panic(r.NewTypeError("Method DataView.prototype.setFloat32 called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) dataViewProto_setFloat64(call FunctionCall) Value {
+	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		val := call.Argument(1).ToFloat()
+		idx, bo := dv.getIdxAndByteOrder(call.Argument(0), call.Argument(2), 8)
+		dv.viewedArrayBuf.setFloat64(idx, val, bo)
+		return _undefined
+	}
+	panic(r.NewTypeError("Method DataView.prototype.setFloat64 called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) dataViewProto_setInt8(call FunctionCall) Value {
+	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		val := toInt8(call.Argument(1))
+		idx, _ := dv.getIdxAndByteOrder(call.Argument(0), call.Argument(2), 1)
+		dv.viewedArrayBuf.setInt8(idx, val)
+		return _undefined
+	}
+	panic(r.NewTypeError("Method DataView.prototype.setInt8 called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) dataViewProto_setInt16(call FunctionCall) Value {
+	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		val := toInt16(call.Argument(1))
+		idx, bo := dv.getIdxAndByteOrder(call.Argument(0), call.Argument(2), 2)
+		dv.viewedArrayBuf.setInt16(idx, val, bo)
+		return _undefined
+	}
+	panic(r.NewTypeError("Method DataView.prototype.setInt16 called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) dataViewProto_setInt32(call FunctionCall) Value {
+	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		val := toInt32(call.Argument(1))
+		idx, bo := dv.getIdxAndByteOrder(call.Argument(0), call.Argument(2), 4)
+		dv.viewedArrayBuf.setInt32(idx, val, bo)
+		return _undefined
+	}
+	panic(r.NewTypeError("Method DataView.prototype.setInt32 called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) dataViewProto_setUint8(call FunctionCall) Value {
+	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		val := toUint8(call.Argument(1))
+		idx, _ := dv.getIdxAndByteOrder(call.Argument(0), call.Argument(2), 1)
+		dv.viewedArrayBuf.setUint8(idx, val)
+		return _undefined
+	}
+	panic(r.NewTypeError("Method DataView.prototype.setUint8 called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) dataViewProto_setUint16(call FunctionCall) Value {
+	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		val := toUint16(call.Argument(1))
+		idx, bo := dv.getIdxAndByteOrder(call.Argument(0), call.Argument(2), 2)
+		dv.viewedArrayBuf.setUint16(idx, val, bo)
+		return _undefined
+	}
+	panic(r.NewTypeError("Method DataView.prototype.setUint16 called on incompatible receiver %s", call.This.String()))
+}
 
+func (r *Runtime) dataViewProto_setUint32(call FunctionCall) Value {
+	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		val := toUint32(call.Argument(1))
+		idx, bo := dv.getIdxAndByteOrder(call.Argument(0), call.Argument(2), 4)
+		dv.viewedArrayBuf.setUint32(idx, val, bo)
+		return _undefined
+	}
+	panic(r.NewTypeError("Method DataView.prototype.setUint32 called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_getBuffer(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		return ta.viewedArrayBuf.val
+	}
+	panic(r.NewTypeError("Method get TypedArray.prototype.buffer called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_getByteLen(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		if ta.viewedArrayBuf.data == nil {
+			return _positiveZero
+		}
+		return intToValue(int64(ta.length) * int64(ta.elemSize))
+	}
+	panic(r.NewTypeError("Method get TypedArray.prototype.byteLength called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_getLength(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		if ta.viewedArrayBuf.data == nil {
+			return _positiveZero
+		}
+		return intToValue(int64(ta.length))
+	}
+	panic(r.NewTypeError("Method get TypedArray.prototype.length called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_getByteOffset(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		if ta.viewedArrayBuf.data == nil {
+			return _positiveZero
+		}
+		return intToValue(int64(ta.offset) * int64(ta.elemSize))
+	}
+	panic(r.NewTypeError("Method get TypedArray.prototype.byteOffset called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_copyWithin(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached()
+		l := int64(ta.length)
+		var relEnd int64
+		to := toInt(relToIdx(call.Argument(0).ToInteger(), l))
+		from := toInt(relToIdx(call.Argument(1).ToInteger(), l))
+		if end := call.Argument(2); end != _undefined {
+			relEnd = end.ToInteger()
 		} else {
-			stop = l
+			relEnd = l
+		}
+		final := toInt(relToIdx(relEnd, l))
+		data := ta.viewedArrayBuf.data
+		offset := ta.offset
+		elemSize := ta.elemSize
+		if final > from {
+			copy(data[(offset+to)*elemSize:], data[(offset+from)*elemSize:(offset+final)*elemSize])
+		}
+		return call.This
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.copyWithin called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_entries(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached()
+		return r.createArrayIterator(ta.val, iterationKindKeyValue)
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.entries called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_every(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached()
+		callbackFn := r.toCallable(call.Argument(0))
+		fc := FunctionCall{
+			This:      call.Argument(1),
+			Arguments: []Value{nil, nil, call.This},
+		}
+		for k := 0; k < ta.length; k++ {
+			ta.viewedArrayBuf.ensureNotDetached()
+			fc.Arguments[0] = ta.typedArray.get(ta.offset + k)
+			fc.Arguments[1] = intToValue(int64(k))
+			if !callbackFn(fc).ToBoolean() {
+				return valueFalse
+			}
+		}
+		return valueTrue
+
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.every called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_fill(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached()
+		l := int64(ta.length)
+		k := toInt(relToIdx(call.Argument(1).ToInteger(), l))
+		var relEnd int64
+		if endArg := call.Argument(2); endArg != _undefined {
+			relEnd = endArg.ToInteger()
+		} else {
+			relEnd = l
+		}
+		final := toInt(relToIdx(relEnd, l))
+		value := ta.typedArray.toRaw(call.Argument(0))
+		ta.viewedArrayBuf.ensureNotDetached()
+		for ; k < final; k++ {
+			ta.typedArray.setRaw(ta.offset+k, value)
+		}
+		return call.This
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.fill called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_filter(call FunctionCall) Value {
+	o := r.toObject(call.This)
+	if ta, ok := o.self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached()
+		callbackFn := r.toCallable(call.Argument(0))
+		fc := FunctionCall{
+			This:      call.Argument(1),
+			Arguments: []Value{nil, nil, call.This},
+		}
+		buf := make([]byte, 0, ta.length*ta.elemSize)
+		captured := 0
+		for k := 0; k < ta.length; k++ {
+			ta.viewedArrayBuf.ensureNotDetached()
+			fc.Arguments[0] = ta.typedArray.get(k)
+			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]...)
+				captured++
+			}
+		}
+		c := r.speciesConstructorObj(o, ta.defaultCtor)
+		ab := r._newArrayBuffer(r.global.ArrayBufferPrototype, nil)
+		ab.data = buf
+		kept := r.toConstructor(ta.defaultCtor)([]Value{ab.val}, ta.defaultCtor)
+		if c == ta.defaultCtor {
+			return kept
+		} else {
+			ret := r.typedArrayCreate(c, []Value{intToValue(int64(captured))})
+			keptTa := kept.self.(*typedArrayObject)
+			for i := 0; i < captured; i++ {
+				ret.typedArray.set(i, keptTa.typedArray.get(i))
+			}
+			return ret.val
+		}
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.filter called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_find(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached()
+		predicate := r.toCallable(call.Argument(0))
+		fc := FunctionCall{
+			This:      call.Argument(1),
+			Arguments: []Value{nil, nil, call.This},
+		}
+		for k := 0; k < ta.length; k++ {
+			ta.viewedArrayBuf.ensureNotDetached()
+			val := ta.typedArray.get(ta.offset + k)
+			fc.Arguments[0] = val
+			fc.Arguments[1] = intToValue(int64(k))
+			if predicate(fc).ToBoolean() {
+				return val
+			}
+		}
+		return _undefined
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.find called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_findIndex(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached()
+		predicate := r.toCallable(call.Argument(0))
+		fc := FunctionCall{
+			This:      call.Argument(1),
+			Arguments: []Value{nil, nil, call.This},
+		}
+		for k := 0; k < ta.length; k++ {
+			ta.viewedArrayBuf.ensureNotDetached()
+			fc.Arguments[0] = ta.typedArray.get(ta.offset + k)
+			fc.Arguments[1] = intToValue(int64(k))
+			if predicate(fc).ToBoolean() {
+				return fc.Arguments[1]
+			}
+		}
+		return intToValue(-1)
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.findIndex called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_forEach(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached()
+		callbackFn := r.toCallable(call.Argument(0))
+		fc := FunctionCall{
+			This:      call.Argument(1),
+			Arguments: []Value{nil, nil, call.This},
+		}
+		for k := 0; k < ta.length; k++ {
+			ta.viewedArrayBuf.ensureNotDetached()
+			if val := ta.typedArray.get(k); val != nil {
+				fc.Arguments[0] = val
+				fc.Arguments[1] = intToValue(int64(k))
+				callbackFn(fc)
+			}
 		}
+		return _undefined
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.forEach called on incompatible receiver %s", call.This.String()))
+}
 
-		ret := r._newArrayBuffer(r.global.ArrayBufferPrototype, nil)
+func (r *Runtime) typedArrayProto_includes(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached()
+		length := int64(ta.length)
+		if length == 0 {
+			return valueFalse
+		}
 
-		if stop > start {
-			ret.data = b.data[start:stop]
+		n := call.Argument(1).ToInteger()
+		if n >= length {
+			return valueFalse
 		}
 
-		return ret.val
+		if n < 0 {
+			n = max(length+n, 0)
+		}
+
+		ta.viewedArrayBuf.ensureNotDetached()
+		searchElement := call.Argument(0)
+		if searchElement == _negativeZero {
+			searchElement = _positiveZero
+		}
+		if ta.typedArray.typeMatch(searchElement) {
+			se := ta.typedArray.toRaw(searchElement)
+			for k := toInt(n); k < ta.length; k++ {
+				if ta.typedArray.getRaw(ta.offset+k) == se {
+					return valueTrue
+				}
+			}
+		}
+		return valueFalse
 	}
-	r.typeErrorResult(true, "Object is not ArrayBuffer: %s", o)
-	panic("unreachable")
+	panic(r.NewTypeError("Method TypedArray.prototype.includes called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_indexOf(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached()
+		length := int64(ta.length)
+		if length == 0 {
+			return intToValue(-1)
+		}
+
+		n := call.Argument(1).ToInteger()
+		if n >= length {
+			return intToValue(-1)
+		}
+
+		if n < 0 {
+			n = max(length+n, 0)
+		}
+
+		ta.viewedArrayBuf.ensureNotDetached()
+		searchElement := call.Argument(0)
+		if searchElement == _negativeZero {
+			searchElement = _positiveZero
+		}
+		if !IsNaN(searchElement) && ta.typedArray.typeMatch(searchElement) {
+			se := ta.typedArray.toRaw(searchElement)
+			for k := toInt(n); k < ta.length; k++ {
+				if ta.typedArray.getRaw(ta.offset+k) == se {
+					return intToValue(int64(k))
+				}
+			}
+		}
+		return intToValue(-1)
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.indexOf called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_join(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached()
+		s := call.Argument(0)
+		sep := ""
+		if s != _undefined {
+			sep = s.toString().String()
+		} else {
+			sep = ","
+		}
+		l := ta.length
+		if l == 0 {
+			return stringEmpty
+		}
+
+		var buf strings.Builder
+
+		ta.viewedArrayBuf.ensureNotDetached()
+		element0 := ta.typedArray.get(0)
+		if element0 != nil && element0 != _undefined && element0 != _null {
+			buf.WriteString(element0.String())
+		}
+
+		for i := 1; i < l; i++ {
+			ta.viewedArrayBuf.ensureNotDetached()
+			buf.WriteString(sep)
+			element := ta.typedArray.get(i)
+			if element != nil && element != _undefined && element != _null {
+				buf.WriteString(element.String())
+			}
+		}
+
+		return newStringValue(buf.String())
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.join called on incompatible receiver"))
+}
+
+func (r *Runtime) typedArrayProto_keys(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached()
+		return r.createArrayIterator(ta.val, iterationKindKey)
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.keys called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_lastIndexOf(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached()
+		length := int64(ta.length)
+		if length == 0 {
+			return intToValue(-1)
+		}
+
+		var fromIndex int64
+
+		if len(call.Arguments) < 2 {
+			fromIndex = length - 1
+		} else {
+			fromIndex = call.Argument(1).ToInteger()
+			if fromIndex >= 0 {
+				fromIndex = min(fromIndex, length-1)
+			} else {
+				fromIndex += length
+			}
+		}
+
+		ta.viewedArrayBuf.ensureNotDetached()
+		searchElement := call.Argument(0)
+		if searchElement == _negativeZero {
+			searchElement = _positiveZero
+		}
+		if !IsNaN(searchElement) && ta.typedArray.typeMatch(searchElement) {
+			se := ta.typedArray.toRaw(searchElement)
+			for k := toInt(fromIndex); k >= 0; k-- {
+				if ta.typedArray.getRaw(ta.offset+k) == se {
+					return intToValue(int64(k))
+				}
+			}
+		}
+
+		return intToValue(-1)
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.lastIndexOf called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_map(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached()
+		callbackFn := r.toCallable(call.Argument(0))
+		fc := FunctionCall{
+			This:      call.Argument(1),
+			Arguments: []Value{nil, nil, call.This},
+		}
+		dst := r.typedArraySpeciesCreate(ta, []Value{intToValue(int64(ta.length))})
+		for i := 0; i < ta.length; i++ {
+			ta.viewedArrayBuf.ensureNotDetached()
+			fc.Arguments[0] = ta.typedArray.get(ta.offset + i)
+			fc.Arguments[1] = intToValue(int64(i))
+			dst.typedArray.set(i, callbackFn(fc))
+		}
+		return dst.val
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.map called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_reduce(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached()
+		callbackFn := r.toCallable(call.Argument(0))
+		fc := FunctionCall{
+			This:      _undefined,
+			Arguments: []Value{nil, nil, nil, call.This},
+		}
+		k := 0
+		if len(call.Arguments) >= 2 {
+			fc.Arguments[0] = call.Argument(1)
+		} else {
+			if ta.length > 0 {
+				fc.Arguments[0] = ta.typedArray.get(ta.offset + 0)
+				k = 1
+			}
+		}
+		if fc.Arguments[0] == nil {
+			panic(r.NewTypeError("Reduce of empty array with no initial value"))
+		}
+		for ; k < ta.length; k++ {
+			ta.viewedArrayBuf.ensureNotDetached()
+			idx := valueInt(k)
+			fc.Arguments[1] = ta.typedArray.get(ta.offset + k)
+			fc.Arguments[2] = idx
+			fc.Arguments[0] = callbackFn(fc)
+		}
+		return fc.Arguments[0]
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.reduce called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_reduceRight(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached()
+		callbackFn := r.toCallable(call.Argument(0))
+		fc := FunctionCall{
+			This:      _undefined,
+			Arguments: []Value{nil, nil, nil, call.This},
+		}
+		k := ta.length - 1
+		if len(call.Arguments) >= 2 {
+			fc.Arguments[0] = call.Argument(1)
+		} else {
+			if k >= 0 {
+				fc.Arguments[0] = ta.typedArray.get(ta.offset + k)
+				k--
+			}
+		}
+		if fc.Arguments[0] == nil {
+			panic(r.NewTypeError("Reduce of empty array with no initial value"))
+		}
+		for ; k >= 0; k-- {
+			ta.viewedArrayBuf.ensureNotDetached()
+			idx := valueInt(k)
+			fc.Arguments[1] = ta.typedArray.get(ta.offset + k)
+			fc.Arguments[2] = idx
+			fc.Arguments[0] = callbackFn(fc)
+		}
+		return fc.Arguments[0]
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.reduceRight called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_reverse(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached()
+		l := ta.length
+		middle := l / 2
+		for lower := 0; lower != middle; lower++ {
+			upper := l - lower - 1
+			ta.typedArray.swap(ta.offset+lower, ta.offset+upper)
+		}
+
+		return call.This
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.reverse called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_set(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		srcObj := r.toObject(call.Argument(0))
+		targetOffset := toInt(call.Argument(1).ToInteger())
+		if targetOffset < 0 {
+			panic(r.newError(r.global.RangeError, "offset should be >= 0"))
+		}
+		ta.viewedArrayBuf.ensureNotDetached()
+		targetLen := ta.length
+		if src, ok := srcObj.self.(*typedArrayObject); ok {
+			src.viewedArrayBuf.ensureNotDetached()
+			srcLen := src.length
+			if x := srcLen + targetOffset; x < 0 || x > targetLen {
+				panic(r.newError(r.global.RangeError, "Source is too large"))
+			}
+			if src.defaultCtor == ta.defaultCtor {
+				copy(ta.viewedArrayBuf.data[(ta.offset+targetOffset)*ta.elemSize:],
+					src.viewedArrayBuf.data[src.offset*src.elemSize:(src.offset+srcLen)*src.elemSize])
+			} else {
+				curSrc := uintptr(unsafe.Pointer(&src.viewedArrayBuf.data[src.offset*src.elemSize]))
+				endSrc := curSrc + uintptr(srcLen*src.elemSize)
+				curDst := uintptr(unsafe.Pointer(&ta.viewedArrayBuf.data[(ta.offset+targetOffset)*ta.elemSize]))
+				dstOffset := ta.offset + targetOffset
+				srcOffset := src.offset
+				if ta.elemSize == src.elemSize {
+					if curDst <= curSrc || curDst >= endSrc {
+						for i := 0; i < srcLen; i++ {
+							ta.typedArray.set(dstOffset+i, src.typedArray.get(srcOffset+i))
+						}
+					} else {
+						for i := srcLen - 1; i >= 0; i-- {
+							ta.typedArray.set(dstOffset+i, src.typedArray.get(srcOffset+i))
+						}
+					}
+				} else {
+					x := int(curDst-curSrc) / (src.elemSize - ta.elemSize)
+					if x < 0 {
+						x = 0
+					} else if x > srcLen {
+						x = srcLen
+					}
+					if ta.elemSize < src.elemSize {
+						for i := x; i < srcLen; i++ {
+							ta.typedArray.set(dstOffset+i, src.typedArray.get(srcOffset+i))
+						}
+						for i := x - 1; i >= 0; i-- {
+							ta.typedArray.set(dstOffset+i, src.typedArray.get(srcOffset+i))
+						}
+					} else {
+						for i := 0; i < x; i++ {
+							ta.typedArray.set(dstOffset+i, src.typedArray.get(srcOffset+i))
+						}
+						for i := srcLen - 1; i >= x; i-- {
+							ta.typedArray.set(dstOffset+i, src.typedArray.get(srcOffset+i))
+						}
+					}
+				}
+			}
+		} else {
+			targetLen := ta.length
+			srcLen := toInt(toLength(srcObj.self.getStr("length", nil)))
+			if x := srcLen + targetOffset; x < 0 || x > targetLen {
+				panic(r.newError(r.global.RangeError, "Source is too large"))
+			}
+			for i := 0; i < srcLen; i++ {
+				val := nilSafe(srcObj.self.getIdx(valueInt(i), nil))
+				ta.viewedArrayBuf.ensureNotDetached()
+				ta.typedArray.set(targetOffset+i, val)
+			}
+		}
+		return _undefined
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.set called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_slice(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached()
+		length := int64(ta.length)
+		start := toInt(relToIdx(call.Argument(0).ToInteger(), length))
+		var e int64
+		if endArg := call.Argument(1); endArg != _undefined {
+			e = endArg.ToInteger()
+		} else {
+			e = length
+		}
+		end := toInt(relToIdx(e, length))
+
+		count := end - start
+		if count < 0 {
+			count = 0
+		}
+		dst := r.typedArraySpeciesCreate(ta, []Value{intToValue(int64(count))})
+		if dst.defaultCtor == ta.defaultCtor {
+			if count > 0 {
+				ta.viewedArrayBuf.ensureNotDetached()
+				offset := ta.offset
+				elemSize := ta.elemSize
+				copy(dst.viewedArrayBuf.data, ta.viewedArrayBuf.data[(offset+start)*elemSize:(offset+start+count)*elemSize])
+			}
+		} else {
+			for i := 0; i < count; i++ {
+				ta.viewedArrayBuf.ensureNotDetached()
+				dst.typedArray.set(i, ta.typedArray.get(ta.offset+start+i))
+			}
+		}
+		return dst.val
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.slice called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_some(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached()
+		callbackFn := r.toCallable(call.Argument(0))
+		fc := FunctionCall{
+			This:      call.Argument(1),
+			Arguments: []Value{nil, nil, call.This},
+		}
+		for k := 0; k < ta.length; k++ {
+			ta.viewedArrayBuf.ensureNotDetached()
+			fc.Arguments[0] = ta.typedArray.get(ta.offset + k)
+			fc.Arguments[1] = intToValue(int64(k))
+			if callbackFn(fc).ToBoolean() {
+				return valueTrue
+			}
+		}
+		return valueFalse
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.some called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_sort(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached()
+		var compareFn func(FunctionCall) Value
+
+		if arg, ok := call.Argument(0).(*Object); ok {
+			compareFn, _ = arg.self.assertCallable()
+		}
+
+		ctx := typedArraySortCtx{
+			ta:      ta,
+			compare: compareFn,
+		}
+
+		sort.Sort(&ctx)
+		return call.This
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.sort called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_subarray(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		l := int64(ta.length)
+		beginIdx := relToIdx(call.Argument(0).ToInteger(), l)
+		var relEnd int64
+		if endArg := call.Argument(1); endArg != _undefined {
+			relEnd = endArg.ToInteger()
+		} else {
+			relEnd = l
+		}
+		endIdx := relToIdx(relEnd, l)
+		newLen := max(endIdx-beginIdx, 0)
+		return r.typedArraySpeciesCreate(ta, []Value{ta.viewedArrayBuf.val,
+			intToValue((int64(ta.offset) + beginIdx) * int64(ta.elemSize)),
+			intToValue(newLen),
+		}).val
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.subarray called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_toLocaleString(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		length := ta.length
+		var buf strings.Builder
+		for i := 0; i < length; i++ {
+			ta.viewedArrayBuf.ensureNotDetached()
+			if i > 0 {
+				buf.WriteByte(',')
+			}
+			item := ta.typedArray.get(i)
+			r.writeItemLocaleString(item, &buf)
+		}
+		return newStringValue(buf.String())
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.toLocaleString called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_values(call FunctionCall) Value {
+	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
+		ta.viewedArrayBuf.ensureNotDetached()
+		return r.createArrayIterator(ta.val, iterationKindValue)
+	}
+	panic(r.NewTypeError("Method TypedArray.prototype.values called on incompatible receiver %s", call.This.String()))
+}
+
+func (r *Runtime) typedArrayProto_toStringTag(call FunctionCall) Value {
+	if obj, ok := call.This.(*Object); ok {
+		if ta, ok := obj.self.(*typedArrayObject); ok {
+			return ta.defaultCtor.self.getStr("name", nil)
+		}
+	}
+
+	return _undefined
+}
+
+func (r *Runtime) newTypedArray([]Value, *Object) *Object {
+	panic(r.NewTypeError("Abstract class TypedArray not directly constructable"))
+}
+
+func (r *Runtime) typedArray_from(call FunctionCall) Value {
+	mapFn := call.Argument(1)
+	if mapFn == _undefined {
+		mapFn = nil
+	}
+	return r.typedArrayFrom(r.toObject(call.This), call.Argument(0).ToObject(r), mapFn, call.Argument(2))
+}
+
+func (r *Runtime) typedArray_of(call FunctionCall) Value {
+	ta := r.typedArrayCreate(r.toObject(call.This), []Value{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 {
+	buf := r._newArrayBuffer(r.global.ArrayBufferPrototype, nil)
+	ta := taCtor(buf, 0, length, r.getPrototypeFromCtor(newTarget, nil, r.global.TypedArrayPrototype))
+	if length > 0 {
+		buf.data = make([]byte, length*ta.elemSize)
+	}
+	return ta.val
+}
+
+func (r *Runtime) typedArraySpeciesCreate(ta *typedArrayObject, args []Value) *typedArrayObject {
+	return r.typedArrayCreate(r.speciesConstructorObj(ta.val, ta.defaultCtor), args)
+}
+
+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()
+		if len(args) == 1 {
+			if l, ok := args[0].(valueInt); ok {
+				if ta.length < int(l) {
+					panic(r.NewTypeError("Derived TypedArray constructor created an array which was too small"))
+				}
+			}
+		}
+		return ta
+	}
+	panic(r.NewTypeError("Invalid TypedArray: %s", o))
+}
+
+func (r *Runtime) typedArrayFrom(ctor, items *Object, mapFn, thisValue Value) *Object {
+	var mapFc func(call FunctionCall) Value
+	if mapFn != nil {
+		mapFc = r.toCallable(mapFn)
+		if thisValue == nil {
+			thisValue = _undefined
+		}
+	}
+	usingIter := toMethod(items.self.getSym(symIterator, nil))
+	if usingIter != nil {
+		iter := r.getIterator(items, usingIter)
+		var values []Value
+		r.iterate(iter, func(item Value) {
+			values = append(values, item)
+		})
+		ta := r.typedArrayCreate(ctor, []Value{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 := toInt(toLength(items.self.getStr("length", nil)))
+	ta := r.typedArrayCreate(ctor, []Value{intToValue(int64(length))})
+	if mapFc == nil {
+		for i := 0; i < length; i++ {
+			ta.typedArray.set(i, nilSafe(items.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] = items.self.getIdx(idx, nil), idx
+			ta.typedArray.set(i, mapFc(fc))
+		}
+	}
+	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))
+	var byteOffset int
+	if len(args) > 1 && args[1] != nil && args[1] != _undefined {
+		byteOffset = r.toIndex(args[1])
+		if byteOffset%ta.elemSize != 0 {
+			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()
+	var length int
+	if len(args) > 2 && args[2] != nil && args[2] != _undefined {
+		length = r.toIndex(args[2])
+		if byteOffset+length*ta.elemSize > len(ab.data) {
+			panic(r.newError(r.global.RangeError, "Invalid typed array length: %d", length))
+		}
+	} else {
+		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
+	}
+	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})
+	src.viewedArrayBuf.ensureNotDetached()
+	l := src.length
+	dst.viewedArrayBuf.prototype = r.getPrototypeFromCtor(r.toObject(src.viewedArrayBuf.getStr("constructor", nil)), r.global.ArrayBuffer, r.global.ArrayBufferPrototype)
+	dst.viewedArrayBuf.data = make([]byte, int64(l)*int64(dst.elemSize))
+	if src.defaultCtor == dst.defaultCtor {
+		copy(dst.viewedArrayBuf.data, src.viewedArrayBuf.data[src.offset*src.elemSize:])
+		dst.length = src.length
+		return dst.val
+	}
+	dst.length = l
+	for i := 0; i < l; i++ {
+		dst.typedArray.set(i, src.typedArray.get(src.offset+i))
+	}
+	return dst.val
+}
+
+func (r *Runtime) _newTypedArray(args []Value, newTarget *Object, taCtor typedArrayObjectCtor) *Object {
+	if newTarget == nil {
+		panic(r.needNew("TypedArray"))
+	}
+	if len(args) > 0 {
+		if obj, ok := args[0].(*Object); ok {
+			switch o := obj.self.(type) {
+			case *arrayBufferObject:
+				return r._newTypedArrayFromArrayBuffer(o, args, newTarget, taCtor)
+			case *typedArrayObject:
+				return r._newTypedArrayFromTypedArray(o, newTarget)
+			default:
+				return r.typedArrayFrom(newTarget, obj, nil, nil)
+			}
+		}
+	}
+	var l int
+	if len(args) > 0 {
+		if arg0 := args[0]; arg0 != nil {
+			l = r.toIndex(arg0)
+		}
+	}
+	return r.allocateTypedArray(newTarget, l, taCtor)
+}
+
+func (r *Runtime) newUint8Array(args []Value, newTarget *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newUint8ArrayObject)
+}
+
+func (r *Runtime) newUint8ClampedArray(args []Value, newTarget *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newUint8ClampedArrayObject)
+}
+
+func (r *Runtime) newInt8Array(args []Value, newTarget *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newInt8ArrayObject)
+}
+
+func (r *Runtime) newUint16Array(args []Value, newTarget *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newUint16ArrayObject)
+}
+
+func (r *Runtime) newInt16Array(args []Value, newTarget *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newInt16ArrayObject)
+}
+
+func (r *Runtime) newUint32Array(args []Value, newTarget *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newUint32ArrayObject)
+}
+
+func (r *Runtime) newInt32Array(args []Value, newTarget *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newInt32ArrayObject)
+}
+
+func (r *Runtime) newFloat32Array(args []Value, newTarget *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newFloat32ArrayObject)
+}
+
+func (r *Runtime) newFloat64Array(args []Value, newTarget *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newFloat64ArrayObject)
 }
 
 func (r *Runtime) createArrayBufferProto(val *Object) objectImpl {
-	b := r._newArrayBuffer(r.global.Object, val)
+	b := newBaseObjectObj(val, r.global.ObjectPrototype, classObject)
 	byteLengthProp := &valueProperty{
 		accessor:     true,
 		configurable: true,
 		getterFunc:   r.newNativeFunc(r.arrayBufferProto_getByteLength, nil, "get byteLength", nil, 0),
 	}
 	b._put("byteLength", byteLengthProp)
+	b._putProp("constructor", r.global.ArrayBuffer, true, false, true)
 	b._putProp("slice", r.newNativeFunc(r.arrayBufferProto_slice, nil, "slice", nil, 2), true, false, true)
+	b._putSym(symToStringTag, valueProp(asciiString("ArrayBuffer"), false, false, true))
 	return b
 }
 
+func (r *Runtime) createArrayBuffer(val *Object) objectImpl {
+	o := r.newNativeConstructOnly(val, r.builtin_newArrayBuffer, r.global.ArrayBufferPrototype, "ArrayBuffer", 1)
+	o._putProp("isView", r.newNativeFunc(r.arrayBuffer_isView, nil, "isView", nil, 1), true, false, true)
+	o._putSym(symSpecies, &valueProperty{
+		getterFunc:   r.newNativeFunc(r.returnThis, nil, "get [Symbol.species]", nil, 0),
+		accessor:     true,
+		configurable: true,
+	})
+	return o
+}
+
+func (r *Runtime) createDataViewProto(val *Object) objectImpl {
+	b := newBaseObjectObj(val, r.global.ObjectPrototype, classObject)
+	b._put("buffer", &valueProperty{
+		accessor:     true,
+		configurable: true,
+		getterFunc:   r.newNativeFunc(r.dataViewProto_getBuffer, nil, "get buffer", nil, 0),
+	})
+	b._put("byteLength", &valueProperty{
+		accessor:     true,
+		configurable: true,
+		getterFunc:   r.newNativeFunc(r.dataViewProto_getByteLen, nil, "get byteLength", nil, 0),
+	})
+	b._put("byteOffset", &valueProperty{
+		accessor:     true,
+		configurable: true,
+		getterFunc:   r.newNativeFunc(r.dataViewProto_getByteOffset, nil, "get byteOffset", nil, 0),
+	})
+	b._putProp("constructor", r.global.DataView, true, false, true)
+	b._putProp("getFloat32", r.newNativeFunc(r.dataViewProto_getFloat32, nil, "getFloat32", nil, 1), true, false, true)
+	b._putProp("getFloat64", r.newNativeFunc(r.dataViewProto_getFloat64, nil, "getFloat64", nil, 1), true, false, true)
+	b._putProp("getInt8", r.newNativeFunc(r.dataViewProto_getInt8, nil, "getInt8", nil, 1), true, false, true)
+	b._putProp("getInt16", r.newNativeFunc(r.dataViewProto_getInt16, nil, "getInt16", nil, 1), true, false, true)
+	b._putProp("getInt32", r.newNativeFunc(r.dataViewProto_getInt32, nil, "getInt32", nil, 1), true, false, true)
+	b._putProp("getUint8", r.newNativeFunc(r.dataViewProto_getUint8, nil, "getUint8", nil, 1), true, false, true)
+	b._putProp("getUint16", r.newNativeFunc(r.dataViewProto_getUint16, nil, "getUint16", nil, 1), true, false, true)
+	b._putProp("getUint32", r.newNativeFunc(r.dataViewProto_getUint32, nil, "getUint32", nil, 1), true, false, true)
+	b._putProp("setFloat32", r.newNativeFunc(r.dataViewProto_setFloat32, nil, "setFloat32", nil, 2), true, false, true)
+	b._putProp("setFloat64", r.newNativeFunc(r.dataViewProto_setFloat64, nil, "setFloat64", nil, 2), true, false, true)
+	b._putProp("setInt8", r.newNativeFunc(r.dataViewProto_setInt8, nil, "setInt8", nil, 2), true, false, true)
+	b._putProp("setInt16", r.newNativeFunc(r.dataViewProto_setInt16, nil, "setInt16", nil, 2), true, false, true)
+	b._putProp("setInt32", r.newNativeFunc(r.dataViewProto_setInt32, nil, "setInt32", nil, 2), true, false, true)
+	b._putProp("setUint8", r.newNativeFunc(r.dataViewProto_setUint8, nil, "setUint8", nil, 2), true, false, true)
+	b._putProp("setUint16", r.newNativeFunc(r.dataViewProto_setUint16, nil, "setUint16", nil, 2), true, false, true)
+	b._putProp("setUint32", r.newNativeFunc(r.dataViewProto_setUint32, nil, "setUint32", nil, 2), true, false, true)
+	b._putSym(symToStringTag, valueProp(asciiString("DataView"), false, false, true))
+
+	return b
+}
+
+func (r *Runtime) createDataView(val *Object) objectImpl {
+	o := r.newNativeConstructOnly(val, r.newDataView, r.global.DataViewPrototype, "DataView", 3)
+	return o
+}
+
+func (r *Runtime) createTypedArrayProto(val *Object) objectImpl {
+	b := newBaseObjectObj(val, r.global.ObjectPrototype, classObject)
+	b._put("buffer", &valueProperty{
+		accessor:     true,
+		configurable: true,
+		getterFunc:   r.newNativeFunc(r.typedArrayProto_getBuffer, nil, "get buffer", nil, 0),
+	})
+	b._put("byteLength", &valueProperty{
+		accessor:     true,
+		configurable: true,
+		getterFunc:   r.newNativeFunc(r.typedArrayProto_getByteLen, nil, "get byteLength", nil, 0),
+	})
+	b._put("byteOffset", &valueProperty{
+		accessor:     true,
+		configurable: true,
+		getterFunc:   r.newNativeFunc(r.typedArrayProto_getByteOffset, nil, "get byteOffset", nil, 0),
+	})
+	b._putProp("constructor", r.global.TypedArray, true, false, true)
+	b._putProp("copyWithin", r.newNativeFunc(r.typedArrayProto_copyWithin, nil, "copyWithin", nil, 2), true, false, true)
+	b._putProp("entries", r.newNativeFunc(r.typedArrayProto_entries, nil, "entries", nil, 0), true, false, true)
+	b._putProp("every", r.newNativeFunc(r.typedArrayProto_every, nil, "every", nil, 1), true, false, true)
+	b._putProp("fill", r.newNativeFunc(r.typedArrayProto_fill, nil, "fill", nil, 1), true, false, true)
+	b._putProp("filter", r.newNativeFunc(r.typedArrayProto_filter, nil, "filter", nil, 1), true, false, true)
+	b._putProp("find", r.newNativeFunc(r.typedArrayProto_find, nil, "find", nil, 1), true, false, true)
+	b._putProp("findIndex", r.newNativeFunc(r.typedArrayProto_findIndex, nil, "findIndex", nil, 1), true, false, true)
+	b._putProp("forEach", r.newNativeFunc(r.typedArrayProto_forEach, nil, "forEach", nil, 1), true, false, true)
+	b._putProp("includes", r.newNativeFunc(r.typedArrayProto_includes, nil, "includes", nil, 1), true, false, true)
+	b._putProp("indexOf", r.newNativeFunc(r.typedArrayProto_indexOf, nil, "indexOf", nil, 1), true, false, true)
+	b._putProp("join", r.newNativeFunc(r.typedArrayProto_join, nil, "join", nil, 1), true, false, true)
+	b._putProp("keys", r.newNativeFunc(r.typedArrayProto_keys, nil, "keys", nil, 0), true, false, true)
+	b._putProp("lastIndexOf", r.newNativeFunc(r.typedArrayProto_lastIndexOf, nil, "lastIndexOf", nil, 1), true, false, true)
+	b._put("length", &valueProperty{
+		accessor:     true,
+		configurable: true,
+		getterFunc:   r.newNativeFunc(r.typedArrayProto_getLength, nil, "get length", nil, 0),
+	})
+	b._putProp("map", r.newNativeFunc(r.typedArrayProto_map, nil, "map", nil, 1), true, false, true)
+	b._putProp("reduce", r.newNativeFunc(r.typedArrayProto_reduce, nil, "reduce", nil, 1), true, false, true)
+	b._putProp("reduceRight", r.newNativeFunc(r.typedArrayProto_reduceRight, nil, "reduceRight", nil, 1), true, false, true)
+	b._putProp("reverse", r.newNativeFunc(r.typedArrayProto_reverse, nil, "reverse", nil, 0), true, false, true)
+	b._putProp("set", r.newNativeFunc(r.typedArrayProto_set, nil, "set", nil, 1), true, false, true)
+	b._putProp("slice", r.newNativeFunc(r.typedArrayProto_slice, nil, "slice", nil, 2), true, false, true)
+	b._putProp("some", r.newNativeFunc(r.typedArrayProto_some, nil, "some", nil, 1), true, false, true)
+	b._putProp("sort", r.newNativeFunc(r.typedArrayProto_sort, nil, "sort", nil, 1), true, false, true)
+	b._putProp("subarray", r.newNativeFunc(r.typedArrayProto_subarray, nil, "subarray", nil, 2), true, false, true)
+	b._putProp("toLocaleString", r.newNativeFunc(r.typedArrayProto_toLocaleString, nil, "toLocaleString", nil, 0), true, false, true)
+	b._putProp("toString", r.global.arrayToString, true, false, true)
+	valuesFunc := r.newNativeFunc(r.typedArrayProto_values, nil, "values", nil, 0)
+	b._putProp("values", valuesFunc, true, false, true)
+	b._putSym(symIterator, valueProp(valuesFunc, true, false, true))
+	b._putSym(symToStringTag, &valueProperty{
+		getterFunc:   r.newNativeFunc(r.typedArrayProto_toStringTag, nil, "get [Symbol.toStringTag]", nil, 0),
+		accessor:     true,
+		configurable: true,
+	})
+
+	return b
+}
+
+func (r *Runtime) createTypedArray(val *Object) objectImpl {
+	o := r.newNativeConstructOnly(val, r.newTypedArray, r.global.TypedArrayPrototype, "TypedArray", 0)
+	o._putProp("from", r.newNativeFunc(r.typedArray_from, nil, "from", nil, 1), true, false, true)
+	o._putProp("of", r.newNativeFunc(r.typedArray_of, nil, "of", nil, 0), true, false, true)
+	o._putSym(symSpecies, &valueProperty{
+		getterFunc:   r.newNativeFunc(r.returnThis, nil, "get [Symbol.species]", nil, 0),
+		accessor:     true,
+		configurable: true,
+	})
+
+	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 string, bytesPerElement int) func(val *Object) objectImpl {
+	return func(val *Object) objectImpl {
+		o := r.newNativeConstructOnly(val, ctor, nil, name, 3)
+		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)
+		return o
+	}
+}
+
 func (r *Runtime) initTypedArrays() {
 
 	r.global.ArrayBufferPrototype = r.newLazyObject(r.createArrayBufferProto)
-
-	r.global.ArrayBuffer = r.newNativeFuncConstruct(r.builtin_ArrayBuffer, "ArrayBuffer", r.global.ArrayBufferPrototype, 1)
+	r.global.ArrayBuffer = r.newLazyObject(r.createArrayBuffer)
 	r.addToGlobal("ArrayBuffer", r.global.ArrayBuffer)
+
+	r.global.DataViewPrototype = r.newLazyObject(r.createDataViewProto)
+	r.global.DataView = r.newLazyObject(r.createDataView)
+	r.addToGlobal("DataView", r.global.DataView)
+
+	r.global.TypedArrayPrototype = r.newLazyObject(r.createTypedArrayProto)
+	r.global.TypedArray = r.newLazyObject(r.createTypedArray)
+
+	r.global.Uint8Array = r.newLazyObject(r.typedArrayCreator(r.newUint8Array, "Uint8Array", 1))
+	r.addToGlobal("Uint8Array", r.global.Uint8Array)
+
+	r.global.Uint8ClampedArray = r.newLazyObject(r.typedArrayCreator(r.newUint8ClampedArray, "Uint8ClampedArray", 1))
+	r.addToGlobal("Uint8ClampedArray", r.global.Uint8ClampedArray)
+
+	r.global.Int8Array = r.newLazyObject(r.typedArrayCreator(r.newInt8Array, "Int8Array", 1))
+	r.addToGlobal("Int8Array", r.global.Int8Array)
+
+	r.global.Uint16Array = r.newLazyObject(r.typedArrayCreator(r.newUint16Array, "Uint16Array", 2))
+	r.addToGlobal("Uint16Array", r.global.Uint16Array)
+
+	r.global.Int16Array = r.newLazyObject(r.typedArrayCreator(r.newInt16Array, "Int16Array", 2))
+	r.addToGlobal("Int16Array", r.global.Int16Array)
+
+	r.global.Uint32Array = r.newLazyObject(r.typedArrayCreator(r.newUint32Array, "Uint32Array", 4))
+	r.addToGlobal("Uint32Array", r.global.Uint32Array)
+
+	r.global.Int32Array = r.newLazyObject(r.typedArrayCreator(r.newInt32Array, "Int32Array", 4))
+	r.addToGlobal("Int32Array", r.global.Int32Array)
+
+	r.global.Float32Array = r.newLazyObject(r.typedArrayCreator(r.newFloat32Array, "Float32Array", 4))
+	r.addToGlobal("Float32Array", r.global.Float32Array)
+
+	r.global.Float64Array = r.newLazyObject(r.typedArrayCreator(r.newFloat64Array, "Float64Array", 8))
+	r.addToGlobal("Float64Array", r.global.Float64Array)
 }

+ 254 - 0
builtin_typedarrays_test.go

@@ -1,5 +1,9 @@
 package goja
 
+import (
+	"testing"
+)
+
 /*
 func TestArrayBufferNew(t *testing.T) {
 	const SCRIPT = `
@@ -10,3 +14,253 @@ func TestArrayBufferNew(t *testing.T) {
 	testScript1(SCRIPT, intToValue(16), t)
 }
 */
+
+func TestArrayBufferSetUint32(t *testing.T) {
+	vm := New()
+	b := vm._newArrayBuffer(vm.global.ArrayBufferPrototype, nil)
+	b.data = make([]byte, 4)
+	b.setUint32(0, 0xCAFEBABE, bigEndian)
+
+	i := b.getUint32(0, bigEndian)
+	if i != 0xCAFEBABE {
+		t.Fatal(i)
+	}
+	i = b.getUint32(0, littleEndian)
+	if i != 0xBEBAFECA {
+		t.Fatal(i)
+	}
+
+	b.setUint32(0, 0xBEBAFECA, littleEndian)
+	i = b.getUint32(0, bigEndian)
+	if i != 0xCAFEBABE {
+		t.Fatal(i)
+	}
+}
+
+func TestArrayBufferSetInt32(t *testing.T) {
+	vm := New()
+	b := vm._newArrayBuffer(vm.global.ArrayBufferPrototype, nil)
+	b.data = make([]byte, 4)
+	b.setInt32(0, -42, littleEndian)
+	if v := b.getInt32(0, littleEndian); v != -42 {
+		t.Fatal(v)
+	}
+
+	b.setInt32(0, -42, bigEndian)
+	if v := b.getInt32(0, bigEndian); v != -42 {
+		t.Fatal(v)
+	}
+}
+
+func TestNewUint8Array(t *testing.T) {
+	const SCRIPT = `
+	var a = new Uint8Array(1);
+	a[0] = 42;
+	a.byteLength === 1 && a.length === 1 && a[0] === 42;
+	`
+
+	testScript1(SCRIPT, valueTrue, t)
+}
+
+func TestNewUint16Array(t *testing.T) {
+	const SCRIPT = `
+	var a = new Uint16Array(1);
+	a[0] = 42;
+	a.byteLength === 2 && a.length === 1 && a[0] === 42;
+	`
+
+	testScript1(SCRIPT, valueTrue, t)
+}
+
+func TestTypedArraysSpeciesConstructor(t *testing.T) {
+	const SCRIPT = `
+    'use strict';
+    function MyArray() {
+        var NewTarget = this.__proto__.constructor;
+        return Reflect.construct(Uint16Array, arguments, NewTarget);
+    }
+    MyArray.prototype = Object.create(Uint16Array.prototype, {
+        constructor: {
+            value: MyArray,
+            writable: true,
+            configurable: true
+        }
+    });
+    var a = new MyArray(1);
+    Object.defineProperty(MyArray, Symbol.species, {value: Uint8Array, configurable: true});
+    a[0] = 32767;
+    var b = a.filter(function() {
+        return true;
+    });
+	if (a[0] !== 32767) {
+		throw new Error("a[0]=" + a[0]); 
+	}
+	if (!(b instanceof Uint8Array)) {
+		throw new Error("b instanceof Uint8Array");
+	}
+	if (b[0] != 255) {
+		throw new Error("b[0]=" + b[0]);
+	}
+	`
+
+	testScript1(SCRIPT, _undefined, t)
+}
+
+func TestTypedArrayFromArrayBuffer(t *testing.T) {
+	const SCRIPT = `
+	var buf = new ArrayBuffer(2);
+	var a16 = new Uint16Array(buf);
+	if (!(a16 instanceof Uint16Array)) {
+		throw new Error("a16 is not an instance");
+	}
+	if (a16.buffer !== buf) {
+		throw new Error("a16.buffer !== buf");
+	}
+	if (a16.length !== 1) {
+		throw new Error("a16.length=" + a16.length);
+	}
+	var a8 = new Uint8Array(buf);
+	a8.fill(0xAA);
+	if (a16[0] !== 0xAAAA) {
+		throw new Error("a16[0]=" + a16[0]);
+	}
+	`
+
+	testScript1(SCRIPT, _undefined, t)
+}
+
+func TestTypedArraySetOverlapDifSize(t *testing.T) {
+	const SCRIPT = `
+	var buf = new ArrayBuffer(4);
+	var src = new Uint8Array(buf, 1, 2);
+	src[0] = 1;
+	src[1] = 2;
+	var dst = new Uint16Array(buf);
+	dst.set(src);
+	if (dst[0] !== 1 || dst[1] !== 2) {
+		throw new Error("dst: " + dst.join(","));
+	}	
+	`
+	testScript1(SCRIPT, _undefined, t)
+}
+
+func TestTypedArraySetOverlapDifSize2(t *testing.T) {
+	const SCRIPT = `
+	var buf = new ArrayBuffer(4);
+	var src = new Uint8Array(buf, 0, 2);
+	src[0] = 1;
+	src[1] = 2;
+	var dst = new Uint16Array(buf);
+	dst.set(src);
+	if (dst[0] !== 1 || dst[1] !== 2) {
+		throw new Error("dst: " + dst.join(","));
+	}	
+	`
+	testScript1(SCRIPT, _undefined, t)
+}
+
+func TestTypedArraySetOverlapDifSize3(t *testing.T) {
+	const SCRIPT = `
+	var buf = new ArrayBuffer(8);
+	var src = new Uint8Array(buf, 2, 4);
+	src[0] = 1;
+	src[1] = 2;
+	src[2] = 3;
+	src[3] = 4;
+	var dst = new Uint16Array(buf);
+	dst.set(src);
+	if (dst[0] !== 1 || dst[1] !== 2 || dst[2] !== 3 || dst[3] !== 4) {
+		throw new Error("dst: " + dst.join(","));
+	}	
+	`
+	testScript1(SCRIPT, _undefined, t)
+}
+
+func TestTypedArraySetOverlapDifSize4(t *testing.T) {
+	const SCRIPT = `
+	var buf = new ArrayBuffer(10);
+	var dst = new Uint8Array(buf, 2, 5);
+	var src = new Uint16Array(buf);
+	src[0] = 1;
+	src[1] = 2;
+	src[2] = 3;
+	src[3] = 4;
+	src[4] = 5;
+	dst.set(src);
+	if (dst[0] !== 1 || dst[1] !== 2 || dst[2] !== 3 || dst[3] !== 4 || dst[4] !== 5) {
+		throw new Error("dst: " + dst.join(","));
+	}	
+	`
+	testScript1(SCRIPT, _undefined, t)
+}
+
+func TestTypedArraySetNoOverlapDifSizeForward(t *testing.T) {
+	const SCRIPT = `
+	var buf = new ArrayBuffer(10);
+	var dst = new Uint8Array(buf, 7, 2);
+	var src = new Uint16Array(buf, 0, 2);
+	src[0] = 1;
+	src[1] = 2;
+	dst.set(src);
+	if (dst[0] !== 1 || dst[1] !== 2 || src[0] !== 1 || src[1] !== 2) {
+		throw new Error("dst: " + dst.join(","));
+	}	
+	`
+	testScript1(SCRIPT, _undefined, t)
+}
+
+func TestTypedArraySetNoOverlapDifSizeBackward(t *testing.T) {
+	const SCRIPT = `
+	var buf = new ArrayBuffer(10);
+	var dst = new Uint8Array(buf, 0, 2);
+	var src = new Uint16Array(buf, 6, 2);
+	src[0] = 1;
+	src[1] = 2;
+	dst.set(src);
+	if (dst[0] !== 1 || dst[1] !== 2 || src[0] !== 1 || src[1] !== 2) {
+		throw new Error("dst: " + dst.join(","));
+	}	
+	`
+	testScript1(SCRIPT, _undefined, t)
+}
+
+func TestTypedArraySetNoOverlapDifSizeDifBuffers(t *testing.T) {
+	const SCRIPT = `
+	var dstBuf = new ArrayBuffer(1024);
+	var dst = new Uint8Array(dstBuf, 0, 2);
+	var src = new Uint16Array(2);
+	src[0] = 1;
+	src[1] = 2;
+	dst.set(src);
+	if (dst[0] !== 1 || dst[1] !== 2 || src[0] !== 1 || src[1] !== 2) {
+		throw new Error("dst: " + dst.join(","));
+	}	
+	`
+	testScript1(SCRIPT, _undefined, t)
+}
+
+func TestTypedArraySliceSameType(t *testing.T) {
+	const SCRIPT = `
+	var src = Uint8Array.of(1,2,3,4);
+	var dst = src.slice(1, 3);
+	if (dst.length !== 2 || dst[0] !== 2 || dst[1] !== 3) {
+		throw new Error("dst: " + dst.join(","));
+	}	
+	`
+	testScript1(SCRIPT, _undefined, t)
+}
+
+func TestTypedArraySliceDifType(t *testing.T) {
+	const SCRIPT = `
+	var src = Uint8Array.of(1,2,3,4);
+	Object.defineProperty(Uint8Array, Symbol.species, {value: Uint16Array, configurable: true});
+	var dst = src.slice(1, 3);
+	if (!(dst instanceof Uint16Array)) {
+		throw new Error("wrong dst type: " + dst);
+	}
+	if (dst.length !== 2 || dst[0] !== 2 || dst[1] !== 3) {
+		throw new Error("dst: " + dst.join(","));
+	}	
+	`
+	testScript1(SCRIPT, _undefined, t)
+}

+ 21 - 2
builtin_weakmap.go

@@ -133,7 +133,26 @@ func (r *Runtime) weakMapProto_set(call FunctionCall) Value {
 	return call.This
 }
 
-func (r *Runtime) builtin_newWeakMap(args []Value, proto *Object) *Object {
+func (r *Runtime) needNew(name string) *Object {
+	return r.NewTypeError("Constructor %s requires 'new'", name)
+}
+
+func (r *Runtime) getPrototypeFromCtor(newTarget, defCtor, defProto *Object) *Object {
+	if newTarget == defCtor {
+		return defProto
+	}
+	proto := newTarget.self.getStr("prototype", nil)
+	if obj, ok := proto.(*Object); ok {
+		return obj
+	}
+	return defProto
+}
+
+func (r *Runtime) builtin_newWeakMap(args []Value, newTarget *Object) *Object {
+	if newTarget == nil {
+		panic(r.needNew("WeakMap"))
+	}
+	proto := r.getPrototypeFromCtor(newTarget, r.global.WeakMap, r.global.WeakMapPrototype)
 	o := &Object{runtime: r}
 
 	wmo := &weakMapObject{}
@@ -189,7 +208,7 @@ func (r *Runtime) createWeakMapProto(val *Object) objectImpl {
 }
 
 func (r *Runtime) createWeakMap(val *Object) objectImpl {
-	o := r.newNativeFuncObj(val, r.constructorThrower("WeakMap"), r.builtin_newWeakMap, "WeakMap", r.global.WeakMapPrototype, 0)
+	o := r.newNativeConstructOnly(val, r.builtin_newWeakMap, r.global.WeakMapPrototype, "WeakMap", 0)
 
 	return o
 }

+ 1 - 0
builtin_weakmap_test.go

@@ -23,6 +23,7 @@ func TestWeakMapExpiry(t *testing.T) {
 		t.Fatal(err)
 	}
 	runtime.GC()
+	runtime.GC()
 	wmo := vm.Get("m").ToObject(vm).self.(*weakMapObject)
 	wmo.m.Lock()
 	l := len(wmo.m.data)

+ 6 - 2
builtin_weakset.go

@@ -113,7 +113,11 @@ func (r *Runtime) populateWeakSetGeneric(s *Object, adderValue Value, iterable V
 	})
 }
 
-func (r *Runtime) builtin_newWeakSet(args []Value, proto *Object) *Object {
+func (r *Runtime) builtin_newWeakSet(args []Value, newTarget *Object) *Object {
+	if newTarget == nil {
+		panic(r.needNew("WeakSet"))
+	}
+	proto := r.getPrototypeFromCtor(newTarget, r.global.WeakSet, r.global.WeakSetPrototype)
 	o := &Object{runtime: r}
 
 	wso := &weakSetObject{}
@@ -155,7 +159,7 @@ func (r *Runtime) createWeakSetProto(val *Object) objectImpl {
 }
 
 func (r *Runtime) createWeakSet(val *Object) objectImpl {
-	o := r.newNativeFuncObj(val, r.constructorThrower("WeakSet"), r.builtin_newWeakSet, "WeakSet", r.global.WeakSetPrototype, 0)
+	o := r.newNativeConstructOnly(val, r.builtin_newWeakSet, r.global.WeakSetPrototype, "WeakSet", 0)
 
 	return o
 }

+ 1 - 0
builtin_weakset_test.go

@@ -36,6 +36,7 @@ func TestWeakSetExpiry(t *testing.T) {
 		t.Fatal(err)
 	}
 	runtime.GC()
+	runtime.GC()
 	wso := vm.Get("s").ToObject(vm).self.(*weakSetObject)
 	wso.s.Lock()
 	l := len(wso.s.data)

+ 13 - 0
date_test.go

@@ -79,6 +79,19 @@ assert.throws = function (expectedErrorConstructor, func, message) {
   $ERROR(message);
 };
 
+function compareArray(a, b) {
+  if (b.length !== a.length) {
+    return false;
+  }
+
+  for (var i = 0; i < a.length; i++) {
+    if (b[i] !== a[i]) {
+      return false;
+    }
+  }
+  return true;
+}
+
 `
 
 func TestDateUTC(t *testing.T) {

+ 0 - 26
object.go

@@ -461,18 +461,6 @@ func (o *baseObject) setProto(proto *Object, throw bool) bool {
 	return true
 }
 
-func (o *baseObject) _setProto(val Value) {
-	var proto *Object
-	if val != _null {
-		if obj, ok := val.(*Object); ok {
-			proto = obj
-		} else {
-			return
-		}
-	}
-	o.setProto(proto, true)
-}
-
 func (o *baseObject) setOwnStr(name string, val Value, throw bool) bool {
 	ownDesc := o.values[name]
 	if ownDesc == nil {
@@ -1148,8 +1136,6 @@ func instanceOfOperator(o Value, c *Object) bool {
 
 func (o *Object) get(p Value, receiver Value) Value {
 	switch p := p.(type) {
-	case valueString:
-		return o.self.getStr(p.String(), receiver)
 	case valueInt:
 		return o.self.getIdx(p, receiver)
 	case *valueSymbol:
@@ -1161,8 +1147,6 @@ func (o *Object) get(p Value, receiver Value) Value {
 
 func (o *Object) getOwnProp(p Value) Value {
 	switch p := p.(type) {
-	case valueString:
-		return o.self.getOwnPropStr(p.String())
 	case valueInt:
 		return o.self.getOwnPropIdx(p)
 	case *valueSymbol:
@@ -1174,8 +1158,6 @@ func (o *Object) getOwnProp(p Value) Value {
 
 func (o *Object) hasOwnProperty(p Value) bool {
 	switch p := p.(type) {
-	case valueString:
-		return o.self.hasOwnPropertyStr(p.String())
 	case valueInt:
 		return o.self.hasOwnPropertyIdx(p)
 	case *valueSymbol:
@@ -1187,8 +1169,6 @@ func (o *Object) hasOwnProperty(p Value) bool {
 
 func (o *Object) hasProperty(p Value) bool {
 	switch p := p.(type) {
-	case valueString:
-		return o.self.hasPropertyStr(p.String())
 	case valueInt:
 		return o.self.hasPropertyIdx(p)
 	case *valueSymbol:
@@ -1237,8 +1217,6 @@ func (o *Object) setStr(name string, val, receiver Value, throw bool) bool {
 
 func (o *Object) set(name Value, val, receiver Value, throw bool) bool {
 	switch name := name.(type) {
-	case valueString:
-		return o.setStr(name.String(), val, receiver, throw)
 	case valueInt:
 		return o.setIdx(name, val, receiver, throw)
 	case *valueSymbol:
@@ -1335,8 +1313,6 @@ func (o *Object) setSym(name *valueSymbol, val, receiver Value, throw bool) bool
 
 func (o *Object) delete(n Value, throw bool) bool {
 	switch n := n.(type) {
-	case valueString:
-		return o.self.deleteStr(n.String(), throw)
 	case valueInt:
 		return o.self.deleteIdx(n, throw)
 	case *valueSymbol:
@@ -1348,8 +1324,6 @@ func (o *Object) delete(n Value, throw bool) bool {
 
 func (o *Object) defineOwnProperty(n Value, desc PropertyDescriptor, throw bool) bool {
 	switch n := n.(type) {
-	case valueString:
-		return o.self.defineOwnPropertyStr(n.String(), desc, throw)
 	case valueInt:
 		return o.self.defineOwnPropertyIdx(n, desc, throw)
 	case *valueSymbol:

+ 10 - 6
parser/statement.go

@@ -438,9 +438,11 @@ func (self *_parser) parseForOrForInStatement() ast.Statement {
 				if self.token == token.IN {
 					self.next() // in
 					forIn = true
-				} else if self.token == token.OF {
-					self.next() // of
-					forOf = true
+				} else if self.token == token.IDENTIFIER {
+					if self.literal == "of" {
+						self.next()
+						forOf = true
+					}
 				}
 			}
 			left = list
@@ -449,9 +451,11 @@ func (self *_parser) parseForOrForInStatement() ast.Statement {
 			if self.token == token.IN {
 				self.next()
 				forIn = true
-			} else if self.token == token.OF {
-				self.next()
-				forOf = true
+			} else if self.token == token.IDENTIFIER {
+				if self.literal == "of" {
+					self.next()
+					forOf = true
+				}
 			}
 		}
 		self.scope.allowIn = allowIn

+ 36 - 6
proxy.go

@@ -1,9 +1,18 @@
 package goja
 
+import "reflect"
+
+// Proxy is a Go wrapper around ECMAScript Proxy. Calling Runtime.ToValue() on it
+// returns the underlying Proxy. Calling Export() on an ECMAScript Proxy returns a wrapper.
+// Use Runtime.NewProxy() to create one.
 type Proxy struct {
 	proxy *proxyObject
 }
 
+var (
+	proxyType = reflect.TypeOf(Proxy{})
+)
+
 type proxyPropIter struct {
 	p     *proxyObject
 	names []Value
@@ -24,7 +33,7 @@ func (i *proxyPropIter) next() (propIterItem, iterNextFunc) {
 	return propIterItem{}, nil
 }
 
-func (r *Runtime) newProxyObject(target *Object, handler *Object, proto *Object) *proxyObject {
+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"))
@@ -58,10 +67,21 @@ func (r *Runtime) newProxyObject(target *Object, handler *Object, proto *Object)
 	return p
 }
 
-func (p *Proxy) Revoke() {
+func (p Proxy) Revoke() {
 	p.proxy.revoke()
 }
 
+func (p Proxy) toValue(r *Runtime) Value {
+	if p.proxy == nil {
+		return _null
+	}
+	proxy := p.proxy.val
+	if proxy.runtime != r {
+		panic(r.NewTypeError("Illegal runtime transition of a Proxy"))
+	}
+	return proxy
+}
+
 type proxyTrap string
 
 const (
@@ -638,11 +658,11 @@ func (p *proxyObject) __isCompatibleDescriptor(extensible bool, desc *PropertyDe
 		}
 
 		if p.__isDataDescriptor(desc) && !current.accessor {
-			if desc.Configurable == FLAG_FALSE {
-				if desc.Writable == FLAG_FALSE && current.writable {
+			if !current.configurable {
+				if desc.Writable == FLAG_TRUE && !current.writable {
 					return false
 				}
-				if desc.Writable == FLAG_FALSE {
+				if !current.writable {
 					if desc.Value != nil && !desc.Value.SameAs(current.value) {
 						return false
 					}
@@ -651,7 +671,7 @@ func (p *proxyObject) __isCompatibleDescriptor(extensible bool, desc *PropertyDe
 			return true
 		}
 		if p.__isAccessorDescriptor(desc) && current.accessor {
-			if desc.Configurable == FLAG_FALSE {
+			if !current.configurable {
 				if desc.Setter != nil && desc.Setter.SameAs(current.setterFunc) {
 					return false
 				}
@@ -758,6 +778,16 @@ func (p *proxyObject) className() string {
 	return classObject
 }
 
+func (p *proxyObject) exportType() reflect.Type {
+	return proxyType
+}
+
+func (p *proxyObject) export() interface{} {
+	return Proxy{
+		proxy: p,
+	}
+}
+
 func (p *proxyObject) revoke() {
 	p.handler = nil
 	p.target = nil

+ 188 - 39
runtime.go

@@ -7,6 +7,7 @@ import (
 	"go/ast"
 	"hash/maphash"
 	"math"
+	"math/bits"
 	"math/rand"
 	"reflect"
 	"strconv"
@@ -50,11 +51,23 @@ type global struct {
 	Symbol   *Object
 	Proxy    *Object
 
-	ArrayBuffer *Object
-	WeakSet     *Object
-	WeakMap     *Object
-	Map         *Object
-	Set         *Object
+	ArrayBuffer       *Object
+	DataView          *Object
+	TypedArray        *Object
+	Uint8Array        *Object
+	Uint8ClampedArray *Object
+	Int8Array         *Object
+	Uint16Array       *Object
+	Int16Array        *Object
+	Uint32Array       *Object
+	Int32Array        *Object
+	Float32Array      *Object
+	Float64Array      *Object
+
+	WeakSet *Object
+	WeakMap *Object
+	Map     *Object
+	Set     *Object
 
 	Error          *Object
 	TypeError      *Object
@@ -78,6 +91,8 @@ type global struct {
 	ArrayIterator     *Object
 
 	ArrayBufferPrototype *Object
+	DataViewPrototype    *Object
+	TypedArrayPrototype  *Object
 	WeakSetPrototype     *Object
 	WeakMapPrototype     *Object
 	MapPrototype         *Object
@@ -109,6 +124,7 @@ type global struct {
 	mapAdder        *Object
 	setAdder        *Object
 	arrayValues     *Object
+	arrayToString   *Object
 }
 
 type Flag int
@@ -340,7 +356,7 @@ func (r *Runtime) init() {
 	r.initMath()
 	r.initJSON()
 
-	//r.initTypedArrays()
+	r.initTypedArrays()
 	r.initSymbol()
 	r.initWeakSet()
 	r.initWeakMap()
@@ -485,6 +501,39 @@ 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 string, length int) *nativeFuncObject {
+	if v == nil {
+		v = &Object{runtime: r}
+	}
+
+	f := &nativeFuncObject{
+		baseFuncObject: baseFuncObject{
+			baseObject: baseObject{
+				class:      classFunction,
+				val:        v,
+				extensible: true,
+				prototype:  r.global.FunctionPrototype,
+			},
+		},
+		f: func(call FunctionCall) Value {
+			return ctor(call.Arguments, nil)
+		},
+		construct: func(args []Value, newTarget *Object) *Object {
+			if newTarget == nil {
+				newTarget = v
+			}
+			return ctor(args, newTarget)
+		},
+	}
+	v.self = f
+	f.init(name, length)
+	if defaultProto != nil {
+		f._putProp("prototype", defaultProto, false, false, false)
+	}
+
+	return f
+}
+
 func (r *Runtime) newNativeFunc(call func(FunctionCall) Value, construct func(args []Value, proto *Object) *Object, name string, proto *Object, length int) *Object {
 	v := &Object{runtime: r}
 
@@ -707,8 +756,11 @@ func (r *Runtime) wrapNativeConstruct(c func(args []Value, proto *Object) *Objec
 	return func(args []Value, newTarget *Object) *Object {
 		var p *Object
 		if newTarget != nil {
-			p = r.toObject(newTarget.self.getStr("prototype", nil))
-		} else {
+			if pp, ok := newTarget.self.getStr("prototype", nil).(*Object); ok {
+				p = pp
+			}
+		}
+		if p == nil {
 			p = proto
 		}
 		return c(args, p)
@@ -730,48 +782,103 @@ func (r *Runtime) checkObjectCoercible(v Value) {
 	}
 }
 
-func toUInt32(v Value) uint32 {
+func toInt8(v Value) int8 {
 	v = v.ToNumber()
 	if i, ok := v.(valueInt); ok {
-		return uint32(i)
+		return int8(i)
 	}
 
 	if f, ok := v.(valueFloat); ok {
 		f := float64(f)
 		if !math.IsNaN(f) && !math.IsInf(f, 0) {
-			return uint32(int64(f))
+			return int8(int64(f))
 		}
 	}
 	return 0
 }
 
-func toUInt16(v Value) uint16 {
+func toUint8(v Value) uint8 {
 	v = v.ToNumber()
 	if i, ok := v.(valueInt); ok {
-		return uint16(i)
+		return uint8(i)
 	}
 
 	if f, ok := v.(valueFloat); ok {
 		f := float64(f)
 		if !math.IsNaN(f) && !math.IsInf(f, 0) {
-			return uint16(int64(f))
+			return uint8(int64(f))
 		}
 	}
 	return 0
 }
 
-func toLength(v Value) int64 {
-	if v == nil {
-		return 0
+func toUint8Clamp(v Value) uint8 {
+	v = v.ToNumber()
+	if i, ok := v.(valueInt); ok {
+		if i < 0 {
+			return 0
+		}
+		if i <= 255 {
+			return uint8(i)
+		}
+		return 255
 	}
-	i := v.ToInteger()
-	if i < 0 {
-		return 0
+
+	if num, ok := v.(valueFloat); ok {
+		num := float64(num)
+		if !math.IsNaN(num) {
+			if num < 0 {
+				return 0
+			}
+			if num > 255 {
+				return 255
+			}
+			f := math.Floor(num)
+			f1 := f + 0.5
+			if f1 < num {
+				return uint8(f + 1)
+			}
+			if f1 > num {
+				return uint8(f)
+			}
+			r := uint8(f)
+			if r&1 != 0 {
+				return r + 1
+			}
+			return r
+		}
 	}
-	if i >= maxInt {
-		return maxInt - 1
+	return 0
+}
+
+func toInt16(v Value) int16 {
+	v = v.ToNumber()
+	if i, ok := v.(valueInt); ok {
+		return int16(i)
 	}
-	return i
+
+	if f, ok := v.(valueFloat); ok {
+		f := float64(f)
+		if !math.IsNaN(f) && !math.IsInf(f, 0) {
+			return int16(int64(f))
+		}
+	}
+	return 0
+}
+
+func toUint16(v Value) uint16 {
+	v = v.ToNumber()
+	if i, ok := v.(valueInt); ok {
+		return uint16(i)
+	}
+
+	if f, ok := v.(valueFloat); ok {
+		f := float64(f)
+		if !math.IsNaN(f) && !math.IsInf(f, 0) {
+			return uint16(int64(f))
+		}
+	}
+	return 0
 }
 
 func toInt32(v Value) int32 {
@@ -789,6 +896,50 @@ func toInt32(v Value) int32 {
 	return 0
 }
 
+func toUint32(v Value) uint32 {
+	v = v.ToNumber()
+	if i, ok := v.(valueInt); ok {
+		return uint32(i)
+	}
+
+	if f, ok := v.(valueFloat); ok {
+		f := float64(f)
+		if !math.IsNaN(f) && !math.IsInf(f, 0) {
+			return uint32(int64(f))
+		}
+	}
+	return 0
+}
+
+func toFloat32(v Value) float32 {
+	return float32(v.ToFloat())
+}
+
+func toLength(v Value) int64 {
+	if v == nil {
+		return 0
+	}
+	i := v.ToInteger()
+	if i < 0 {
+		return 0
+	}
+	if i >= maxInt {
+		return maxInt - 1
+	}
+	return i
+}
+
+func (r *Runtime) toIndex(v Value) int {
+	intIdx := v.ToInteger()
+	if intIdx >= 0 && intIdx < maxInt {
+		if bits.UintSize == 32 && intIdx >= math.MaxInt32 {
+			panic(r.newError(r.global.RangeError, "Index %s overflows int", v.String()))
+		}
+		return int(intIdx)
+	}
+	panic(r.newError(r.global.RangeError, "Invalid index %s", v.String()))
+}
+
 func (r *Runtime) toBoolean(b bool) Value {
 	if b {
 		return valueTrue
@@ -1014,6 +1165,8 @@ func (r *Runtime) ToValue(i interface{}) Value {
 			panic(r.NewTypeError("Illegal runtime transition of an Object"))
 		}
 		return i
+	case valueContainer:
+		return i.toValue(r)
 	case Value:
 		return i
 	case string:
@@ -1030,15 +1183,6 @@ func (r *Runtime) ToValue(i interface{}) Value {
 	case func(ConstructorCall) *Object:
 		name := runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
 		return r.newNativeConstructor(i, name, 0)
-	case *Proxy:
-		if i == nil {
-			return _null
-		}
-		proxy := i.proxy.val
-		if proxy.runtime != r {
-			panic(r.NewTypeError("Illegal runtime transition of a Proxy"))
-		}
-		return proxy
 	case int:
 		return intToValue(int64(i))
 	case int8:
@@ -1668,12 +1812,23 @@ func (r *Runtime) speciesConstructor(o, defaultConstructor *Object) func(args []
 	if c != nil && c != _undefined {
 		c = r.toObject(c).self.getSym(symSpecies, nil)
 	}
-	if c == nil || c == _undefined {
+	if c == nil || c == _undefined || c == _null {
 		c = defaultConstructor
 	}
 	return r.toConstructor(c)
 }
 
+func (r *Runtime) speciesConstructorObj(o, defaultConstructor *Object) *Object {
+	c := o.self.getStr("constructor", nil)
+	if c != nil && c != _undefined {
+		c = r.toObject(c).self.getSym(symSpecies, nil)
+	}
+	if c == nil || c == _undefined || c == _null {
+		return defaultConstructor
+	}
+	return r.toObject(c)
+}
+
 func (r *Runtime) returnThis(call FunctionCall) Value {
 	return call.This
 }
@@ -1756,12 +1911,6 @@ func (r *Runtime) newLazyObject(create func(*Object) objectImpl) *Object {
 	return val
 }
 
-func (r *Runtime) constructorThrower(name string) func(call FunctionCall) Value {
-	return func(FunctionCall) Value {
-		panic(r.NewTypeError("Constructor %s requires 'new'", name))
-	}
-}
-
 func nilSafe(v Value) Value {
 	if v != nil {
 		return v

+ 70 - 70
tc39_test.go

@@ -18,6 +18,16 @@ const (
 
 var (
 	invalidFormatError = errors.New("Invalid file format")
+
+	ignorableTestError = &valueSymbol{}
+
+	sabStub = MustCompile("sabStub.js", `
+		Object.defineProperty(this, "SharedArrayBuffer", {
+			get: function() {
+				throw IgnorableTestError;
+			}
+		});`,
+		false)
 )
 
 var (
@@ -34,64 +44,6 @@ var (
 		// utf-16
 		"test/built-ins/Array/prototype/concat/Array.prototype.concat_spreadable-string-wrapper.js": true,
 
-		// cross-realm
-		"test/built-ins/Symbol/unscopables/cross-realm.js":                                                          true,
-		"test/built-ins/Symbol/toStringTag/cross-realm.js":                                                          true,
-		"test/built-ins/Symbol/toPrimitive/cross-realm.js":                                                          true,
-		"test/built-ins/Symbol/split/cross-realm.js":                                                                true,
-		"test/built-ins/Symbol/species/cross-realm.js":                                                              true,
-		"test/built-ins/Symbol/search/cross-realm.js":                                                               true,
-		"test/built-ins/Symbol/replace/cross-realm.js":                                                              true,
-		"test/built-ins/Symbol/match/cross-realm.js":                                                                true,
-		"test/built-ins/Symbol/keyFor/cross-realm.js":                                                               true,
-		"test/built-ins/Symbol/iterator/cross-realm.js":                                                             true,
-		"test/built-ins/Symbol/isConcatSpreadable/cross-realm.js":                                                   true,
-		"test/built-ins/Symbol/hasInstance/cross-realm.js":                                                          true,
-		"test/built-ins/Symbol/for/cross-realm.js":                                                                  true,
-		"test/built-ins/WeakSet/proto-from-ctor-realm.js":                                                           true,
-		"test/built-ins/WeakMap/proto-from-ctor-realm.js":                                                           true,
-		"test/built-ins/Map/proto-from-ctor-realm.js":                                                               true,
-		"test/built-ins/Set/proto-from-ctor-realm.js":                                                               true,
-		"test/built-ins/Object/proto-from-ctor.js":                                                                  true,
-		"test/built-ins/Array/from/proto-from-ctor-realm.js":                                                        true,
-		"test/built-ins/Array/of/proto-from-ctor-realm.js":                                                          true,
-		"test/built-ins/Array/prototype/concat/create-proto-from-ctor-realm-non-array.js":                           true,
-		"test/built-ins/Array/prototype/concat/create-proto-from-ctor-realm-array.js":                               true,
-		"test/built-ins/Array/prototype/filter/create-proto-from-ctor-realm-non-array.js":                           true,
-		"test/built-ins/Array/prototype/filter/create-proto-from-ctor-realm-array.js":                               true,
-		"test/built-ins/Array/prototype/map/create-proto-from-ctor-realm-non-array.js":                              true,
-		"test/built-ins/Array/prototype/map/create-proto-from-ctor-realm-array.js":                                  true,
-		"test/built-ins/Array/prototype/slice/create-proto-from-ctor-realm-non-array.js":                            true,
-		"test/built-ins/Array/prototype/slice/create-proto-from-ctor-realm-array.js":                                true,
-		"test/built-ins/Array/prototype/splice/create-proto-from-ctor-realm-non-array.js":                           true,
-		"test/built-ins/Array/prototype/splice/create-proto-from-ctor-realm-array.js":                               true,
-		"test/built-ins/Proxy/construct/arguments-realm.js":                                                         true,
-		"test/built-ins/Proxy/setPrototypeOf/trap-is-not-callable-realm.js":                                         true,
-		"test/built-ins/Proxy/getPrototypeOf/trap-is-not-callable-realm.js":                                         true,
-		"test/built-ins/Proxy/set/trap-is-not-callable-realm.js":                                                    true,
-		"test/built-ins/Proxy/getOwnPropertyDescriptor/trap-is-not-callable-realm.js":                               true,
-		"test/built-ins/Proxy/getOwnPropertyDescriptor/result-type-is-not-object-nor-undefined-realm.js":            true,
-		"test/built-ins/Proxy/get/trap-is-not-callable-realm.js":                                                    true,
-		"test/built-ins/Proxy/preventExtensions/trap-is-not-callable-realm.js":                                      true,
-		"test/built-ins/Proxy/defineProperty/null-handler-realm.js":                                                 true,
-		"test/built-ins/Proxy/ownKeys/trap-is-not-callable-realm.js":                                                true,
-		"test/built-ins/Proxy/ownKeys/return-not-list-object-throws-realm.js":                                       true,
-		"test/built-ins/Proxy/deleteProperty/trap-is-not-callable-realm.js":                                         true,
-		"test/built-ins/Proxy/isExtensible/trap-is-not-callable-realm.js":                                           true,
-		"test/built-ins/Proxy/defineProperty/trap-is-not-callable-realm.js":                                         true,
-		"test/built-ins/Proxy/defineProperty/targetdesc-undefined-target-is-not-extensible-realm.js":                true,
-		"test/built-ins/Proxy/defineProperty/targetdesc-undefined-not-configurable-descriptor-realm.js":             true,
-		"test/built-ins/Proxy/defineProperty/targetdesc-not-compatible-descriptor.js":                               true,
-		"test/built-ins/Proxy/defineProperty/targetdesc-not-compatible-descriptor-realm.js":                         true,
-		"test/built-ins/Proxy/defineProperty/targetdesc-not-compatible-descriptor-not-configurable-target-realm.js": true,
-		"test/built-ins/Proxy/defineProperty/targetdesc-configurable-desc-not-configurable-realm.js":                true,
-		"test/built-ins/Proxy/has/trap-is-not-callable-realm.js":                                                    true,
-		"test/built-ins/Proxy/defineProperty/desc-realm.js":                                                         true,
-		"test/built-ins/Proxy/apply/trap-is-not-callable-realm.js":                                                  true,
-		"test/built-ins/Proxy/apply/arguments-realm.js":                                                             true,
-		"test/built-ins/Proxy/construct/trap-is-undefined-proto-from-ctor-realm.js":                                 true,
-		"test/built-ins/Proxy/construct/trap-is-not-callable-realm.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,
@@ -107,6 +59,10 @@ var (
 		"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,
 
 		// full unicode regexp flag
 		"test/built-ins/RegExp/prototype/Symbol.match/u-advance-after-empty.js":               true,
@@ -115,18 +71,18 @@ var (
 		"test/built-ins/RegExp/prototype/Symbol.match/builtin-infer-unicode.js":               true,
 
 		// object literals
-		"test/built-ins/Array/from/source-object-iterator-1.js": true,
-		"test/built-ins/Array/from/source-object-iterator-2.js": true,
-
-		// Typed arrays
-		"test/built-ins/Array/from/items-is-arraybuffer.js":                                 true,
-		"test/built-ins/Array/prototype/concat/Array.prototype.concat_small-typed-array.js": true,
-		"test/built-ins/Array/prototype/concat/Array.prototype.concat_large-typed-array.js": true,
-
-		// for-of
-		"test/language/statements/for-of/Array.prototype.keys.js":            true,
-		"test/language/statements/for-of/Array.prototype.entries.js":         true,
-		"test/language/statements/for-of/Array.prototype.Symbol.iterator.js": true,
+		"test/built-ins/Array/from/source-object-iterator-1.js":                   true,
+		"test/built-ins/Array/from/source-object-iterator-2.js":                   true,
+		"test/built-ins/TypedArray/prototype/fill/fill-values-conversion-once.js": true,
+		"test/built-ins/TypedArrays/of/this-is-not-constructor.js":                true,
+		"test/built-ins/TypedArrays/of/argument-number-value-throws.js":           true,
+		"test/built-ins/TypedArrays/from/this-is-not-constructor.js":              true,
+		"test/built-ins/TypedArrays/from/set-value-abrupt-completion.js":          true,
+		"test/built-ins/TypedArrays/from/property-abrupt-completion.js":           true,
+		"test/built-ins/TypedArray/of/this-is-not-constructor.js":                 true,
+		"test/built-ins/TypedArray/from/this-is-not-constructor.js":               true,
+		"test/built-ins/DataView/custom-proto-access-throws.js":                   true,
+		"test/built-ins/DataView/custom-proto-access-throws-sab.js":               true,
 
 		// arrow-function
 		"test/built-ins/Object/prototype/toString/proxy-function.js": true,
@@ -154,16 +110,23 @@ var (
 		"22.1.2.5",
 		"22.1.3",
 		"22.1.4",
+		"22.2",
 		"23.1",
 		"23.2",
 		"23.3",
 		"23.4",
+		"24.2",
 		"25.1.2",
 		"26.1",
 		"26.2",
 		"B.2.1",
 		"B.2.2",
 	}
+
+	esIdPrefixWhiteList = []string{
+		"sec-array.prototype.includes",
+		"sec-%typedarray%",
+	}
 )
 
 type tc39Test struct {
@@ -239,6 +202,20 @@ func parseTC39File(name string) (*tc39Meta, string, error) {
 	return &meta, str, nil
 }
 
+func (*tc39TestCtx) detachArrayBuffer(call FunctionCall) Value {
+	if obj, ok := call.Argument(0).(*Object); ok {
+		if buf, ok := obj.self.(*arrayBufferObject); ok {
+			buf.detach()
+			return _undefined
+		}
+	}
+	panic(typeError("detachArrayBuffer() is called with incompatible argument"))
+}
+
+func (*tc39TestCtx) throwIgnorableTestError(FunctionCall) Value {
+	panic(ignorableTestError)
+}
+
 func (ctx *tc39TestCtx) runTC39Test(name, src string, meta *tc39Meta, t testing.TB) {
 	defer func() {
 		if x := recover(); x != nil {
@@ -246,10 +223,21 @@ func (ctx *tc39TestCtx) runTC39Test(name, src string, meta *tc39Meta, t testing.
 		}
 	}()
 	vm := New()
+	_262 := vm.NewObject()
+	_262.Set("detachArrayBuffer", ctx.detachArrayBuffer)
+	_262.Set("createRealm", ctx.throwIgnorableTestError)
+	vm.Set("$262", _262)
+	vm.Set("IgnorableTestError", ignorableTestError)
+	vm.RunProgram(sabStub)
 	err, early := ctx.runTC39Script(name, src, meta.Includes, vm)
 
 	if err != nil {
 		if meta.Negative.Type == "" {
+			if err, ok := err.(*Exception); ok {
+				if err.Value() == ignorableTestError {
+					t.Skip("Test threw IgnorableTestError")
+				}
+			}
 			t.Fatalf("%s: %v", name, err)
 		} else {
 			if meta.Negative.Phase == "early" && !early || meta.Negative.Phase == "runtime" && early {
@@ -321,6 +309,18 @@ func (ctx *tc39TestCtx) runTC39File(name string, t testing.TB) {
 				}
 			}
 		}
+		if skip {
+			if meta.Esid != "" {
+				for _, prefix := range esIdPrefixWhiteList {
+					if strings.HasPrefix(meta.Esid, prefix) &&
+						(len(meta.Esid) == len(prefix) || meta.Esid[len(prefix)] == '.') {
+
+						skip = false
+						break
+					}
+				}
+			}
+		}
 		if skip {
 			t.Skip("Not ES5")
 		}

+ 0 - 3
token/token_const.go

@@ -208,9 +208,6 @@ var keywordTable = map[string]_keyword{
 	"in": {
 		token: IN,
 	},
-	"of": {
-		token: OF,
-	},
 	"do": {
 		token: DO,
 	},

+ 884 - 0
typedarrays.go

@@ -0,0 +1,884 @@
+package goja
+
+import (
+	"math"
+	"math/bits"
+	"reflect"
+	"strconv"
+	"unsafe"
+)
+
+type byteOrder bool
+
+const (
+	bigEndian    byteOrder = false
+	littleEndian byteOrder = true
+)
+
+var (
+	nativeEndian byteOrder
+
+	arrayBufferType = reflect.TypeOf(ArrayBuffer{})
+)
+
+type typedArrayObjectCtor func(buf *arrayBufferObject, offset, length int, proto *Object) *typedArrayObject
+
+type arrayBufferObject struct {
+	baseObject
+	detached bool
+	data     []byte
+}
+
+// ArrayBuffer is a Go wrapper around ECMAScript ArrayBuffer. Calling Runtime.ToValue() on it
+// returns the underlying ArrayBuffer. Calling Export() on an ECMAScript ArrayBuffer returns a wrapper.
+// Use Runtime.NewArrayBuffer([]byte) to create one.
+type ArrayBuffer struct {
+	buf *arrayBufferObject
+}
+
+type dataViewObject struct {
+	baseObject
+	viewedArrayBuf      *arrayBufferObject
+	byteLen, byteOffset int
+}
+
+type typedArray interface {
+	toRaw(Value) uint64
+	get(idx int) Value
+	set(idx int, value Value)
+	getRaw(idx int) uint64
+	setRaw(idx int, raw uint64)
+	less(i, j int) bool
+	swap(i, j int)
+	typeMatch(v Value) bool
+}
+
+type uint8Array []uint8
+type uint8ClampedArray []uint8
+type int8Array []int8
+type uint16Array []uint16
+type int16Array []int16
+type uint32Array []uint32
+type int32Array []int32
+type float32Array []float32
+type float64Array []float64
+
+type typedArrayObject struct {
+	baseObject
+	viewedArrayBuf *arrayBufferObject
+	defaultCtor    *Object
+	length, offset int
+	elemSize       int
+	typedArray     typedArray
+}
+
+func (a ArrayBuffer) toValue(r *Runtime) Value {
+	if a.buf == nil {
+		return _null
+	}
+	v := a.buf.val
+	if v.runtime != r {
+		panic(r.NewTypeError("Illegal runtime transition of an ArrayBuffer"))
+	}
+	return v
+}
+
+// Bytes returns the underlying []byte for this ArrayBuffer.
+// For detached ArrayBuffers returns nil.
+func (a ArrayBuffer) Bytes() []byte {
+	return a.buf.data
+}
+
+// Detach the ArrayBuffer. After this, the underlying []byte becomes unreferenced and any attempt
+// to use this ArrayBuffer results in a TypeError.
+// Returns false if it was already detached, true otherwise.
+// Note, this method may only be called from the goroutine that 'owns' the Runtime, it may not
+// be called concurrently.
+func (a ArrayBuffer) Detach() bool {
+	if a.buf.detached {
+		return false
+	}
+	a.buf.detach()
+	return true
+}
+
+// Detached returns true if the ArrayBuffer is detached.
+func (a ArrayBuffer) Detached() bool {
+	return a.buf.detached
+}
+
+func (r *Runtime) NewArrayBuffer(data []byte) ArrayBuffer {
+	buf := r._newArrayBuffer(r.global.ArrayBufferPrototype, nil)
+	buf.data = data
+	return ArrayBuffer{
+		buf: buf,
+	}
+}
+
+func (a *uint8Array) get(idx int) Value {
+	return intToValue(int64((*a)[idx]))
+}
+
+func (a *uint8Array) getRaw(idx int) uint64 {
+	return uint64((*a)[idx])
+}
+
+func (a *uint8Array) set(idx int, value Value) {
+	(*a)[idx] = toUint8(value)
+}
+
+func (a *uint8Array) toRaw(v Value) uint64 {
+	return uint64(toUint8(v))
+}
+
+func (a *uint8Array) setRaw(idx int, v uint64) {
+	(*a)[idx] = uint8(v)
+}
+
+func (a *uint8Array) less(i, j int) bool {
+	return (*a)[i] < (*a)[j]
+}
+
+func (a *uint8Array) swap(i, j int) {
+	(*a)[i], (*a)[j] = (*a)[j], (*a)[i]
+}
+
+func (a *uint8Array) typeMatch(v Value) bool {
+	if i, ok := v.(valueInt); ok {
+		return i >= 0 && i <= 255
+	}
+	return false
+}
+
+func (a *uint8ClampedArray) get(idx int) Value {
+	return intToValue(int64((*a)[idx]))
+}
+
+func (a *uint8ClampedArray) getRaw(idx int) uint64 {
+	return uint64((*a)[idx])
+}
+
+func (a *uint8ClampedArray) set(idx int, value Value) {
+	(*a)[idx] = toUint8Clamp(value)
+}
+
+func (a *uint8ClampedArray) toRaw(v Value) uint64 {
+	return uint64(toUint8Clamp(v))
+}
+
+func (a *uint8ClampedArray) setRaw(idx int, v uint64) {
+	(*a)[idx] = uint8(v)
+}
+
+func (a *uint8ClampedArray) less(i, j int) bool {
+	return (*a)[i] < (*a)[j]
+}
+
+func (a *uint8ClampedArray) swap(i, j int) {
+	(*a)[i], (*a)[j] = (*a)[j], (*a)[i]
+}
+
+func (a *uint8ClampedArray) typeMatch(v Value) bool {
+	if i, ok := v.(valueInt); ok {
+		return i >= 0 && i <= 255
+	}
+	return false
+}
+
+func (a *int8Array) get(idx int) Value {
+	return intToValue(int64((*a)[idx]))
+}
+
+func (a *int8Array) getRaw(idx int) uint64 {
+	return uint64((*a)[idx])
+}
+
+func (a *int8Array) set(idx int, value Value) {
+	(*a)[idx] = toInt8(value)
+}
+
+func (a *int8Array) toRaw(v Value) uint64 {
+	return uint64(toInt8(v))
+}
+
+func (a *int8Array) setRaw(idx int, v uint64) {
+	(*a)[idx] = int8(v)
+}
+
+func (a *int8Array) less(i, j int) bool {
+	return (*a)[i] < (*a)[j]
+}
+
+func (a *int8Array) swap(i, j int) {
+	(*a)[i], (*a)[j] = (*a)[j], (*a)[i]
+}
+
+func (a *int8Array) typeMatch(v Value) bool {
+	if i, ok := v.(valueInt); ok {
+		return i >= math.MinInt8 && i <= math.MaxInt8
+	}
+	return false
+}
+
+func (a *uint16Array) get(idx int) Value {
+	return intToValue(int64((*a)[idx]))
+}
+
+func (a *uint16Array) getRaw(idx int) uint64 {
+	return uint64((*a)[idx])
+}
+
+func (a *uint16Array) set(idx int, value Value) {
+	(*a)[idx] = toUint16(value)
+}
+
+func (a *uint16Array) toRaw(v Value) uint64 {
+	return uint64(toUint16(v))
+}
+
+func (a *uint16Array) setRaw(idx int, v uint64) {
+	(*a)[idx] = uint16(v)
+}
+
+func (a *uint16Array) less(i, j int) bool {
+	return (*a)[i] < (*a)[j]
+}
+
+func (a *uint16Array) swap(i, j int) {
+	(*a)[i], (*a)[j] = (*a)[j], (*a)[i]
+}
+
+func (a *uint16Array) typeMatch(v Value) bool {
+	if i, ok := v.(valueInt); ok {
+		return i >= 0 && i <= math.MaxUint16
+	}
+	return false
+}
+
+func (a *int16Array) get(idx int) Value {
+	return intToValue(int64((*a)[idx]))
+}
+
+func (a *int16Array) getRaw(idx int) uint64 {
+	return uint64((*a)[idx])
+}
+
+func (a *int16Array) set(idx int, value Value) {
+	(*a)[idx] = toInt16(value)
+}
+
+func (a *int16Array) toRaw(v Value) uint64 {
+	return uint64(toInt16(v))
+}
+
+func (a *int16Array) setRaw(idx int, v uint64) {
+	(*a)[idx] = int16(v)
+}
+
+func (a *int16Array) less(i, j int) bool {
+	return (*a)[i] < (*a)[j]
+}
+
+func (a *int16Array) swap(i, j int) {
+	(*a)[i], (*a)[j] = (*a)[j], (*a)[i]
+}
+
+func (a *int16Array) typeMatch(v Value) bool {
+	if i, ok := v.(valueInt); ok {
+		return i >= math.MinInt16 && i <= math.MaxInt16
+	}
+	return false
+}
+
+func (a *uint32Array) get(idx int) Value {
+	return intToValue(int64((*a)[idx]))
+}
+
+func (a *uint32Array) getRaw(idx int) uint64 {
+	return uint64((*a)[idx])
+}
+
+func (a *uint32Array) set(idx int, value Value) {
+	(*a)[idx] = toUint32(value)
+}
+
+func (a *uint32Array) toRaw(v Value) uint64 {
+	return uint64(toUint32(v))
+}
+
+func (a *uint32Array) setRaw(idx int, v uint64) {
+	(*a)[idx] = uint32(v)
+}
+
+func (a *uint32Array) less(i, j int) bool {
+	return (*a)[i] < (*a)[j]
+}
+
+func (a *uint32Array) swap(i, j int) {
+	(*a)[i], (*a)[j] = (*a)[j], (*a)[i]
+}
+
+func (a *uint32Array) typeMatch(v Value) bool {
+	if i, ok := v.(valueInt); ok {
+		return i >= 0 && i <= math.MaxUint32
+	}
+	return false
+}
+
+func (a *int32Array) get(idx int) Value {
+	return intToValue(int64((*a)[idx]))
+}
+
+func (a *int32Array) getRaw(idx int) uint64 {
+	return uint64((*a)[idx])
+}
+
+func (a *int32Array) set(idx int, value Value) {
+	(*a)[idx] = toInt32(value)
+}
+
+func (a *int32Array) toRaw(v Value) uint64 {
+	return uint64(toInt32(v))
+}
+
+func (a *int32Array) setRaw(idx int, v uint64) {
+	(*a)[idx] = int32(v)
+}
+
+func (a *int32Array) less(i, j int) bool {
+	return (*a)[i] < (*a)[j]
+}
+
+func (a *int32Array) swap(i, j int) {
+	(*a)[i], (*a)[j] = (*a)[j], (*a)[i]
+}
+
+func (a *int32Array) typeMatch(v Value) bool {
+	if i, ok := v.(valueInt); ok {
+		return i >= math.MinInt32 && i <= math.MaxInt32
+	}
+	return false
+}
+
+func (a *float32Array) get(idx int) Value {
+	return floatToValue(float64((*a)[idx]))
+}
+
+func (a *float32Array) getRaw(idx int) uint64 {
+	return uint64(math.Float32bits((*a)[idx]))
+}
+
+func (a *float32Array) set(idx int, value Value) {
+	(*a)[idx] = toFloat32(value)
+}
+
+func (a *float32Array) toRaw(v Value) uint64 {
+	return uint64(math.Float32bits(toFloat32(v)))
+}
+
+func (a *float32Array) setRaw(idx int, v uint64) {
+	(*a)[idx] = math.Float32frombits(uint32(v))
+}
+
+func typedFloatLess(x, y float64) bool {
+	xNan := math.IsNaN(x)
+	yNan := math.IsNaN(y)
+	if xNan && yNan {
+		return false
+	}
+	if xNan {
+		return false
+	}
+	if yNan {
+		return true
+	}
+	if x >= y {
+		return false
+	}
+	return true
+}
+
+func (a *float32Array) less(i, j int) bool {
+	return typedFloatLess(float64((*a)[i]), float64((*a)[j]))
+}
+
+func (a *float32Array) swap(i, j int) {
+	(*a)[i], (*a)[j] = (*a)[j], (*a)[i]
+}
+
+func (a *float32Array) typeMatch(v Value) bool {
+	switch v.(type) {
+	case valueInt, valueFloat:
+		return true
+	}
+	return false
+}
+
+func (a *float64Array) get(idx int) Value {
+	return floatToValue((*a)[idx])
+}
+
+func (a *float64Array) getRaw(idx int) uint64 {
+	return math.Float64bits((*a)[idx])
+}
+
+func (a *float64Array) set(idx int, value Value) {
+	(*a)[idx] = value.ToFloat()
+}
+
+func (a *float64Array) toRaw(v Value) uint64 {
+	return math.Float64bits(v.ToFloat())
+}
+
+func (a *float64Array) setRaw(idx int, v uint64) {
+	(*a)[idx] = math.Float64frombits(v)
+}
+
+func (a *float64Array) less(i, j int) bool {
+	return typedFloatLess((*a)[i], (*a)[j])
+}
+
+func (a *float64Array) swap(i, j int) {
+	(*a)[i], (*a)[j] = (*a)[j], (*a)[i]
+}
+
+func (a *float64Array) typeMatch(v Value) bool {
+	switch v.(type) {
+	case valueInt, valueFloat:
+		return true
+	}
+	return false
+}
+
+func (a *typedArrayObject) _getIdx(idx int) Value {
+	a.viewedArrayBuf.ensureNotDetached()
+	if idx < a.length {
+		return a.typedArray.get(idx + a.offset)
+	}
+	return nil
+}
+
+func strToTAIdx(s string) (int, bool) {
+	i, err := strconv.ParseInt(s, 10, bits.UintSize)
+	if err != nil {
+		return 0, false
+	}
+	return int(i), true
+}
+
+func (a *typedArrayObject) getOwnPropStr(name string) Value {
+	if idx, ok := strToTAIdx(name); ok {
+		v := a._getIdx(idx)
+		if v != nil {
+			return &valueProperty{
+				value:      v,
+				writable:   true,
+				enumerable: true,
+			}
+		}
+		return nil
+	}
+	return a.baseObject.getOwnPropStr(name)
+}
+
+func (a *typedArrayObject) getOwnPropIdx(idx valueInt) Value {
+	v := a._getIdx(toInt(int64(idx)))
+	if v != nil {
+		return &valueProperty{
+			value:      v,
+			writable:   true,
+			enumerable: true,
+		}
+	}
+	return nil
+}
+
+func (a *typedArrayObject) getStr(name string, receiver Value) Value {
+	if idx, ok := strToTAIdx(name); ok {
+		prop := a._getIdx(idx)
+		if prop == nil {
+			if a.prototype != nil {
+				if receiver == nil {
+					return a.prototype.self.getStr(name, a.val)
+				}
+				return a.prototype.self.getStr(name, receiver)
+			}
+		}
+		return prop
+	}
+	return a.baseObject.getStr(name, receiver)
+}
+
+func (a *typedArrayObject) getIdx(idx valueInt, receiver Value) Value {
+	prop := a._getIdx(toInt(int64(idx)))
+	if prop == nil {
+		if a.prototype != nil {
+			if receiver == nil {
+				return a.prototype.self.getIdx(idx, a.val)
+			}
+			return a.prototype.self.getIdx(idx, receiver)
+		}
+	}
+	return prop
+}
+
+func (a *typedArrayObject) _putIdx(idx int, v Value, throw bool) bool {
+	v = v.ToNumber()
+	a.viewedArrayBuf.ensureNotDetached()
+	if idx >= 0 && idx < a.length {
+		a.typedArray.set(idx+a.offset, v)
+		return true
+	}
+	// As far as I understand the specification this should throw, but neither V8 nor SpiderMonkey does
+	return false
+}
+
+func (a *typedArrayObject) _hasIdx(idx int) bool {
+	a.viewedArrayBuf.ensureNotDetached()
+	return idx >= 0 && idx < a.length
+}
+
+func (a *typedArrayObject) setOwnStr(p string, v Value, throw bool) bool {
+	if idx, ok := strToTAIdx(p); ok {
+		return a._putIdx(idx, v, throw)
+	}
+	return a.baseObject.setOwnStr(p, v, throw)
+}
+
+func (a *typedArrayObject) setOwnIdx(p valueInt, v Value, throw bool) bool {
+	return a._putIdx(toInt(int64(p)), v, throw)
+}
+
+func (a *typedArrayObject) setForeignStr(p string, v, receiver Value, throw bool) (res bool, handled bool) {
+	return a._setForeignStr(p, a.getOwnPropStr(p), v, receiver, throw)
+}
+
+func (a *typedArrayObject) setForeignIdx(p valueInt, v, receiver Value, throw bool) (res bool, handled bool) {
+	return a._setForeignIdx(p, trueValIfPresent(a.hasOwnPropertyIdx(p)), v, receiver, throw)
+}
+
+func (a *typedArrayObject) hasOwnPropertyStr(name string) bool {
+	if idx, ok := strToTAIdx(name); ok {
+		a.viewedArrayBuf.ensureNotDetached()
+		return idx < a.length
+	}
+
+	return a.baseObject.hasOwnPropertyStr(name)
+}
+
+func (a *typedArrayObject) hasOwnPropertyIdx(idx valueInt) bool {
+	return a._hasIdx(toInt(int64(idx)))
+}
+
+func (a *typedArrayObject) _defineIdxProperty(idx int, desc PropertyDescriptor, throw bool) bool {
+	prop, ok := a._defineOwnProperty(strconv.Itoa(idx), a.getOwnPropIdx(valueInt(idx)), desc, throw)
+	if ok {
+		return a._putIdx(idx, prop, throw)
+	}
+	return ok
+}
+
+func (a *typedArrayObject) defineOwnPropertyStr(name string, desc PropertyDescriptor, throw bool) bool {
+	if idx, ok := strToTAIdx(name); ok {
+		return a._defineIdxProperty(idx, desc, throw)
+	}
+	return a.baseObject.defineOwnPropertyStr(name, desc, throw)
+}
+
+func (a *typedArrayObject) defineOwnPropertyIdx(name valueInt, desc PropertyDescriptor, throw bool) bool {
+	return a._defineIdxProperty(toInt(int64(name)), desc, throw)
+}
+
+func (a *typedArrayObject) deleteStr(name string, throw bool) bool {
+	if idx, ok := strToTAIdx(name); ok {
+		if idx < a.length {
+			a.val.runtime.typeErrorResult(throw, "Cannot delete property '%d' of %s", idx, a.val.String())
+		}
+	}
+
+	return a.baseObject.deleteStr(name, throw)
+}
+
+func (a *typedArrayObject) deleteIdx(idx valueInt, throw bool) bool {
+	if idx >= 0 && int64(idx) < int64(a.length) {
+		a.val.runtime.typeErrorResult(throw, "Cannot delete property '%d' of %s", idx, a.val.String())
+	}
+
+	return true
+}
+
+func (a *typedArrayObject) ownKeys(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)
+}
+
+type typedArrayPropIter struct {
+	a   *typedArrayObject
+	idx int
+}
+
+func (i *typedArrayPropIter) next() (propIterItem, iterNextFunc) {
+	if i.idx < i.a.length {
+		name := strconv.Itoa(i.idx)
+		prop := i.a._getIdx(i.idx)
+		i.idx++
+		return propIterItem{name: name, value: prop}, i.next
+	}
+
+	return i.a.baseObject.enumerateUnfiltered()()
+}
+
+func (a *typedArrayObject) enumerateUnfiltered() iterNextFunc {
+	return (&typedArrayPropIter{
+		a: a,
+	}).next
+}
+
+func (r *Runtime) _newTypedArrayObject(buf *arrayBufferObject, offset, length, elemSize int, defCtor *Object, arr typedArray, proto *Object) *typedArrayObject {
+	o := &Object{runtime: r}
+	a := &typedArrayObject{
+		baseObject: baseObject{
+			val:        o,
+			class:      classObject,
+			prototype:  proto,
+			extensible: true,
+		},
+		viewedArrayBuf: buf,
+		offset:         offset,
+		length:         length,
+		elemSize:       elemSize,
+		defaultCtor:    defCtor,
+		typedArray:     arr,
+	}
+	o.self = a
+	a.init()
+	return a
+
+}
+
+func (r *Runtime) newUint8ArrayObject(buf *arrayBufferObject, offset, length int, proto *Object) *typedArrayObject {
+	return r._newTypedArrayObject(buf, offset, length, 1, r.global.Uint8Array, (*uint8Array)(&buf.data), proto)
+}
+
+func (r *Runtime) newUint8ClampedArrayObject(buf *arrayBufferObject, offset, length int, proto *Object) *typedArrayObject {
+	return r._newTypedArrayObject(buf, offset, length, 1, r.global.Uint8ClampedArray, (*uint8ClampedArray)(&buf.data), proto)
+}
+
+func (r *Runtime) newInt8ArrayObject(buf *arrayBufferObject, offset, length int, proto *Object) *typedArrayObject {
+	return r._newTypedArrayObject(buf, offset, length, 1, r.global.Int8Array, (*int8Array)(unsafe.Pointer(&buf.data)), proto)
+}
+
+func (r *Runtime) newUint16ArrayObject(buf *arrayBufferObject, offset, length int, proto *Object) *typedArrayObject {
+	return r._newTypedArrayObject(buf, offset, length, 2, r.global.Uint16Array, (*uint16Array)(unsafe.Pointer(&buf.data)), proto)
+}
+
+func (r *Runtime) newInt16ArrayObject(buf *arrayBufferObject, offset, length int, proto *Object) *typedArrayObject {
+	return r._newTypedArrayObject(buf, offset, length, 2, r.global.Int16Array, (*int16Array)(unsafe.Pointer(&buf.data)), proto)
+}
+
+func (r *Runtime) newUint32ArrayObject(buf *arrayBufferObject, offset, length int, proto *Object) *typedArrayObject {
+	return r._newTypedArrayObject(buf, offset, length, 4, r.global.Uint32Array, (*uint32Array)(unsafe.Pointer(&buf.data)), proto)
+}
+
+func (r *Runtime) newInt32ArrayObject(buf *arrayBufferObject, offset, length int, proto *Object) *typedArrayObject {
+	return r._newTypedArrayObject(buf, offset, length, 4, r.global.Int32Array, (*int32Array)(unsafe.Pointer(&buf.data)), proto)
+}
+
+func (r *Runtime) newFloat32ArrayObject(buf *arrayBufferObject, offset, length int, proto *Object) *typedArrayObject {
+	return r._newTypedArrayObject(buf, offset, length, 4, r.global.Float32Array, (*float32Array)(unsafe.Pointer(&buf.data)), proto)
+}
+
+func (r *Runtime) newFloat64ArrayObject(buf *arrayBufferObject, offset, length int, proto *Object) *typedArrayObject {
+	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)
+	o.viewedArrayBuf.ensureNotDetached()
+	if getIdx+size > o.byteLen {
+		panic(o.val.runtime.newError(o.val.runtime.global.RangeError, "Index %d is out of bounds", getIdx))
+	}
+	getIdx += o.byteOffset
+	var bo byteOrder
+	if littleEndianVal != nil {
+		if littleEndianVal.ToBoolean() {
+			bo = littleEndian
+		} else {
+			bo = bigEndian
+		}
+	} else {
+		bo = nativeEndian
+	}
+	return getIdx, bo
+}
+
+func (o *arrayBufferObject) ensureNotDetached() {
+	if o.detached {
+		panic(o.val.runtime.NewTypeError("ArrayBuffer is detached"))
+	}
+}
+
+func (o *arrayBufferObject) getFloat32(idx int, byteOrder byteOrder) float32 {
+	return math.Float32frombits(o.getUint32(idx, byteOrder))
+}
+
+func (o *arrayBufferObject) setFloat32(idx int, val float32, byteOrder byteOrder) {
+	o.setUint32(idx, math.Float32bits(val), byteOrder)
+}
+
+func (o *arrayBufferObject) getFloat64(idx int, byteOrder byteOrder) float64 {
+	return math.Float64frombits(o.getUint64(idx, byteOrder))
+}
+
+func (o *arrayBufferObject) setFloat64(idx int, val float64, byteOrder byteOrder) {
+	o.setUint64(idx, math.Float64bits(val), byteOrder)
+}
+
+func (o *arrayBufferObject) getUint64(idx int, byteOrder byteOrder) uint64 {
+	var b []byte
+	if byteOrder == nativeEndian {
+		b = o.data[idx : idx+8]
+	} else {
+		b = make([]byte, 8)
+		d := o.data[idx : idx+8]
+		b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7] = d[7], d[6], d[5], d[4], d[3], d[2], d[1], d[0]
+	}
+	return *((*uint64)(unsafe.Pointer(&b[0])))
+}
+
+func (o *arrayBufferObject) setUint64(idx int, val uint64, byteOrder byteOrder) {
+	if byteOrder == nativeEndian {
+		*(*uint64)(unsafe.Pointer(&o.data[idx])) = val
+	} else {
+		b := (*[8]byte)(unsafe.Pointer(&val))
+		d := o.data[idx : idx+8]
+		d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7] = b[7], b[6], b[5], b[4], b[3], b[2], b[1], b[0]
+	}
+}
+
+func (o *arrayBufferObject) getUint32(idx int, byteOrder byteOrder) uint32 {
+	var b []byte
+	if byteOrder == nativeEndian {
+		b = o.data[idx : idx+4]
+	} else {
+		b = make([]byte, 4)
+		d := o.data[idx : idx+4]
+		b[0], b[1], b[2], b[3] = d[3], d[2], d[1], d[0]
+	}
+	return *((*uint32)(unsafe.Pointer(&b[0])))
+}
+
+func (o *arrayBufferObject) setUint32(idx int, val uint32, byteOrder byteOrder) {
+	if byteOrder == nativeEndian {
+		*(*uint32)(unsafe.Pointer(&o.data[idx])) = val
+	} else {
+		b := (*[4]byte)(unsafe.Pointer(&val))
+		d := o.data[idx : idx+4]
+		d[0], d[1], d[2], d[3] = b[3], b[2], b[1], b[0]
+	}
+}
+
+func (o *arrayBufferObject) getUint16(idx int, byteOrder byteOrder) uint16 {
+	var b []byte
+	if byteOrder == nativeEndian {
+		b = o.data[idx : idx+2]
+	} else {
+		b = make([]byte, 2)
+		d := o.data[idx : idx+2]
+		b[0], b[1] = d[1], d[0]
+	}
+	return *((*uint16)(unsafe.Pointer(&b[0])))
+}
+
+func (o *arrayBufferObject) setUint16(idx int, val uint16, byteOrder byteOrder) {
+	if byteOrder == nativeEndian {
+		*(*uint16)(unsafe.Pointer(&o.data[idx])) = val
+	} else {
+		b := (*[2]byte)(unsafe.Pointer(&val))
+		d := o.data[idx : idx+2]
+		d[0], d[1] = b[1], b[0]
+	}
+}
+
+func (o *arrayBufferObject) getUint8(idx int) uint8 {
+	return o.data[idx]
+}
+
+func (o *arrayBufferObject) setUint8(idx int, val uint8) {
+	o.data[idx] = val
+}
+
+func (o *arrayBufferObject) getInt32(idx int, byteOrder byteOrder) int32 {
+	return int32(o.getUint32(idx, byteOrder))
+}
+
+func (o *arrayBufferObject) setInt32(idx int, val int32, byteOrder byteOrder) {
+	o.setUint32(idx, uint32(val), byteOrder)
+}
+
+func (o *arrayBufferObject) getInt16(idx int, byteOrder byteOrder) int16 {
+	return int16(o.getUint16(idx, byteOrder))
+}
+
+func (o *arrayBufferObject) setInt16(idx int, val int16, byteOrder byteOrder) {
+	o.setUint16(idx, uint16(val), byteOrder)
+}
+
+func (o *arrayBufferObject) getInt8(idx int) int8 {
+	return int8(o.data[idx])
+}
+
+func (o *arrayBufferObject) setInt8(idx int, val int8) {
+	o.setUint8(idx, uint8(val))
+}
+
+func (o *arrayBufferObject) detach() {
+	o.data = nil
+	o.detached = true
+}
+
+func (o *arrayBufferObject) exportType() reflect.Type {
+	return arrayBufferType
+}
+
+func (o *arrayBufferObject) export() interface{} {
+	return ArrayBuffer{
+		buf: o,
+	}
+}
+
+func (r *Runtime) _newArrayBuffer(proto *Object, o *Object) *arrayBufferObject {
+	if o == nil {
+		o = &Object{runtime: r}
+	}
+	b := &arrayBufferObject{
+		baseObject: baseObject{
+			class:      classObject,
+			val:        o,
+			prototype:  proto,
+			extensible: true,
+		},
+	}
+	o.self = b
+	b.init()
+	return b
+}
+
+func init() {
+	buf := [2]byte{}
+	*(*uint16)(unsafe.Pointer(&buf[0])) = uint16(0xCAFE)
+
+	switch buf {
+	case [2]byte{0xFE, 0xCA}:
+		nativeEndian = littleEndian
+	case [2]byte{0xCA, 0xFE}:
+		nativeEndian = bigEndian
+	default:
+		panic("Could not determine native endianness.")
+	}
+}

+ 71 - 0
typedarrays_test.go

@@ -0,0 +1,71 @@
+package goja
+
+import "testing"
+
+func TestUint16ArrayObject(t *testing.T) {
+	vm := New()
+	buf := vm._newArrayBuffer(vm.global.ArrayBufferPrototype, nil)
+	buf.data = make([]byte, 16)
+	if nativeEndian == littleEndian {
+		buf.data[2] = 0xFE
+		buf.data[3] = 0xCA
+	} else {
+		buf.data[2] = 0xCA
+		buf.data[3] = 0xFE
+	}
+	a := vm.newUint16ArrayObject(buf, 1, 1, nil)
+	v := a.getIdx(valueInt(0), nil)
+	if v != valueInt(0xCAFE) {
+		t.Fatalf("v: %v", v)
+	}
+}
+
+func TestArrayBufferGoWrapper(t *testing.T) {
+	vm := New()
+	data := []byte{0xAA, 0xBB}
+	buf := vm.NewArrayBuffer(data)
+	vm.Set("buf", buf)
+	_, err := vm.RunString(`
+	var a = new Uint8Array(buf);
+	if (a.length !== 2 || a[0] !== 0xAA || a[1] !== 0xBB) {
+		throw new Error(a);
+	}
+	`)
+	if err != nil {
+		t.Fatal(err)
+	}
+	ret, err := vm.RunString(`
+	var b = Uint8Array.of(0xCC, 0xDD);
+	b.buffer;
+	`)
+	if err != nil {
+		t.Fatal(err)
+	}
+	buf1 := ret.Export().(ArrayBuffer)
+	data1 := buf1.Bytes()
+	if len(data1) != 2 || data1[0] != 0xCC || data1[1] != 0xDD {
+		t.Fatal(data1)
+	}
+	if buf1.Detached() {
+		t.Fatal("buf1.Detached() returned true")
+	}
+	if !buf1.Detach() {
+		t.Fatal("buf1.Detach() returned false")
+	}
+	if !buf1.Detached() {
+		t.Fatal("buf1.Detached() returned false")
+	}
+	_, err = vm.RunString(`
+	try {
+		(b[0]);
+		throw new Error("expected TypeError");
+	} catch (e) {
+		if (!(e instanceof TypeError)) {
+			throw e;
+		}
+	}
+	`)
+	if err != nil {
+		t.Fatal(err)
+	}
+}

+ 6 - 1
value.go

@@ -18,7 +18,8 @@ var (
 	_positiveInf  Value = valueFloat(math.Inf(+1))
 	_negativeInf  Value = valueFloat(math.Inf(-1))
 	_positiveZero Value = valueInt(0)
-	_negativeZero Value = valueFloat(math.Float64frombits(0 | (1 << 63)))
+	negativeZero        = math.Float64frombits(0 | (1 << 63))
+	_negativeZero Value = valueFloat(negativeZero)
 	_epsilon            = valueFloat(2.2204460492503130808472633361816e-16)
 	_undefined    Value = valueUndefined{}
 )
@@ -55,6 +56,10 @@ type Value interface {
 	hash(hash64 hash.Hash64) uint64
 }
 
+type valueContainer interface {
+	toValue(*Runtime) Value
+}
+
 type typeError string
 
 type valueInt int64

+ 4 - 4
vm.go

@@ -924,7 +924,7 @@ var sal _sal
 
 func (_sal) exec(vm *vm) {
 	left := toInt32(vm.stack[vm.sp-2])
-	right := toUInt32(vm.stack[vm.sp-1])
+	right := toUint32(vm.stack[vm.sp-1])
 	vm.stack[vm.sp-2] = intToValue(int64(left << (right & 0x1F)))
 	vm.sp--
 	vm.pc++
@@ -936,7 +936,7 @@ var sar _sar
 
 func (_sar) exec(vm *vm) {
 	left := toInt32(vm.stack[vm.sp-2])
-	right := toUInt32(vm.stack[vm.sp-1])
+	right := toUint32(vm.stack[vm.sp-1])
 	vm.stack[vm.sp-2] = intToValue(int64(left >> (right & 0x1F)))
 	vm.sp--
 	vm.pc++
@@ -947,8 +947,8 @@ type _shr struct{}
 var shr _shr
 
 func (_shr) exec(vm *vm) {
-	left := toUInt32(vm.stack[vm.sp-2])
-	right := toUInt32(vm.stack[vm.sp-1])
+	left := toUint32(vm.stack[vm.sp-2])
+	right := toUint32(vm.stack[vm.sp-1])
 	vm.stack[vm.sp-2] = intToValue(int64(left >> (right & 0x1F)))
 	vm.sp--
 	vm.pc++