Browse Source

Aligned detached buffer semantics with the latest specs. Fixes #315.

Dmitry Panov 4 years ago
parent
commit
ebe1101e51
5 changed files with 287 additions and 106 deletions
  1. 64 63
      builtin_typedarrays.go
  2. 26 0
      builtin_typedarrays_test.go
  3. 15 0
      tc39_test.go
  4. 38 36
      typedarrays.go
  5. 144 7
      typedarrays_test.go

+ 64 - 63
builtin_typedarrays.go

@@ -21,7 +21,7 @@ func (ctx *typedArraySortCtx) Len() int {
 
 func (ctx *typedArraySortCtx) Less(i, j int) bool {
 	if ctx.needValidate {
-		ctx.ta.viewedArrayBuf.ensureNotDetached()
+		ctx.ta.viewedArrayBuf.ensureNotDetached(true)
 		ctx.needValidate = false
 	}
 	offset := ctx.ta.offset
@@ -54,7 +54,7 @@ func (ctx *typedArraySortCtx) Less(i, j int) bool {
 
 func (ctx *typedArraySortCtx) Swap(i, j int) {
 	if ctx.needValidate {
-		ctx.ta.viewedArrayBuf.ensureNotDetached()
+		ctx.ta.viewedArrayBuf.ensureNotDetached(true)
 		ctx.needValidate = false
 	}
 	offset := ctx.ta.offset
@@ -88,8 +88,10 @@ func (r *Runtime) builtin_newArrayBuffer(args []Value, newTarget *Object) *Objec
 func (r *Runtime) arrayBufferProto_getByteLength(call FunctionCall) Value {
 	o := r.toObject(call.This)
 	if b, ok := o.self.(*arrayBufferObject); ok {
-		b.ensureNotDetached()
-		return intToValue(int64(len(b.data)))
+		if b.ensureNotDetached(false) {
+			return intToValue(int64(len(b.data)))
+		}
+		return intToValue(0)
 	}
 	panic(r.NewTypeError("Object is not ArrayBuffer: %s", o))
 }
@@ -109,16 +111,15 @@ func (r *Runtime) arrayBufferProto_slice(call FunctionCall) Value {
 		newLen := max(stop-start, 0)
 		ret := r.speciesConstructor(o, r.global.ArrayBuffer)([]Value{intToValue(newLen)}, nil)
 		if ab, ok := ret.self.(*arrayBufferObject); ok {
-			ab.ensureNotDetached()
-			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)))
-			}
-			b.ensureNotDetached()
-
-			if stop > start {
+			if newLen > 0 {
+				b.ensureNotDetached(true)
+				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)))
+				}
+				ab.ensureNotDetached(true)
 				copy(ab.data, b.data[start:stop])
 			}
 			return ret
@@ -162,7 +163,7 @@ func (r *Runtime) newDataView(args []Value, newTarget *Object) *Object {
 	if len(args) > 1 {
 		offsetArg := nilSafe(args[1])
 		byteOffset = r.toIndex(offsetArg)
-		buffer.ensureNotDetached()
+		buffer.ensureNotDetached(true)
 		if byteOffset > len(buffer.data) {
 			panic(r.newError(r.global.RangeError, "Start offset %s is outside the bounds of the buffer", offsetArg.String()))
 		}
@@ -201,7 +202,7 @@ func (r *Runtime) dataViewProto_getBuffer(call FunctionCall) Value {
 
 func (r *Runtime) dataViewProto_getByteLen(call FunctionCall) Value {
 	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
-		dv.viewedArrayBuf.ensureNotDetached()
+		dv.viewedArrayBuf.ensureNotDetached(true)
 		return intToValue(int64(dv.byteLen))
 	}
 	panic(r.NewTypeError("Method get DataView.prototype.byteLength called on incompatible receiver %s", call.This.String()))
@@ -209,7 +210,7 @@ func (r *Runtime) dataViewProto_getByteLen(call FunctionCall) Value {
 
 func (r *Runtime) dataViewProto_getByteOffset(call FunctionCall) Value {
 	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
-		dv.viewedArrayBuf.ensureNotDetached()
+		dv.viewedArrayBuf.ensureNotDetached(true)
 		return intToValue(int64(dv.byteOffset))
 	}
 	panic(r.NewTypeError("Method get DataView.prototype.byteOffset called on incompatible receiver %s", call.This.String()))
@@ -392,7 +393,7 @@ func (r *Runtime) typedArrayProto_getByteOffset(call FunctionCall) Value {
 
 func (r *Runtime) typedArrayProto_copyWithin(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		l := int64(ta.length)
 		var relEnd int64
 		to := toIntStrict(relToIdx(call.Argument(0).ToInteger(), l))
@@ -407,7 +408,7 @@ func (r *Runtime) typedArrayProto_copyWithin(call FunctionCall) Value {
 		offset := ta.offset
 		elemSize := ta.elemSize
 		if final > from {
-			ta.viewedArrayBuf.ensureNotDetached()
+			ta.viewedArrayBuf.ensureNotDetached(true)
 			copy(data[(offset+to)*elemSize:], data[(offset+from)*elemSize:(offset+final)*elemSize])
 		}
 		return call.This
@@ -417,7 +418,7 @@ func (r *Runtime) typedArrayProto_copyWithin(call FunctionCall) Value {
 
 func (r *Runtime) typedArrayProto_entries(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		return r.createArrayIterator(ta.val, iterationKindKeyValue)
 	}
 	panic(r.NewTypeError("Method TypedArray.prototype.entries called on incompatible receiver %s", call.This.String()))
@@ -425,14 +426,14 @@ func (r *Runtime) typedArrayProto_entries(call FunctionCall) Value {
 
 func (r *Runtime) typedArrayProto_every(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		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()
+			ta.viewedArrayBuf.ensureNotDetached(true)
 			fc.Arguments[0] = ta.typedArray.get(ta.offset + k)
 			fc.Arguments[1] = intToValue(int64(k))
 			if !callbackFn(fc).ToBoolean() {
@@ -447,7 +448,7 @@ func (r *Runtime) typedArrayProto_every(call FunctionCall) Value {
 
 func (r *Runtime) typedArrayProto_fill(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		l := int64(ta.length)
 		k := toIntStrict(relToIdx(call.Argument(1).ToInteger(), l))
 		var relEnd int64
@@ -458,7 +459,7 @@ func (r *Runtime) typedArrayProto_fill(call FunctionCall) Value {
 		}
 		final := toIntStrict(relToIdx(relEnd, l))
 		value := ta.typedArray.toRaw(call.Argument(0))
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		for ; k < final; k++ {
 			ta.typedArray.setRaw(ta.offset+k, value)
 		}
@@ -470,7 +471,7 @@ func (r *Runtime) typedArrayProto_fill(call FunctionCall) Value {
 func (r *Runtime) typedArrayProto_filter(call FunctionCall) Value {
 	o := r.toObject(call.This)
 	if ta, ok := o.self.(*typedArrayObject); ok {
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		callbackFn := r.toCallable(call.Argument(0))
 		fc := FunctionCall{
 			This:      call.Argument(1),
@@ -479,7 +480,7 @@ func (r *Runtime) typedArrayProto_filter(call FunctionCall) Value {
 		buf := make([]byte, 0, ta.length*ta.elemSize)
 		captured := 0
 		for k := 0; k < ta.length; k++ {
-			ta.viewedArrayBuf.ensureNotDetached()
+			ta.viewedArrayBuf.ensureNotDetached(true)
 			fc.Arguments[0] = ta.typedArray.get(k)
 			fc.Arguments[1] = intToValue(int64(k))
 			if callbackFn(fc).ToBoolean() {
@@ -508,14 +509,14 @@ func (r *Runtime) typedArrayProto_filter(call FunctionCall) Value {
 
 func (r *Runtime) typedArrayProto_find(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		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()
+			ta.viewedArrayBuf.ensureNotDetached(true)
 			val := ta.typedArray.get(ta.offset + k)
 			fc.Arguments[0] = val
 			fc.Arguments[1] = intToValue(int64(k))
@@ -530,14 +531,14 @@ func (r *Runtime) typedArrayProto_find(call FunctionCall) Value {
 
 func (r *Runtime) typedArrayProto_findIndex(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		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()
+			ta.viewedArrayBuf.ensureNotDetached(true)
 			fc.Arguments[0] = ta.typedArray.get(ta.offset + k)
 			fc.Arguments[1] = intToValue(int64(k))
 			if predicate(fc).ToBoolean() {
@@ -551,14 +552,14 @@ func (r *Runtime) typedArrayProto_findIndex(call FunctionCall) Value {
 
 func (r *Runtime) typedArrayProto_forEach(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		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()
+			ta.viewedArrayBuf.ensureNotDetached(true)
 			if val := ta.typedArray.get(k); val != nil {
 				fc.Arguments[0] = val
 				fc.Arguments[1] = intToValue(int64(k))
@@ -572,7 +573,7 @@ func (r *Runtime) typedArrayProto_forEach(call FunctionCall) Value {
 
 func (r *Runtime) typedArrayProto_includes(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		length := int64(ta.length)
 		if length == 0 {
 			return valueFalse
@@ -587,7 +588,7 @@ func (r *Runtime) typedArrayProto_includes(call FunctionCall) Value {
 			n = max(length+n, 0)
 		}
 
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		searchElement := call.Argument(0)
 		if searchElement == _negativeZero {
 			searchElement = _positiveZero
@@ -607,7 +608,7 @@ func (r *Runtime) typedArrayProto_includes(call FunctionCall) Value {
 
 func (r *Runtime) typedArrayProto_indexOf(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		length := int64(ta.length)
 		if length == 0 {
 			return intToValue(-1)
@@ -622,7 +623,7 @@ func (r *Runtime) typedArrayProto_indexOf(call FunctionCall) Value {
 			n = max(length+n, 0)
 		}
 
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		searchElement := call.Argument(0)
 		if searchElement == _negativeZero {
 			searchElement = _positiveZero
@@ -642,7 +643,7 @@ func (r *Runtime) typedArrayProto_indexOf(call FunctionCall) Value {
 
 func (r *Runtime) typedArrayProto_join(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		s := call.Argument(0)
 		var sep valueString
 		if s != _undefined {
@@ -657,14 +658,14 @@ func (r *Runtime) typedArrayProto_join(call FunctionCall) Value {
 
 		var buf valueStringBuilder
 
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		element0 := ta.typedArray.get(0)
 		if element0 != nil && element0 != _undefined && element0 != _null {
 			buf.WriteString(element0.toString())
 		}
 
 		for i := 1; i < l; i++ {
-			ta.viewedArrayBuf.ensureNotDetached()
+			ta.viewedArrayBuf.ensureNotDetached(true)
 			buf.WriteString(sep)
 			element := ta.typedArray.get(i)
 			if element != nil && element != _undefined && element != _null {
@@ -679,7 +680,7 @@ func (r *Runtime) typedArrayProto_join(call FunctionCall) Value {
 
 func (r *Runtime) typedArrayProto_keys(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		return r.createArrayIterator(ta.val, iterationKindKey)
 	}
 	panic(r.NewTypeError("Method TypedArray.prototype.keys called on incompatible receiver %s", call.This.String()))
@@ -687,7 +688,7 @@ func (r *Runtime) typedArrayProto_keys(call FunctionCall) Value {
 
 func (r *Runtime) typedArrayProto_lastIndexOf(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		length := int64(ta.length)
 		if length == 0 {
 			return intToValue(-1)
@@ -709,7 +710,7 @@ func (r *Runtime) typedArrayProto_lastIndexOf(call FunctionCall) Value {
 			}
 		}
 
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		searchElement := call.Argument(0)
 		if searchElement == _negativeZero {
 			searchElement = _positiveZero
@@ -730,7 +731,7 @@ func (r *Runtime) typedArrayProto_lastIndexOf(call FunctionCall) Value {
 
 func (r *Runtime) typedArrayProto_map(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		callbackFn := r.toCallable(call.Argument(0))
 		fc := FunctionCall{
 			This:      call.Argument(1),
@@ -738,7 +739,7 @@ func (r *Runtime) typedArrayProto_map(call FunctionCall) Value {
 		}
 		dst := r.typedArraySpeciesCreate(ta, []Value{intToValue(int64(ta.length))})
 		for i := 0; i < ta.length; i++ {
-			ta.viewedArrayBuf.ensureNotDetached()
+			ta.viewedArrayBuf.ensureNotDetached(true)
 			fc.Arguments[0] = ta.typedArray.get(ta.offset + i)
 			fc.Arguments[1] = intToValue(int64(i))
 			dst.typedArray.set(i, callbackFn(fc))
@@ -750,7 +751,7 @@ func (r *Runtime) typedArrayProto_map(call FunctionCall) Value {
 
 func (r *Runtime) typedArrayProto_reduce(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		callbackFn := r.toCallable(call.Argument(0))
 		fc := FunctionCall{
 			This:      _undefined,
@@ -769,7 +770,7 @@ func (r *Runtime) typedArrayProto_reduce(call FunctionCall) Value {
 			panic(r.NewTypeError("Reduce of empty array with no initial value"))
 		}
 		for ; k < ta.length; k++ {
-			ta.viewedArrayBuf.ensureNotDetached()
+			ta.viewedArrayBuf.ensureNotDetached(true)
 			idx := valueInt(k)
 			fc.Arguments[1] = ta.typedArray.get(ta.offset + k)
 			fc.Arguments[2] = idx
@@ -782,7 +783,7 @@ func (r *Runtime) typedArrayProto_reduce(call FunctionCall) Value {
 
 func (r *Runtime) typedArrayProto_reduceRight(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		callbackFn := r.toCallable(call.Argument(0))
 		fc := FunctionCall{
 			This:      _undefined,
@@ -801,7 +802,7 @@ func (r *Runtime) typedArrayProto_reduceRight(call FunctionCall) Value {
 			panic(r.NewTypeError("Reduce of empty array with no initial value"))
 		}
 		for ; k >= 0; k-- {
-			ta.viewedArrayBuf.ensureNotDetached()
+			ta.viewedArrayBuf.ensureNotDetached(true)
 			idx := valueInt(k)
 			fc.Arguments[1] = ta.typedArray.get(ta.offset + k)
 			fc.Arguments[2] = idx
@@ -814,7 +815,7 @@ func (r *Runtime) typedArrayProto_reduceRight(call FunctionCall) Value {
 
 func (r *Runtime) typedArrayProto_reverse(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		l := ta.length
 		middle := l / 2
 		for lower := 0; lower != middle; lower++ {
@@ -834,10 +835,10 @@ func (r *Runtime) typedArrayProto_set(call FunctionCall) Value {
 		if targetOffset < 0 {
 			panic(r.newError(r.global.RangeError, "offset should be >= 0"))
 		}
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		targetLen := ta.length
 		if src, ok := srcObj.self.(*typedArrayObject); ok {
-			src.viewedArrayBuf.ensureNotDetached()
+			src.viewedArrayBuf.ensureNotDetached(true)
 			srcLen := src.length
 			if x := srcLen + targetOffset; x < 0 || x > targetLen {
 				panic(r.newError(r.global.RangeError, "Source is too large"))
@@ -893,7 +894,7 @@ func (r *Runtime) typedArrayProto_set(call FunctionCall) Value {
 			}
 			for i := 0; i < srcLen; i++ {
 				val := nilSafe(srcObj.self.getIdx(valueInt(i), nil))
-				ta.viewedArrayBuf.ensureNotDetached()
+				ta.viewedArrayBuf.ensureNotDetached(true)
 				ta.typedArray.set(targetOffset+i, val)
 			}
 		}
@@ -904,7 +905,7 @@ func (r *Runtime) typedArrayProto_set(call FunctionCall) Value {
 
 func (r *Runtime) typedArrayProto_slice(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		length := int64(ta.length)
 		start := toIntStrict(relToIdx(call.Argument(0).ToInteger(), length))
 		var e int64
@@ -922,14 +923,14 @@ func (r *Runtime) typedArrayProto_slice(call FunctionCall) Value {
 		dst := r.typedArraySpeciesCreate(ta, []Value{intToValue(int64(count))})
 		if dst.defaultCtor == ta.defaultCtor {
 			if count > 0 {
-				ta.viewedArrayBuf.ensureNotDetached()
+				ta.viewedArrayBuf.ensureNotDetached(true)
 				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()
+				ta.viewedArrayBuf.ensureNotDetached(true)
 				dst.typedArray.set(i, ta.typedArray.get(ta.offset+start+i))
 			}
 		}
@@ -940,14 +941,14 @@ func (r *Runtime) typedArrayProto_slice(call FunctionCall) Value {
 
 func (r *Runtime) typedArrayProto_some(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		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()
+			ta.viewedArrayBuf.ensureNotDetached(true)
 			fc.Arguments[0] = ta.typedArray.get(ta.offset + k)
 			fc.Arguments[1] = intToValue(int64(k))
 			if callbackFn(fc).ToBoolean() {
@@ -961,7 +962,7 @@ func (r *Runtime) typedArrayProto_some(call FunctionCall) Value {
 
 func (r *Runtime) typedArrayProto_sort(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		var compareFn func(FunctionCall) Value
 
 		if arg := call.Argument(0); arg != _undefined {
@@ -1004,7 +1005,7 @@ func (r *Runtime) typedArrayProto_toLocaleString(call FunctionCall) Value {
 		length := ta.length
 		var buf valueStringBuilder
 		for i := 0; i < length; i++ {
-			ta.viewedArrayBuf.ensureNotDetached()
+			ta.viewedArrayBuf.ensureNotDetached(true)
 			if i > 0 {
 				buf.WriteRune(',')
 			}
@@ -1018,7 +1019,7 @@ func (r *Runtime) typedArrayProto_toLocaleString(call FunctionCall) Value {
 
 func (r *Runtime) typedArrayProto_values(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
-		ta.viewedArrayBuf.ensureNotDetached()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		return r.createArrayIterator(ta.val, iterationKindValue)
 	}
 	panic(r.NewTypeError("Method TypedArray.prototype.values called on incompatible receiver %s", call.This.String()))
@@ -1070,7 +1071,7 @@ func (r *Runtime) typedArraySpeciesCreate(ta *typedArrayObject, args []Value) *t
 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()
+		ta.viewedArrayBuf.ensureNotDetached(true)
 		if len(args) == 1 {
 			if l, ok := args[0].(valueInt); ok {
 				if ta.length < int(l) {
@@ -1145,7 +1146,7 @@ func (r *Runtime) _newTypedArrayFromArrayBuffer(ab *arrayBufferObject, args []Va
 			panic(r.newError(r.global.RangeError, "Start offset of %s should be a multiple of %d", newTarget.self.getStr("name", nil), ta.elemSize))
 		}
 	}
-	ab.ensureNotDetached()
+	ab.ensureNotDetached(true)
 	var length int
 	if len(args) > 2 && args[2] != nil && args[2] != _undefined {
 		length = r.toIndex(args[2])
@@ -1165,7 +1166,7 @@ func (r *Runtime) _newTypedArrayFromArrayBuffer(ab *arrayBufferObject, args []Va
 
 func (r *Runtime) _newTypedArrayFromTypedArray(src *typedArrayObject, newTarget *Object) *Object {
 	dst := r.typedArrayCreate(newTarget, []Value{_positiveZero})
-	src.viewedArrayBuf.ensureNotDetached()
+	src.viewedArrayBuf.ensureNotDetached(true)
 	l := src.length
 	dst.viewedArrayBuf.prototype = r.getPrototypeFromCtor(r.toObject(src.viewedArrayBuf.getStr("constructor", nil)), r.global.ArrayBuffer, r.global.ArrayBufferPrototype)
 	dst.viewedArrayBuf.data = allocByteSlice(toIntStrict(int64(l) * int64(dst.elemSize)))

+ 26 - 0
builtin_typedarrays_test.go

@@ -304,3 +304,29 @@ func TestInt32ArrayNegativeIndex(t *testing.T) {
 
 	testScript1(SCRIPT, valueTrue, t)
 }
+
+func TestTypedArrayDefineProperty(t *testing.T) {
+	const SCRIPT = `
+  var sample = new Uint8Array([42, 42]);
+
+  assert.sameValue(
+    Reflect.defineProperty(sample, "0", {
+      value: 8,
+      configurable: true,
+      enumerable: true,
+      writable: true
+    }),
+    true
+  );
+
+  assert.sameValue(sample[0], 8, "property value was set");
+  let descriptor0 = Object.getOwnPropertyDescriptor(sample, "0");
+  assert.sameValue(descriptor0.value, 8);
+  assert.sameValue(descriptor0.configurable, true, "configurable");
+  assert.sameValue(descriptor0.enumerable, true);
+  assert.sameValue(descriptor0.writable, true);
+
+	`
+
+	testScript1(TESTLIB+SCRIPT, _undefined, t)
+}

+ 15 - 0
tc39_test.go

@@ -62,6 +62,18 @@ var (
 
 		// Fixed in https://github.com/tc39/test262/commit/7d998a098e5420cb4b6ee4a05eb8c386d750c596
 		"test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/detached-buffer.js": true,
+		// Fixed in https://github.com/tc39/test262/commit/0bb8fe8aba97765aa3a8d4dd8880cd8e3c238f68
+		"test/built-ins/TypedArrayConstructors/internals/Get/detached-buffer.js":                              true,
+		"test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/tonumber-value-detached-buffer.js": true,
+		// 36c2cd165f93e194b9bcad26e69e8571b1d0e6ed
+		"test/built-ins/ArrayBuffer/prototype/byteLength/detached-buffer.js": true,
+
+		// 96aff62fb25cf9ef27929a8ab822ee853d99b06e
+		"test/built-ins/TypedArrayConstructors/internals/Set/tonumber-value-detached-buffer.js": true,
+		"test/built-ins/TypedArrayConstructors/internals/Set/key-is-out-of-bounds.js":           true,
+
+		// 167e596a649ede35df11d03cb3c093941c9cf396
+		"test/built-ins/TypedArrayConstructors/internals/Set/detached-buffer.js": true,
 
 		"test/built-ins/Date/prototype/toISOString/15.9.5.43-0-8.js":  true, // timezone
 		"test/built-ins/Date/prototype/toISOString/15.9.5.43-0-9.js":  true, // timezone
@@ -261,6 +273,7 @@ var (
 		"test/built-ins/Array/prototype/splice/create-species-length-exceeding-integer-limit.js":                   true,
 		"test/built-ins/Array/prototype/slice/length-exceeding-integer-limit-proxied-array.js":                     true,
 		"test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/conversion-operation-consistent-nan.js": true,
+		"test/built-ins/TypedArrayConstructors/internals/Set/conversion-operation-consistent-nan.js":               true,
 
 		// restricted unicode regexp syntax
 		"test/built-ins/RegExp/unicode_restricted_quantifiable_assertion.js":         true,
@@ -423,6 +436,8 @@ var (
 		"sec-functiondeclarations-in-ifstatement-statement-clauses",
 		"sec-evaldeclarationinstantiation",
 		"sec-integer-indexed-exotic-objects-defineownproperty-p-desc",
+		"sec-integer-indexed-exotic-objects-get-p-receiver",
+		"sec-integer-indexed-exotic-objects-set-p-v-receiver",
 		"sec-destructuring-binding-patterns",
 		"sec-runtime-semantics-keyeddestructuringassignmentevaluation",
 	}

+ 38 - 36
typedarrays.go

@@ -449,7 +449,9 @@ func (a *float64Array) typeMatch(v Value) bool {
 
 func (a *typedArrayObject) _getIdx(idx int) Value {
 	if 0 <= idx && idx < a.length {
-		a.viewedArrayBuf.ensureNotDetached()
+		if !a.viewedArrayBuf.ensureNotDetached(false) {
+			return nil
+		}
 		return a.typedArray.get(idx + a.offset)
 	}
 	return nil
@@ -461,9 +463,10 @@ func (a *typedArrayObject) getOwnPropStr(name unistring.String) Value {
 		v := a._getIdx(idx)
 		if v != nil {
 			return &valueProperty{
-				value:      v,
-				writable:   true,
-				enumerable: true,
+				value:        v,
+				writable:     true,
+				enumerable:   true,
+				configurable: true,
 			}
 		}
 		return nil
@@ -489,20 +492,11 @@ func (a *typedArrayObject) getOwnPropIdx(idx valueInt) Value {
 
 func (a *typedArrayObject) getStr(name unistring.String, receiver Value) Value {
 	idx, ok := strToIntNum(name)
-	if ok || idx == 0 {
-		var prop Value
-		if 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
+	if ok {
+		return a._getIdx(idx)
+	}
+	if idx == 0 {
+		return nil
 	}
 	return a.baseObject.getStr(name, receiver)
 }
@@ -520,35 +514,37 @@ func (a *typedArrayObject) getIdx(idx valueInt, receiver Value) Value {
 	return prop
 }
 
-func (a *typedArrayObject) _putIdx(idx int, v Value, throw bool) bool {
+func (a *typedArrayObject) isValidIntegerIndex(idx int, throw bool) bool {
+	return a.viewedArrayBuf.ensureNotDetached(throw) && idx >= 0 && idx < a.length
+}
+
+func (a *typedArrayObject) _putIdx(idx int, v Value) {
 	v = v.ToNumber()
-	if idx >= 0 && idx < a.length {
-		a.viewedArrayBuf.ensureNotDetached()
+	if a.isValidIntegerIndex(idx, false) {
 		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
+	return a.viewedArrayBuf.ensureNotDetached(false) && idx >= 0 && idx < a.length
 }
 
 func (a *typedArrayObject) setOwnStr(p unistring.String, v Value, throw bool) bool {
 	idx, ok := strToIntNum(p)
 	if ok {
-		return a._putIdx(idx, v, throw)
+		a._putIdx(idx, v)
+		return true
 	}
 	if idx == 0 {
+		v.ToNumber() // make sure it throws
 		return false
 	}
 	return a.baseObject.setOwnStr(p, v, throw)
 }
 
 func (a *typedArrayObject) setOwnIdx(p valueInt, v Value, throw bool) bool {
-	return a._putIdx(toIntClamp(int64(p)), v, throw)
+	a._putIdx(toIntClamp(int64(p)), v)
+	return true
 }
 
 func (a *typedArrayObject) setForeignStr(p unistring.String, v, receiver Value, throw bool) (res bool, handled bool) {
@@ -562,8 +558,7 @@ func (a *typedArrayObject) setForeignIdx(p valueInt, v, receiver Value, throw bo
 func (a *typedArrayObject) hasOwnPropertyStr(name unistring.String) bool {
 	idx, ok := strToIntNum(name)
 	if ok {
-		a.viewedArrayBuf.ensureNotDetached()
-		return idx >= 0 && idx < a.length
+		return a.viewedArrayBuf.ensureNotDetached(false) && idx >= 0 && idx < a.length
 	}
 	if idx == 0 {
 		return false
@@ -587,7 +582,11 @@ func (a *typedArrayObject) _defineIdxProperty(idx int, desc PropertyDescriptor,
 	}
 	_, ok := a._defineOwnProperty(unistring.String(strconv.Itoa(idx)), a.getOwnPropIdx(valueInt(idx)), desc, throw)
 	if ok {
-		return a._putIdx(idx, desc.Value, throw)
+		if !a.isValidIntegerIndex(idx, throw) {
+			return false
+		}
+		a._putIdx(idx, desc.Value)
+		return true
 	}
 	return ok
 }
@@ -598,6 +597,7 @@ func (a *typedArrayObject) defineOwnPropertyStr(name unistring.String, desc Prop
 		return a._defineIdxProperty(idx, desc, throw)
 	}
 	if idx == 0 {
+		a.viewedArrayBuf.ensureNotDetached(throw)
 		return false
 	}
 	return a.baseObject.defineOwnPropertyStr(name, desc, throw)
@@ -610,7 +610,7 @@ func (a *typedArrayObject) defineOwnPropertyIdx(name valueInt, desc PropertyDesc
 func (a *typedArrayObject) deleteStr(name unistring.String, throw bool) bool {
 	idx, ok := strToIntNum(name)
 	if ok {
-		if idx >= 0 && idx < a.length {
+		if a.viewedArrayBuf.ensureNotDetached(throw) && idx >= 0 && idx < a.length {
 			a.val.runtime.typeErrorResult(throw, "Cannot delete property '%d' of %s", idx, a.val.String())
 			return false
 		}
@@ -623,7 +623,7 @@ func (a *typedArrayObject) deleteStr(name unistring.String, throw bool) bool {
 }
 
 func (a *typedArrayObject) deleteIdx(idx valueInt, throw bool) bool {
-	if idx >= 0 && int64(idx) < int64(a.length) {
+	if a.viewedArrayBuf.ensureNotDetached(throw) && idx >= 0 && int64(idx) < int64(a.length) {
 		a.val.runtime.typeErrorResult(throw, "Cannot delete property '%d' of %s", idx, a.val.String())
 		return false
 	}
@@ -723,7 +723,7 @@ func (r *Runtime) newFloat64ArrayObject(buf *arrayBufferObject, offset, length i
 
 func (o *dataViewObject) getIdxAndByteOrder(idxVal, littleEndianVal Value, size int) (int, byteOrder) {
 	getIdx := o.val.runtime.toIndex(idxVal)
-	o.viewedArrayBuf.ensureNotDetached()
+	o.viewedArrayBuf.ensureNotDetached(true)
 	if getIdx+size > o.byteLen {
 		panic(o.val.runtime.newError(o.val.runtime.global.RangeError, "Index %d is out of bounds", getIdx))
 	}
@@ -741,10 +741,12 @@ func (o *dataViewObject) getIdxAndByteOrder(idxVal, littleEndianVal Value, size
 	return getIdx, bo
 }
 
-func (o *arrayBufferObject) ensureNotDetached() {
+func (o *arrayBufferObject) ensureNotDetached(throw bool) bool {
 	if o.detached {
-		panic(o.val.runtime.NewTypeError("ArrayBuffer is detached"))
+		o.val.runtime.typeErrorResult(throw, "ArrayBuffer is detached")
+		return false
 	}
+	return true
 }
 
 func (o *arrayBufferObject) getFloat32(idx int, byteOrder byteOrder) float32 {

+ 144 - 7
typedarrays_test.go

@@ -56,13 +56,8 @@ func TestArrayBufferGoWrapper(t *testing.T) {
 		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 (b[0] !== undefined) {
+		throw new Error("b[0] !== undefined");
 	}
 	`)
 	if err != nil {
@@ -146,3 +141,145 @@ func TestTypedArrayIdx(t *testing.T) {
 
 	testScript1(SCRIPT, _undefined, t)
 }
+
+func TestTypedArraySetDetachedBuffer(t *testing.T) {
+	const SCRIPT = `
+	let sample = new Uint8Array([42]);
+	$DETACHBUFFER(sample.buffer);
+	sample[0] = 1;
+
+	assert.sameValue(sample[0], undefined, 'sample[0] = 1 is undefined');
+	sample['1.1'] = 1;
+	assert.sameValue(sample['1.1'], undefined, 'sample[\'1.1\'] = 1 is undefined');
+	sample['-0'] = 1;
+	assert.sameValue(sample['-0'], undefined, 'sample[\'-0\'] = 1 is undefined');
+	sample['-1'] = 1;
+	assert.sameValue(sample['-1'], undefined, 'sample[\'-1\'] = 1 is undefined');
+	sample['1'] = 1;
+	assert.sameValue(sample['1'], undefined, 'sample[\'1\'] = 1 is undefined');
+	sample['2'] = 1;
+	assert.sameValue(sample['2'], undefined, 'sample[\'2\'] = 1 is undefined');	
+	`
+	vm := New()
+	vm.Set("$DETACHBUFFER", func(buf *ArrayBuffer) {
+		buf.Detach()
+	})
+	_, err := vm.RunString(TESTLIB + SCRIPT)
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestTypedArrayDefineDetachedBuffer(t *testing.T) {
+	const SCRIPT = `
+	var desc = {
+	  value: 0,
+	  configurable: false,
+	  enumerable: true,
+	  writable: true
+	};
+	
+	var obj = {
+	  valueOf: function() {
+		throw new Error("valueOf() was called");
+	  }
+	};
+	let sample = new Uint8Array(42);
+	$DETACHBUFFER(sample.buffer);
+	
+	assert.sameValue(
+	Reflect.defineProperty(sample, "0", desc),
+	false,
+	'Reflect.defineProperty(sample, "0", {value: 0, configurable: false, enumerable: true, writable: true} ) must return false'
+	);
+	
+	assert.sameValue(
+	Reflect.defineProperty(sample, "-1", desc),
+	false,
+	'Reflect.defineProperty(sample, "-1", {value: 0, configurable: false, enumerable: true, writable: true} ) must return false'
+	);
+	
+	assert.sameValue(
+	Reflect.defineProperty(sample, "1.1", desc),
+	false,
+	'Reflect.defineProperty(sample, "1.1", {value: 0, configurable: false, enumerable: true, writable: true} ) must return false'
+	);
+	
+	assert.sameValue(
+	Reflect.defineProperty(sample, "-0", desc),
+	false,
+	'Reflect.defineProperty(sample, "-0", {value: 0, configurable: false, enumerable: true, writable: true} ) must return false'
+	);
+	
+	assert.sameValue(
+	Reflect.defineProperty(sample, "2", {
+	  configurable: true,
+	  enumerable: true,
+	  writable: true,
+	  value: obj
+	}),
+	false,
+	'Reflect.defineProperty(sample, "2", {configurable: true, enumerable: true, writable: true, value: obj}) must return false'
+	);
+	
+	assert.sameValue(
+	Reflect.defineProperty(sample, "3", {
+	  configurable: false,
+	  enumerable: false,
+	  writable: true,
+	  value: obj
+	}),
+	false,
+	'Reflect.defineProperty(sample, "3", {configurable: false, enumerable: false, writable: true, value: obj}) must return false'
+	);
+	
+	assert.sameValue(
+	Reflect.defineProperty(sample, "4", {
+	  writable: false,
+	  configurable: false,
+	  enumerable: true,
+	  value: obj
+	}),
+	false,
+	'Reflect.defineProperty("new TA(42)", "4", {writable: false, configurable: false, enumerable: true, value: obj}) must return false'
+	);
+	
+	assert.sameValue(
+	Reflect.defineProperty(sample, "42", desc),
+	false,
+	'Reflect.defineProperty(sample, "42", {value: 0, configurable: false, enumerable: true, writable: true} ) must return false'
+	);
+	
+	assert.sameValue(
+	Reflect.defineProperty(sample, "43", desc),
+	false,
+	'Reflect.defineProperty(sample, "43", {value: 0, configurable: false, enumerable: true, writable: true} ) must return false'
+	);
+	
+	assert.sameValue(
+	Reflect.defineProperty(sample, "5", {
+	  get: function() {}
+	}),
+	false,
+	'Reflect.defineProperty(sample, "5", {get: function() {}}) must return false'
+	);
+	
+	assert.sameValue(
+	Reflect.defineProperty(sample, "6", {
+	  configurable: false,
+	  enumerable: true,
+	  writable: true
+	}),
+	false,
+	'Reflect.defineProperty(sample, "6", {configurable: false, enumerable: true, writable: true}) must return false'
+	);
+	`
+	vm := New()
+	vm.Set("$DETACHBUFFER", func(buf *ArrayBuffer) {
+		buf.Detach()
+	})
+	_, err := vm.RunString(TESTLIB + SCRIPT)
+	if err != nil {
+		t.Fatal(err)
+	}
+}