shiroyk пре 1 година
родитељ
комит
fa6d1ed5e4
20 измењених фајлова са 1306 додато и 126 уклоњено
  1. 369 0
      builtin_bigint.go
  2. 117 0
      builtin_bigint_test.go
  3. 3 1
      builtin_global.go
  4. 8 2
      builtin_json.go
  5. 3 2
      builtin_string.go
  6. 93 4
      builtin_typedarrays.go
  7. 5 1
      compiler_expr.go
  8. 10 1
      compiler_test.go
  9. 3 0
      map_test.go
  10. 23 1
      parser/lexer.go
  11. 17 0
      parser/lexer_test.go
  12. 13 0
      parser/parser_test.go
  13. 33 2
      runtime.go
  14. 1 0
      string.go
  15. 9 0
      string_ascii.go
  16. 0 24
      tc39_test.go
  17. 194 2
      typedarrays.go
  18. 28 0
      typedarrays_test.go
  19. 14 1
      value.go
  20. 363 85
      vm.go

+ 369 - 0
builtin_bigint.go

@@ -0,0 +1,369 @@
+package goja
+
+import (
+	"fmt"
+	"hash/maphash"
+	"math"
+	"math/big"
+	"reflect"
+	"strconv"
+	"sync"
+
+	"github.com/dop251/goja/unistring"
+)
+
+type valueBigInt big.Int
+
+func (v *valueBigInt) ToInteger() int64 {
+	v.ToNumber()
+	return 0
+}
+
+func (v *valueBigInt) toString() String {
+	return asciiString((*big.Int)(v).String())
+}
+
+func (v *valueBigInt) string() unistring.String {
+	return unistring.String(v.String())
+}
+
+func (v *valueBigInt) ToString() Value {
+	return v
+}
+
+func (v *valueBigInt) String() string {
+	return (*big.Int)(v).String()
+}
+
+func (v *valueBigInt) ToFloat() float64 {
+	v.ToNumber()
+	return 0
+}
+
+func (v *valueBigInt) ToNumber() Value {
+	panic(typeError("Cannot convert a BigInt value to a number"))
+}
+
+func (v *valueBigInt) ToBoolean() bool {
+	return (*big.Int)(v).Sign() != 0
+}
+
+func (v *valueBigInt) ToObject(r *Runtime) *Object {
+	return r.newPrimitiveObject(v, r.getBigIntPrototype(), classObject)
+}
+
+func (v *valueBigInt) SameAs(other Value) bool {
+	if o, ok := other.(*valueBigInt); ok {
+		return (*big.Int)(v).Cmp((*big.Int)(o)) == 0
+	}
+	return false
+}
+
+func (v *valueBigInt) Equals(other Value) bool {
+	switch o := other.(type) {
+	case *valueBigInt:
+		return (*big.Int)(v).Cmp((*big.Int)(o)) == 0
+	case valueInt:
+		return (*big.Int)(v).Cmp(big.NewInt(int64(o))) == 0
+	case valueFloat:
+		if IsInfinity(o) || math.IsNaN(float64(o)) {
+			return false
+		}
+		if f := big.NewFloat(float64(o)); f.IsInt() {
+			i, _ := f.Int(nil)
+			return (*big.Int)(v).Cmp(i) == 0
+		}
+		return false
+	case String:
+		bigInt, err := stringToBigInt(o.toTrimmedUTF8())
+		if err != nil {
+			return false
+		}
+		return bigInt.Cmp((*big.Int)(v)) == 0
+	case valueBool:
+		return (*big.Int)(v).Int64() == o.ToInteger()
+	case *Object:
+		return v.Equals(o.toPrimitiveNumber())
+	}
+	return false
+}
+
+func (v *valueBigInt) StrictEquals(other Value) bool {
+	o, ok := other.(*valueBigInt)
+	if ok {
+		return (*big.Int)(v).Cmp((*big.Int)(o)) == 0
+	}
+	return false
+}
+
+func (v *valueBigInt) Export() interface{} {
+	return new(big.Int).Set((*big.Int)(v))
+}
+
+func (v *valueBigInt) ExportType() reflect.Type {
+	return typeBigInt
+}
+
+func (v *valueBigInt) baseObject(rt *Runtime) *Object {
+	return rt.getBigIntPrototype()
+}
+
+func (v *valueBigInt) hash(hash *maphash.Hash) uint64 {
+	var sign byte
+	if (*big.Int)(v).Sign() < 0 {
+		sign = 0x01
+	} else {
+		sign = 0x00
+	}
+	_ = hash.WriteByte(sign)
+	_, _ = hash.Write((*big.Int)(v).Bytes())
+	h := hash.Sum64()
+	hash.Reset()
+	return h
+}
+
+func toBigInt(value Value) *valueBigInt {
+	// Undefined	Throw a TypeError exception.
+	// Null			Throw a TypeError exception.
+	// Boolean		Return 1n if prim is true and 0n if prim is false.
+	// BigInt		Return prim.
+	// Number		Throw a TypeError exception.
+	// String		1. Let n be StringToBigInt(prim).
+	//				2. If n is undefined, throw a SyntaxError exception.
+	//				3. Return n.
+	// Symbol		Throw a TypeError exception.
+	switch prim := value.(type) {
+	case *valueBigInt:
+		return prim
+	case String:
+		bigInt, err := stringToBigInt(prim.toTrimmedUTF8())
+		if err != nil {
+			panic(syntaxError(fmt.Sprintf("Cannot convert %s to a BigInt", prim)))
+		}
+		return (*valueBigInt)(bigInt)
+	case valueBool:
+		return (*valueBigInt)(big.NewInt(prim.ToInteger()))
+	case *Symbol:
+		panic(typeError("Cannot convert Symbol to a BigInt"))
+	case *Object:
+		return toBigInt(prim.toPrimitiveNumber())
+	default:
+		panic(typeError(fmt.Sprintf("Cannot convert %s to a BigInt", prim)))
+	}
+}
+
+func numberToBigInt(v Value) *valueBigInt {
+	switch v := toNumeric(v).(type) {
+	case *valueBigInt:
+		return v
+	case valueInt:
+		return (*valueBigInt)(big.NewInt(v.ToInteger()))
+	case valueFloat:
+		if IsInfinity(v) || math.IsNaN(float64(v)) {
+			panic(rangeError(fmt.Sprintf("Cannot convert %s to a BigInt", v)))
+		}
+		if f := big.NewFloat(float64(v)); f.IsInt() {
+			n, _ := f.Int(nil)
+			return (*valueBigInt)(n)
+		}
+		panic(rangeError(fmt.Sprintf("Cannot convert %s to a BigInt", v)))
+	case *Object:
+		prim := v.toPrimitiveNumber()
+		switch prim.(type) {
+		case valueInt, valueFloat:
+			return numberToBigInt(prim)
+		default:
+			return toBigInt(prim)
+		}
+	default:
+		panic(newTypeError("Cannot convert %s to a BigInt", v))
+	}
+}
+
+func stringToBigInt(str string) (*big.Int, error) {
+	var bigint big.Int
+	n, err := stringToInt(str)
+	if err != nil {
+		switch {
+		case isRangeErr(err):
+			bigint.SetString(str, 0)
+		case err == strconv.ErrSyntax:
+		default:
+			return nil, strconv.ErrSyntax
+		}
+	} else {
+		bigint.SetInt64(n)
+	}
+	return &bigint, nil
+}
+
+func (r *Runtime) thisBigIntValue(value Value) Value {
+	switch t := value.(type) {
+	case *valueBigInt:
+		return t
+	case *Object:
+		switch t := t.self.(type) {
+		case *primitiveValueObject:
+			return r.thisBigIntValue(t.pValue)
+		case *objectGoReflect:
+			if t.exportType() == typeBigInt && t.valueOf != nil {
+				return t.valueOf()
+			}
+		}
+	}
+	panic(r.NewTypeError("requires that 'this' be a BigInt"))
+}
+
+func (r *Runtime) bigintproto_valueOf(call FunctionCall) Value {
+	return r.thisBigIntValue(call.This)
+}
+
+func (r *Runtime) bigintproto_toString(call FunctionCall) Value {
+	x := (*big.Int)(r.thisBigIntValue(call.This).(*valueBigInt))
+	radix := call.Argument(0)
+	var radixMV int
+
+	if radix == _undefined {
+		radixMV = 10
+	} else {
+		radixMV = int(radix.ToInteger())
+		if radixMV < 2 || radixMV > 36 {
+			panic(r.newError(r.getRangeError(), "radix must be an integer between 2 and 36"))
+		}
+	}
+
+	return asciiString(x.Text(radixMV))
+}
+
+func (r *Runtime) bigint_asIntN(call FunctionCall) Value {
+	if len(call.Arguments) < 2 {
+		panic(r.NewTypeError("Cannot convert undefined to a BigInt"))
+	}
+	bits := r.toIndex(call.Argument(0).ToNumber())
+	if bits < 0 {
+		panic(r.NewTypeError("Invalid value: not (convertible to) a safe integer"))
+	}
+	bigint := toBigInt(call.Argument(1))
+
+	twoToBits := new(big.Int).Lsh(big.NewInt(1), uint(bits))
+	mod := new(big.Int).Mod((*big.Int)(bigint), twoToBits)
+	if bits > 0 && mod.Cmp(new(big.Int).Lsh(big.NewInt(1), uint(bits-1))) >= 0 {
+		return (*valueBigInt)(mod.Sub(mod, twoToBits))
+	} else {
+		return (*valueBigInt)(mod)
+	}
+}
+
+func (r *Runtime) bigint_asUintN(call FunctionCall) Value {
+	if len(call.Arguments) < 2 {
+		panic(r.NewTypeError("Cannot convert undefined to a BigInt"))
+	}
+	bits := r.toIndex(call.Argument(0).ToNumber())
+	if bits < 0 {
+		panic(r.NewTypeError("Invalid value: not (convertible to) a safe integer"))
+	}
+	bigint := (*big.Int)(toBigInt(call.Argument(1)))
+	ret := new(big.Int).Mod(bigint, new(big.Int).Lsh(big.NewInt(1), uint(bits)))
+	return (*valueBigInt)(ret)
+}
+
+var bigintTemplate *objectTemplate
+var bigintTemplateOnce sync.Once
+
+func getBigIntTemplate() *objectTemplate {
+	bigintTemplateOnce.Do(func() {
+		bigintTemplate = createBigIntTemplate()
+	})
+	return bigintTemplate
+}
+
+func createBigIntTemplate() *objectTemplate {
+	t := newObjectTemplate()
+	t.protoFactory = func(r *Runtime) *Object {
+		return r.getFunctionPrototype()
+	}
+
+	t.putStr("name", func(r *Runtime) Value { return valueProp(asciiString("BigInt"), false, false, true) })
+	t.putStr("length", func(r *Runtime) Value { return valueProp(intToValue(1), false, false, true) })
+	t.putStr("prototype", func(r *Runtime) Value { return valueProp(r.getBigIntPrototype(), false, false, false) })
+
+	t.putStr("asIntN", func(r *Runtime) Value { return r.methodProp(r.bigint_asIntN, "asIntN", 2) })
+	t.putStr("asUintN", func(r *Runtime) Value { return r.methodProp(r.bigint_asUintN, "asUintN", 2) })
+
+	return t
+}
+
+func (r *Runtime) builtin_BigInt(call FunctionCall) Value {
+	if len(call.Arguments) > 0 {
+		switch v := call.Argument(0).(type) {
+		case *valueBigInt, valueInt, valueFloat, *Object:
+			return numberToBigInt(v)
+		default:
+			return toBigInt(v)
+		}
+	}
+	return (*valueBigInt)(big.NewInt(0))
+}
+
+func (r *Runtime) builtin_newBigInt(args []Value, newTarget *Object) *Object {
+	if newTarget != nil {
+		panic(r.NewTypeError("BigInt is not a constructor"))
+	}
+	var v Value
+	if len(args) > 0 {
+		v = numberToBigInt(args[0])
+	} else {
+		v = (*valueBigInt)(big.NewInt(0))
+	}
+	return r.newPrimitiveObject(v, newTarget, classObject)
+}
+
+func (r *Runtime) getBigInt() *Object {
+	ret := r.global.BigInt
+	if ret == nil {
+		ret = &Object{runtime: r}
+		r.global.BigInt = ret
+		r.newTemplatedFuncObject(getBigIntTemplate(), ret, r.builtin_BigInt,
+			r.wrapNativeConstruct(r.builtin_newBigInt, ret, r.getBigIntPrototype()))
+	}
+	return ret
+}
+
+func createBigIntProtoTemplate() *objectTemplate {
+	t := newObjectTemplate()
+	t.protoFactory = func(r *Runtime) *Object {
+		return r.global.ObjectPrototype
+	}
+
+	t.putStr("length", func(r *Runtime) Value { return valueProp(intToValue(0), false, false, true) })
+	t.putStr("name", func(r *Runtime) Value { return valueProp(asciiString("BigInt"), false, false, true) })
+	t.putStr("constructor", func(r *Runtime) Value { return valueProp(r.getBigInt(), true, false, true) })
+
+	t.putStr("toLocaleString", func(r *Runtime) Value { return r.methodProp(r.bigintproto_toString, "toLocaleString", 0) })
+	t.putStr("toString", func(r *Runtime) Value { return r.methodProp(r.bigintproto_toString, "toString", 0) })
+	t.putStr("valueOf", func(r *Runtime) Value { return r.methodProp(r.bigintproto_valueOf, "valueOf", 0) })
+	t.putSym(SymToStringTag, func(r *Runtime) Value { return valueProp(asciiString("BigInt"), false, false, true) })
+
+	return t
+}
+
+var bigintProtoTemplate *objectTemplate
+var bigintProtoTemplateOnce sync.Once
+
+func getBigIntProtoTemplate() *objectTemplate {
+	bigintProtoTemplateOnce.Do(func() {
+		bigintProtoTemplate = createBigIntProtoTemplate()
+	})
+	return bigintProtoTemplate
+}
+
+func (r *Runtime) getBigIntPrototype() *Object {
+	ret := r.global.BigIntPrototype
+	if ret == nil {
+		ret = &Object{runtime: r}
+		r.global.BigIntPrototype = ret
+		o := r.newTemplatedObject(getBigIntProtoTemplate(), ret)
+		o.class = classObject
+	}
+	return ret
+}

+ 117 - 0
builtin_bigint_test.go

@@ -0,0 +1,117 @@
+package goja
+
+import (
+	"math/big"
+	"testing"
+)
+
+func TestBigInt(t *testing.T) {
+	const SCRIPT = `0xabcdef0123456789abcdef0123n`
+	b := new(big.Int)
+	b.SetString("0xabcdef0123456789abcdef0123", 0)
+	testScript(SCRIPT, (*valueBigInt)(b), t)
+}
+
+func TestBigIntExportTo(t *testing.T) {
+	vm := New()
+
+	t.Run("bigint exportType", func(t *testing.T) {
+		v, err := vm.RunString(`BigInt(Number.MAX_SAFE_INTEGER + 10);`)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if typ := v.ExportType(); typ != typeBigInt {
+			t.Fatal(typ)
+		}
+	})
+
+	t.Run("bigint", func(t *testing.T) {
+		var b big.Int
+		err := vm.ExportTo(vm.ToValue(big.NewInt(10)), &b)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if b.Cmp(big.NewInt(10)) != 0 {
+			t.Fatalf("bigint: %s", b.String())
+		}
+	})
+}
+
+func TestBigIntFormat(t *testing.T) {
+	const SCRIPT = `
+assert.sameValue((1n).toString(undefined), "1", "radius undefined");
+assert.throws(RangeError, () => { (1n).toString(-1); }, "radius -1");
+assert.throws(RangeError, () => { (1n).toString(37); }, "radius 37");
+assert.sameValue((1n).toString(2), "1", "radius 2");
+assert.sameValue((10n).toString(3), "101", "radius 3");
+`
+	testScriptWithTestLib(SCRIPT, _undefined, t)
+}
+
+func TestBigIntOperator(t *testing.T) {
+	const SCRIPT = `
+assert.throws(TypeError, () => { 1 - 1n; }, "mix type add");
+assert.throws(TypeError, () => { 1n - 1; }, "mix type add");
+assert.throws(TypeError, () => { 1n + 1; }, "mix type sub");
+assert.throws(TypeError, () => { 1 + 1n; }, "mix type sub");
+assert.throws(TypeError, () => { 1 * 1n; }, "mix type mul");
+assert.throws(TypeError, () => { 1n * 1; }, "mix type mul");
+assert.throws(TypeError, () => { 1 / 1n; }, "mix type div");
+assert.throws(TypeError, () => { 1n / 1; }, "mix type div");
+assert.throws(TypeError, () => { 1 % 1n; }, "mix type mod");
+assert.throws(TypeError, () => { 1n % 1; }, "mix type mod");
+assert.throws(TypeError, () => { 1n ** 1; }, "mix type exp");
+assert.throws(TypeError, () => { 1 ** 1n; }, "mix type exp");
+assert.throws(TypeError, () => { 1 & 1n; }, "mix type and");
+assert.throws(TypeError, () => { 1n & 1; }, "mix type and");
+assert.throws(TypeError, () => { 1 | 1n; }, "mix type or");
+assert.throws(TypeError, () => { 1n | 1; }, "mix type or");
+assert.throws(TypeError, () => { 1 ^ 1n; }, "mix type xor");
+assert.throws(TypeError, () => { 1n ^ 1; }, "mix type xor");
+assert.throws(TypeError, () => { 1 << 1n; }, "mix type lsh");
+assert.throws(TypeError, () => { 1n << 1; }, "mix type lsh");
+assert.throws(TypeError, () => { 1 >> 1n; }, "mix type rsh");
+assert.throws(TypeError, () => { 1n >> 1; }, "mix type rsh");
+assert.throws(TypeError, () => { 1 >>> 1n; }, "mix type ursh");
+assert.throws(TypeError, () => { 1n >>> 1; }, "mix type ursh");
+
+assert.sameValue(1n + 1n, 2n, "add");
+assert.sameValue(1n - 1n, 0n, "sub");
+assert.sameValue(1n * 2n, 2n, "mul");
+assert.sameValue(1n / 2n, 0n, "div");
+assert.sameValue(1n % 2n, 1n, "mod");
+assert.sameValue(1n ** 2n, 1n, "exp");
+assert.sameValue(1n & 1n, 1n, "and");
+assert.sameValue(1n | 1n, 1n, "or");
+assert.sameValue(2n ^ 1n, 3n, "xor");
+assert.sameValue(1n << 1n, 2n, "lsh");
+assert.sameValue(4n << -1n, 2n, "neg lsh");
+assert.sameValue(4n >> 1n, 2n, "rsh");
+assert.sameValue(2n >> -2n, 8n, "neg rsh");
+
+let a = 1n;
+assert.sameValue(++a, 2n, "inc");
+assert.sameValue(--a, 1n, "dec");
+
+assert.sameValue(Object(1n) - 1n, 0n, "primitive sub");
+assert.sameValue(Object(Object(1n)) - 1n, 0n, "primitive sub");
+assert.sameValue({ [Symbol.toPrimitive]: () => 1n } - 1n, 0n, "primitive sub");
+assert.sameValue({ valueOf: () => 1n } - 1n, 0n, "valueOf sub");
+
+assert.sameValue(1n > 0, true, "gt");
+assert.sameValue(0 > 1n, false, "gt");
+assert.sameValue(Object(1n) > 0, true, "gt");
+assert.sameValue(0 > Object(1n), false, "gt");
+
+assert.sameValue(1n < 0, false, "lt");
+assert.sameValue(0 < 1n, true, "lt");
+assert.sameValue(Object(1n) < 0, false, "lt");
+assert.sameValue(0 < Object(1n), true, "lt");
+
+assert.sameValue(1n >= 0, true, "ge");
+assert.sameValue(0 >= 1n, false, "ge");
+assert.sameValue(1n <= 0, false, "le");
+assert.sameValue(0 <= 1n, true, "le");
+`
+	testScriptWithTestLib(SCRIPT, _undefined, t)
+}

+ 3 - 1
builtin_global.go

@@ -2,7 +2,6 @@ package goja
 
 import (
 	"errors"
-	"github.com/dop251/goja/unistring"
 	"io"
 	"math"
 	"regexp"
@@ -10,6 +9,8 @@ import (
 	"strings"
 	"sync"
 	"unicode/utf8"
+
+	"github.com/dop251/goja/unistring"
 )
 
 const hexUpper = "0123456789ABCDEF"
@@ -339,6 +340,7 @@ func createGlobalObjectTemplate() *objectTemplate {
 	t.putStr("Array", func(r *Runtime) Value { return valueProp(r.getArray(), true, false, true) })
 	t.putStr("String", func(r *Runtime) Value { return valueProp(r.getString(), true, false, true) })
 	t.putStr("Number", func(r *Runtime) Value { return valueProp(r.getNumber(), true, false, true) })
+	t.putStr("BigInt", func(r *Runtime) Value { return valueProp(r.getBigInt(), true, false, true) })
 	t.putStr("RegExp", func(r *Runtime) Value { return valueProp(r.getRegExp(), true, false, true) })
 	t.putStr("Date", func(r *Runtime) Value { return valueProp(r.getDate(), true, false, true) })
 	t.putStr("Boolean", func(r *Runtime) Value { return valueProp(r.getBoolean(), true, false, true) })

+ 8 - 2
builtin_json.go

@@ -281,8 +281,9 @@ func (ctx *_builtinJSON_stringifyContext) do(v Value) bool {
 func (ctx *_builtinJSON_stringifyContext) str(key Value, holder *Object) bool {
 	value := nilSafe(holder.get(key, nil))
 
-	if object, ok := value.(*Object); ok {
-		if toJSON, ok := object.self.getStr("toJSON", nil).(*Object); ok {
+	switch value.(type) {
+	case *Object, *valueBigInt:
+		if toJSON, ok := ctx.r.getVStr(value, "toJSON").(*Object); ok {
 			if c, ok := toJSON.self.assertCallable(); ok {
 				value = c(FunctionCall{
 					This:      value,
@@ -334,6 +335,9 @@ func (ctx *_builtinJSON_stringifyContext) str(key Value, holder *Object) bool {
 						value = valueFalse
 					}
 				}
+				if o1.exportType() == typeBigInt {
+					value = o1.val.ordinaryToPrimitiveNumber()
+				}
 			}
 		}
 	}
@@ -357,6 +361,8 @@ func (ctx *_builtinJSON_stringifyContext) str(key Value, holder *Object) bool {
 		}
 	case valueNull:
 		ctx.buf.WriteString("null")
+	case *valueBigInt:
+		ctx.r.typeErrorResult(true, "Do not know how to serialize a BigInt")
 	case *Object:
 		for _, object := range ctx.stack {
 			if value1.SameAs(object) {

+ 3 - 2
builtin_string.go

@@ -1,13 +1,14 @@
 package goja
 
 import (
-	"github.com/dop251/goja/unistring"
 	"math"
 	"strings"
 	"sync"
 	"unicode/utf16"
 	"unicode/utf8"
 
+	"github.com/dop251/goja/unistring"
+
 	"github.com/dop251/goja/parser"
 	"golang.org/x/text/collate"
 	"golang.org/x/text/language"
@@ -332,7 +333,7 @@ func (r *Runtime) stringproto_indexOf(call FunctionCall) Value {
 	r.checkObjectCoercible(call.This)
 	value := call.This.toString()
 	target := call.Argument(0).toString()
-	pos := call.Argument(1).ToInteger()
+	pos := call.Argument(1).ToNumber().ToInteger()
 
 	if pos < 0 {
 		pos = 0

+ 93 - 4
builtin_typedarrays.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"math"
 	"sort"
+	"strings"
 	"sync"
 	"unsafe"
 
@@ -290,6 +291,20 @@ func (r *Runtime) dataViewProto_getUint32(call FunctionCall) Value {
 	panic(r.NewTypeError("Method DataView.prototype.getUint32 called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: call.This})))
 }
 
+func (r *Runtime) dataViewProto_getBigInt64(call FunctionCall) Value {
+	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		return (*valueBigInt)(dv.viewedArrayBuf.getBigInt64(dv.getIdxAndByteOrder(r.toIndex(call.Argument(0).ToNumber()), call.Argument(1), 8)))
+	}
+	panic(r.NewTypeError("Method DataView.prototype.getBigInt64 called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: call.This})))
+}
+
+func (r *Runtime) dataViewProto_getBigUint64(call FunctionCall) Value {
+	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		return (*valueBigInt)(dv.viewedArrayBuf.getBigUint64(dv.getIdxAndByteOrder(r.toIndex(call.Argument(0).ToNumber()), call.Argument(1), 8)))
+	}
+	panic(r.NewTypeError("Method DataView.prototype.getBigUint64 called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: call.This})))
+}
+
 func (r *Runtime) dataViewProto_setFloat32(call FunctionCall) Value {
 	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
 		idxVal := r.toIndex(call.Argument(0))
@@ -378,6 +393,28 @@ func (r *Runtime) dataViewProto_setUint32(call FunctionCall) Value {
 	panic(r.NewTypeError("Method DataView.prototype.setUint32 called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: call.This})))
 }
 
+func (r *Runtime) dataViewProto_setBigInt64(call FunctionCall) Value {
+	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		idxVal := r.toIndex(call.Argument(0))
+		val := toBigInt64(call.Argument(1))
+		idx, bo := dv.getIdxAndByteOrder(idxVal, call.Argument(2), 8)
+		dv.viewedArrayBuf.setBigInt64(idx, val, bo)
+		return _undefined
+	}
+	panic(r.NewTypeError("Method DataView.prototype.setBigInt64 called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: call.This})))
+}
+
+func (r *Runtime) dataViewProto_setBigUint64(call FunctionCall) Value {
+	if dv, ok := r.toObject(call.This).self.(*dataViewObject); ok {
+		idxVal := r.toIndex(call.Argument(0))
+		val := toBigUint64(call.Argument(1))
+		idx, bo := dv.getIdxAndByteOrder(idxVal, call.Argument(2), 8)
+		dv.viewedArrayBuf.setBigUint64(idx, val, bo)
+		return _undefined
+	}
+	panic(r.NewTypeError("Method DataView.prototype.setBigUint64 called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: call.This})))
+}
+
 func (r *Runtime) typedArrayProto_getBuffer(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
 		return ta.viewedArrayBuf.val
@@ -975,6 +1012,7 @@ func (r *Runtime) typedArrayProto_set(call FunctionCall) Value {
 				copy(ta.viewedArrayBuf.data[(ta.offset+targetOffset)*ta.elemSize:],
 					src.viewedArrayBuf.data[src.offset*src.elemSize:(src.offset+srcLen)*src.elemSize])
 			} else {
+				checkTypedArrayMixBigInt(src.defaultCtor, ta.defaultCtor)
 				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]))
@@ -1173,6 +1211,7 @@ func (r *Runtime) typedArrayProto_with(call FunctionCall) Value {
 	if !ok {
 		panic(r.NewTypeError("%s is not a valid TypedArray", r.objectproto_toString(FunctionCall{This: call.This})))
 	}
+	ta.viewedArrayBuf.ensureNotDetached(true)
 	length := ta.length
 	relativeIndex := call.Argument(0).ToInteger()
 	var actualIndex int
@@ -1186,10 +1225,13 @@ func (r *Runtime) typedArrayProto_with(call FunctionCall) Value {
 		panic(r.newError(r.getRangeError(), "Invalid typed array index"))
 	}
 
-	// TODO BigInt
-	// 7. If O.[[ContentType]] is BIGINT, let numericValue be ? ToBigInt(value).
-	// 8. Else, let numericValue be ? ToNumber(value).
-	numericValue := call.Argument(1).ToNumber()
+	var numericValue Value
+	switch ta.typedArray.(type) {
+	case *bigInt64Array, *bigUint64Array:
+		numericValue = toBigInt(call.Argument(1))
+	default:
+		numericValue = call.Argument(1).ToNumber()
+	}
 
 	a := r.typedArrayCreate(ta.defaultCtor, intToValue(int64(length)))
 	for k := 0; k < length; k++ {
@@ -1210,6 +1252,7 @@ func (r *Runtime) typedArrayProto_toReversed(call FunctionCall) Value {
 	if !ok {
 		panic(r.NewTypeError("%s is not a valid TypedArray", r.objectproto_toString(FunctionCall{This: call.This})))
 	}
+	ta.viewedArrayBuf.ensureNotDetached(true)
 	length := ta.length
 
 	a := r.typedArrayCreate(ta.defaultCtor, intToValue(int64(length)))
@@ -1229,6 +1272,7 @@ func (r *Runtime) typedArrayProto_toSorted(call FunctionCall) Value {
 	if !ok {
 		panic(r.NewTypeError("%s is not a valid TypedArray", r.objectproto_toString(FunctionCall{This: call.This})))
 	}
+	ta.viewedArrayBuf.ensureNotDetached(true)
 
 	var compareFn func(FunctionCall) Value
 	arg := call.Argument(0)
@@ -1426,6 +1470,15 @@ func (r *Runtime) _newTypedArrayFromArrayBuffer(ab *arrayBufferObject, args []Va
 	return ta.val
 }
 
+func checkTypedArrayMixBigInt(src, dst *Object) {
+	srcType := src.self.getStr("name", nil).String()
+	if strings.HasPrefix(srcType, "Big") {
+		if !strings.HasPrefix(dst.self.getStr("name", nil).String(), "Big") {
+			panic(errMixBigIntType)
+		}
+	}
+}
+
 func (r *Runtime) _newTypedArrayFromTypedArray(src *typedArrayObject, newTarget *Object, taCtor typedArrayObjectCtor, proto *Object) *Object {
 	dst := r.allocateTypedArray(newTarget, 0, taCtor, proto)
 	src.viewedArrayBuf.ensureNotDetached(true)
@@ -1437,6 +1490,8 @@ func (r *Runtime) _newTypedArrayFromTypedArray(src *typedArrayObject, newTarget
 		copy(dst.viewedArrayBuf.data, src.viewedArrayBuf.data[src.offset*src.elemSize:])
 		dst.length = src.length
 		return dst.val
+	} else {
+		checkTypedArrayMixBigInt(src.defaultCtor, newTarget)
 	}
 	dst.length = l
 	for i := 0; i < l; i++ {
@@ -1506,6 +1561,14 @@ func (r *Runtime) newFloat64Array(args []Value, newTarget, proto *Object) *Objec
 	return r._newTypedArray(args, newTarget, r.newFloat64ArrayObject, proto)
 }
 
+func (r *Runtime) newBigInt64Array(args []Value, newTarget, proto *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newBigInt64ArrayObject, proto)
+}
+
+func (r *Runtime) newBigUint64Array(args []Value, newTarget, proto *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newBigUint64ArrayObject, proto)
+}
+
 func (r *Runtime) createArrayBufferProto(val *Object) objectImpl {
 	b := newBaseObjectObj(val, r.global.ObjectPrototype, classObject)
 	byteLengthProp := &valueProperty{
@@ -1578,6 +1641,8 @@ func addTypedArrays(t *objectTemplate) {
 	t.putStr("Int32Array", func(r *Runtime) Value { return valueProp(r.getInt32Array(), true, false, true) })
 	t.putStr("Float32Array", func(r *Runtime) Value { return valueProp(r.getFloat32Array(), true, false, true) })
 	t.putStr("Float64Array", func(r *Runtime) Value { return valueProp(r.getFloat64Array(), true, false, true) })
+	t.putStr("BigInt64Array", func(r *Runtime) Value { return valueProp(r.getBigInt64Array(), true, false, true) })
+	t.putStr("BigUint64Array", func(r *Runtime) Value { return valueProp(r.getBigUint64Array(), true, false, true) })
 }
 
 func createTypedArrayProtoTemplate() *objectTemplate {
@@ -1781,6 +1846,26 @@ func (r *Runtime) getFloat64Array() *Object {
 	return ret
 }
 
+func (r *Runtime) getBigInt64Array() *Object {
+	ret := r.global.BigInt64Array
+	if ret == nil {
+		ret = &Object{runtime: r}
+		r.global.BigInt64Array = ret
+		r.createTypedArrayCtor(ret, r.newBigInt64Array, "BigInt64Array", 8)
+	}
+	return ret
+}
+
+func (r *Runtime) getBigUint64Array() *Object {
+	ret := r.global.BigUint64Array
+	if ret == nil {
+		ret = &Object{runtime: r}
+		r.global.BigUint64Array = ret
+		r.createTypedArrayCtor(ret, r.newBigUint64Array, "BigUint64Array", 8)
+	}
+	return ret
+}
+
 func createDataViewProtoTemplate() *objectTemplate {
 	t := newObjectTemplate()
 	t.protoFactory = func(r *Runtime) *Object {
@@ -1819,6 +1904,8 @@ func createDataViewProtoTemplate() *objectTemplate {
 	t.putStr("getUint8", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_getUint8, "getUint8", 1) })
 	t.putStr("getUint16", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_getUint16, "getUint16", 1) })
 	t.putStr("getUint32", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_getUint32, "getUint32", 1) })
+	t.putStr("getBigInt64", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_getBigInt64, "getBigInt64", 1) })
+	t.putStr("getBigUint64", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_getBigUint64, "getBigUint64", 1) })
 	t.putStr("setFloat32", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_setFloat32, "setFloat32", 2) })
 	t.putStr("setFloat64", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_setFloat64, "setFloat64", 2) })
 	t.putStr("setInt8", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_setInt8, "setInt8", 2) })
@@ -1827,6 +1914,8 @@ func createDataViewProtoTemplate() *objectTemplate {
 	t.putStr("setUint8", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_setUint8, "setUint8", 2) })
 	t.putStr("setUint16", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_setUint16, "setUint16", 2) })
 	t.putStr("setUint32", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_setUint32, "setUint32", 2) })
+	t.putStr("setBigInt64", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_setBigInt64, "setBigInt64", 2) })
+	t.putStr("setBigUint64", func(r *Runtime) Value { return r.methodProp(r.dataViewProto_setBigUint64, "setBigUint64", 2) })
 
 	t.putSym(SymToStringTag, func(r *Runtime) Value { return valueProp(asciiString("DataView"), false, false, true) })
 

+ 5 - 1
compiler_expr.go

@@ -1,6 +1,8 @@
 package goja
 
 import (
+	"math/big"
+
 	"github.com/dop251/goja/ast"
 	"github.com/dop251/goja/file"
 	"github.com/dop251/goja/token"
@@ -2446,7 +2448,7 @@ func (c *compiler) emitThrow(v Value) {
 	if o, ok := v.(*Object); ok {
 		t := nilSafe(o.self.getStr("name", nil)).toString().String()
 		switch t {
-		case "TypeError":
+		case "TypeError", "RangeError":
 			c.emit(loadDynamic(t))
 			msg := o.self.getStr("message", nil)
 			if msg != nil {
@@ -3228,6 +3230,8 @@ func (c *compiler) compileNumberLiteral(v *ast.NumberLiteral) compiledExpr {
 		val = intToValue(num)
 	case float64:
 		val = floatToValue(num)
+	case *big.Int:
+		val = (*valueBigInt)(num)
 	default:
 		c.assert(false, int(v.Idx)-1, "Unsupported number literal type: %T", v.Value)
 		panic("unreachable")

+ 10 - 1
compiler_test.go

@@ -14,9 +14,18 @@ function $ERROR(message) {
 	throw new Error(message);
 }
 
-function Test262Error() {
+function Test262Error(message) {
+  this.message = message || "";
 }
 
+Test262Error.prototype.toString = function () {
+  return "Test262Error: " + this.message;
+};
+
+Test262Error.thrower = (message) => {
+  throw new Test262Error(message);
+};
+
 function assert(mustBeTrue, message) {
     if (mustBeTrue === true) {
         return;

+ 3 - 0
map_test.go

@@ -3,6 +3,7 @@ package goja
 import (
 	"hash/maphash"
 	"math"
+	"math/big"
 	"strconv"
 	"testing"
 )
@@ -25,6 +26,8 @@ func TestMapHash(t *testing.T) {
 	testMapHashVal(floatToValue(1.2345), floatToValue(1.2345), true, t)
 	testMapHashVal(SymIterator, SymToStringTag, false, t)
 	testMapHashVal(SymIterator, SymIterator, true, t)
+	testMapHashVal((*valueBigInt)(big.NewInt(1)), (*valueBigInt)(big.NewInt(-1)), false, t)
+	testMapHashVal((*valueBigInt)(big.NewInt(1)), (*valueBigInt)(big.NewInt(1)), true, t)
 
 	// The following tests introduce indeterministic behaviour
 	//testMapHashVal(asciiString("Test"), asciiString("Test1"), false, t)

+ 23 - 1
parser/lexer.go

@@ -3,6 +3,7 @@ package parser
 import (
 	"errors"
 	"fmt"
+	"math/big"
 	"strconv"
 	"strings"
 	"unicode"
@@ -911,7 +912,9 @@ func parseNumberLiteral(literal string) (value interface{}, err error) {
 	err = parseIntErr
 
 	if err.(*strconv.NumError).Err == strconv.ErrRange {
-		if len(literal) > 2 && literal[0] == '0' && (literal[1] == 'X' || literal[1] == 'x') {
+		if len(literal) > 2 &&
+			literal[0] == '0' && (literal[1] == 'X' || literal[1] == 'x') &&
+			literal[len(literal)-1] != 'n' {
 			// Could just be a very large number (e.g. 0x8000000000000000)
 			var value float64
 			literal = literal[2:]
@@ -926,6 +929,21 @@ func parseNumberLiteral(literal string) (value interface{}, err error) {
 		}
 	}
 
+	if len(literal) > 1 && literal[len(literal)-1] == 'n' {
+		if literal[0] == '0' {
+			if len(literal) > 2 && isDecimalDigit(rune(literal[1])) {
+				goto error
+			}
+		}
+		// Parse as big.Int
+		bigInt := new(big.Int)
+		_, ok := bigInt.SetString(literal[:len(literal)-1], 0)
+		if !ok {
+			goto error
+		}
+		return bigInt, nil
+	}
+
 error:
 	return nil, errors.New("Illegal numeric literal")
 }
@@ -1171,6 +1189,10 @@ func (self *_parser) scanNumericLiteral(decimalPoint bool) (token.Token, string)
 		}
 	}
 end:
+	if self.chr == 'n' || self.chr == 'N' {
+		self.read()
+		return tkn, self.str[offset:self.chrOffset]
+	}
 	if isIdentifierStart(self.chr) || isDecimalDigit(self.chr) {
 		return token.ILLEGAL, self.str[offset:self.chrOffset]
 	}

+ 17 - 0
parser/lexer_test.go

@@ -264,6 +264,23 @@ Second line \
 			token.NUMBER, "12.3", 5,
 		)
 
+		test(`1n`,
+			token.NUMBER, "1n", 1,
+		)
+
+		test(`1n 9007199254740991n`,
+			token.NUMBER, "1n", 1,
+			token.NUMBER, "9007199254740991n", 4,
+		)
+
+		test(`0xabn`,
+			token.NUMBER, "0xabn", 1,
+		)
+
+		test(`0xabcdef0123456789abcdef0123n`,
+			token.NUMBER, "0xabcdef0123456789abcdef0123n", 1,
+		)
+
 		test("/ /=",
 			token.SLASH, "", 1,
 			token.QUOTIENT_ASSIGN, "", 3,

+ 13 - 0
parser/parser_test.go

@@ -2,6 +2,7 @@ package parser
 
 import (
 	"errors"
+	"math/big"
 	"regexp"
 	"strings"
 	"testing"
@@ -1041,6 +1042,18 @@ func Test_parseNumberLiteral(t *testing.T) {
 		test("0", 0)
 
 		test("0x8000000000000000", float64(9.223372036854776e+18))
+
+		test("1n", big.NewInt(1))
+
+		test("-1n", big.NewInt(-1))
+
+		test("0x23n", big.NewInt(35))
+
+		test("0xabcdef01n", big.NewInt(2882400001))
+
+		var n big.Int
+		n.SetString("0xabcdef0123456789abcdef0123", 0)
+		test("0xabcdef0123456789abcdef0123n", &n)
 	})
 }
 

+ 33 - 2
runtime.go

@@ -7,6 +7,7 @@ import (
 	"go/ast"
 	"hash/maphash"
 	"math"
+	"math/big"
 	"math/bits"
 	"math/rand"
 	"reflect"
@@ -33,6 +34,7 @@ var (
 	typeValue    = reflect.TypeOf((*Value)(nil)).Elem()
 	typeObject   = reflect.TypeOf((*Object)(nil))
 	typeTime     = reflect.TypeOf(time.Time{})
+	typeBigInt   = reflect.TypeOf((*big.Int)(nil))
 	typeBytes    = reflect.TypeOf(([]byte)(nil))
 )
 
@@ -52,6 +54,7 @@ type global struct {
 	Function *Object
 	String   *Object
 	Number   *Object
+	BigInt   *Object
 	Boolean  *Object
 	RegExp   *Object
 	Date     *Object
@@ -76,6 +79,8 @@ type global struct {
 	Int32Array        *Object
 	Float32Array      *Object
 	Float64Array      *Object
+	BigInt64Array     *Object
+	BigUint64Array    *Object
 
 	WeakSet *Object
 	WeakMap *Object
@@ -96,6 +101,7 @@ type global struct {
 	ObjectPrototype   *Object
 	ArrayPrototype    *Object
 	NumberPrototype   *Object
+	BigIntPrototype   *Object
 	StringPrototype   *Object
 	BooleanPrototype  *Object
 	FunctionPrototype *Object
@@ -829,7 +835,18 @@ func (r *Runtime) newPrimitiveObject(value Value, proto *Object, class string) *
 
 func (r *Runtime) builtin_Number(call FunctionCall) Value {
 	if len(call.Arguments) > 0 {
-		return call.Arguments[0].ToNumber()
+		switch t := call.Arguments[0].(type) {
+		case *Object:
+			primValue := t.toPrimitiveNumber()
+			if bigint, ok := primValue.(*valueBigInt); ok {
+				return intToValue((*big.Int)(bigint).Int64())
+			}
+			return primValue.ToNumber()
+		case *valueBigInt:
+			return intToValue((*big.Int)(t).Int64())
+		default:
+			return t.ToNumber()
+		}
 	} else {
 		return valueInt(0)
 	}
@@ -838,7 +855,19 @@ func (r *Runtime) builtin_Number(call FunctionCall) Value {
 func (r *Runtime) builtin_newNumber(args []Value, proto *Object) *Object {
 	var v Value
 	if len(args) > 0 {
-		v = args[0].ToNumber()
+		switch t := args[0].(type) {
+		case *Object:
+			primValue := t.toPrimitiveNumber()
+			if bigint, ok := primValue.(*valueBigInt); ok {
+				v = intToValue((*big.Int)(bigint).Int64())
+			} else {
+				v = primValue.ToNumber()
+			}
+		case *valueBigInt:
+			v = intToValue((*big.Int)(t).Int64())
+		default:
+			v = t.ToNumber()
+		}
 	} else {
 		v = intToValue(0)
 	}
@@ -1831,6 +1860,8 @@ func (r *Runtime) toValue(i interface{}, origValue reflect.Value) Value {
 		return floatToValue(float64(i))
 	case float64:
 		return floatToValue(i)
+	case *big.Int:
+		return (*valueBigInt)(new(big.Int).Set(i))
 	case map[string]interface{}:
 		if i == nil {
 			return _null

+ 1 - 0
string.go

@@ -24,6 +24,7 @@ var (
 	stringString      String = asciiString("string")
 	stringSymbol      String = asciiString("symbol")
 	stringNumber      String = asciiString("number")
+	stringBigInt      String = asciiString("bigint")
 	stringNaN         String = asciiString("NaN")
 	stringInfinity           = asciiString("Infinity")
 	stringNegInfinity        = asciiString("-Infinity")

+ 9 - 0
string_ascii.go

@@ -4,6 +4,7 @@ import (
 	"hash/maphash"
 	"io"
 	"math"
+	"math/big"
 	"reflect"
 	"strconv"
 	"strings"
@@ -246,6 +247,14 @@ func (s asciiString) Equals(other Value) bool {
 		return false
 	}
 
+	if o, ok := other.(*valueBigInt); ok {
+		bigInt, err := stringToBigInt(s.toTrimmedUTF8())
+		if err != nil {
+			return false
+		}
+		return bigInt.Cmp((*big.Int)(o)) == 0
+	}
+
 	if o, ok := other.(*Object); ok {
 		return s.Equals(o.toPrimitive())
 	}

+ 0 - 24
tc39_test.go

@@ -180,25 +180,6 @@ var (
 		"test/language/expressions/class/cpn-class-expr-computed-property-name-from-integer-separators.js":                true,
 		"test/language/expressions/class/cpn-class-expr-fields-methods-computed-property-name-from-integer-separators.js": true,
 
-		// BigInt
-		"test/built-ins/Object/seal/seal-biguint64array.js":                        true,
-		"test/built-ins/Object/seal/seal-bigint64array.js":                         true,
-		"test/built-ins/Array/prototype/toSorted/comparefn-not-a-function.js":      true,
-		"test/built-ins/TypedArray/prototype/toReversed/this-value-invalid.js":     true,
-		"test/built-ins/TypedArray/prototype/toSorted/comparefn-not-a-function.js": true,
-		"test/built-ins/TypedArray/prototype/toSorted/this-value-invalid.js":       true,
-		"test/built-ins/RegExp/prototype/sticky/this-val-non-obj.js":               true,
-		"test/built-ins/RegExp/prototype/source/this-val-non-obj.js":               true,
-		"test/built-ins/RegExp/prototype/multiline/this-val-non-obj.js":            true,
-		"test/built-ins/RegExp/prototype/ignoreCase/this-val-non-obj.js":           true,
-		"test/built-ins/RegExp/prototype/unicode/this-val-non-obj.js":              true,
-		"test/built-ins/RegExp/prototype/dotAll/this-val-non-obj.js":               true,
-		"test/built-ins/RegExp/prototype/global/this-val-non-obj.js":               true,
-		"test/built-ins/RegExp/prototype/flags/this-val-non-obj.js":                true,
-		"test/built-ins/Iterator/prototype/Symbol.iterator/return-val.js":          true,
-		"test/built-ins/DataView/prototype/setBigUint64/not-a-constructor.js":      true,
-		"test/built-ins/DataView/prototype/getBigUint64/not-a-constructor.js":      true,
-
 		// Regexp
 		"test/language/literals/regexp/invalid-range-negative-lookbehind.js":    true,
 		"test/language/literals/regexp/invalid-range-lookbehind.js":             true,
@@ -227,7 +208,6 @@ var (
 	featuresBlackList = []string{
 		"async-iteration",
 		"Symbol.asyncIterator",
-		"BigInt",
 		"resizable-arraybuffer",
 		"regexp-named-groups",
 		"regexp-duplicate-named-groups",
@@ -327,10 +307,6 @@ func init() {
 
 		"test/language/eval-code/direct/async-gen-",
 
-		// BigInt
-		"test/built-ins/TypedArrayConstructors/BigUint64Array/",
-		"test/built-ins/TypedArrayConstructors/BigInt64Array/",
-
 		// restricted unicode regexp syntax
 		"test/language/literals/regexp/u-",
 

+ 194 - 2
typedarrays.go

@@ -2,6 +2,7 @@ package goja
 
 import (
 	"math"
+	"math/big"
 	"reflect"
 	"strconv"
 	"unsafe"
@@ -65,6 +66,8 @@ type uint32Array []byte
 type int32Array []byte
 type float32Array []byte
 type float64Array []byte
+type bigInt64Array []byte
+type bigUint64Array []byte
 
 type typedArrayObject struct {
 	baseObject
@@ -630,6 +633,136 @@ func (a *float64Array) exportType() reflect.Type {
 	return typeFloat64Array
 }
 
+func (a *bigInt64Array) toRaw(value Value) uint64 {
+	return toBigInt64(value).Uint64()
+}
+
+func (a *bigInt64Array) ptr(idx int) *int64 {
+	p := unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(a)).Data)
+	return (*int64)(unsafe.Pointer(uintptr(p) + uintptr(idx)*8))
+}
+
+func (a *bigInt64Array) get(idx int) Value {
+	return (*valueBigInt)(big.NewInt(*a.ptr(idx)))
+}
+
+func toBigInt64(v Value) *big.Int {
+	n := (*big.Int)(toBigInt(v))
+
+	twoTo64 := new(big.Int).Lsh(big.NewInt(1), 64)
+	twoTo63 := new(big.Int).Lsh(big.NewInt(1), 63)
+
+	int64bit := new(big.Int).Mod(n, twoTo64)
+	if int64bit.Cmp(twoTo63) >= 0 {
+		return int64bit.Sub(int64bit, twoTo64)
+	}
+	return int64bit
+}
+
+func (a *bigInt64Array) set(idx int, value Value) {
+	*(a.ptr(idx)) = toBigInt64(value).Int64()
+}
+
+func (a *bigInt64Array) getRaw(idx int) uint64 {
+	return uint64(*a.ptr(idx))
+}
+
+func (a *bigInt64Array) setRaw(idx int, raw uint64) {
+	*(a.ptr(idx)) = int64(raw)
+}
+
+func (a *bigInt64Array) less(i, j int) bool {
+	return *(a.ptr(i)) < *(a.ptr(j))
+}
+
+func (a *bigInt64Array) swap(i, j int) {
+	pi, pj := a.ptr(i), a.ptr(j)
+	*pi, *pj = *pj, *pi
+}
+
+func (a *bigInt64Array) typeMatch(v Value) bool {
+	if _, ok := v.(*valueBigInt); ok {
+		return true
+	}
+	return false
+}
+
+func (a *bigInt64Array) export(offset int, length int) interface{} {
+	var res []int64
+	sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&res))
+	sliceHeader.Data = (*reflect.SliceHeader)(unsafe.Pointer(a)).Data + uintptr(offset)*8
+	sliceHeader.Len = length
+	sliceHeader.Cap = length
+	return res
+}
+
+var typeBigInt64Array = reflect.TypeOf(([]int64)(nil))
+
+func (a *bigInt64Array) exportType() reflect.Type {
+	return typeBigInt64Array
+}
+
+func (a *bigUint64Array) toRaw(value Value) uint64 {
+	return toBigUint64(value).Uint64()
+}
+
+func (a *bigUint64Array) ptr(idx int) *uint64 {
+	p := unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(a)).Data)
+	return (*uint64)(unsafe.Pointer(uintptr(p) + uintptr(idx)*8))
+}
+
+func (a *bigUint64Array) get(idx int) Value {
+	return (*valueBigInt)(new(big.Int).SetUint64(*a.ptr(idx)))
+}
+
+func toBigUint64(v Value) *big.Int {
+	n := (*big.Int)(toBigInt(v))
+	return new(big.Int).Mod(n, new(big.Int).Lsh(big.NewInt(1), 64))
+}
+
+func (a *bigUint64Array) set(idx int, value Value) {
+	*(a.ptr(idx)) = toBigUint64(value).Uint64()
+}
+
+func (a *bigUint64Array) getRaw(idx int) uint64 {
+	return *a.ptr(idx)
+}
+
+func (a *bigUint64Array) setRaw(idx int, raw uint64) {
+	*(a.ptr(idx)) = raw
+}
+
+func (a *bigUint64Array) less(i, j int) bool {
+	return *(a.ptr(i)) < *(a.ptr(j))
+}
+
+func (a *bigUint64Array) swap(i, j int) {
+	pi, pj := a.ptr(i), a.ptr(j)
+	*pi, *pj = *pj, *pi
+}
+
+func (a *bigUint64Array) typeMatch(v Value) bool {
+	if _, ok := v.(*valueBigInt); ok {
+		return true
+	}
+	return false
+}
+
+func (a *bigUint64Array) export(offset int, length int) interface{} {
+	var res []uint64
+	sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&res))
+	sliceHeader.Data = (*reflect.SliceHeader)(unsafe.Pointer(a)).Data + uintptr(offset)*8
+	sliceHeader.Len = length
+	sliceHeader.Cap = length
+	return res
+}
+
+var typeBigUint64Array = reflect.TypeOf(([]uint64)(nil))
+
+func (a *bigUint64Array) exportType() reflect.Type {
+	return typeBigUint64Array
+}
+
 func (a *typedArrayObject) _getIdx(idx int) Value {
 	if 0 <= idx && idx < a.length {
 		if !a.viewedArrayBuf.ensureNotDetached(false) {
@@ -698,7 +831,12 @@ func (a *typedArrayObject) isValidIntegerIndex(idx int) bool {
 }
 
 func (a *typedArrayObject) _putIdx(idx int, v Value) {
-	v = v.ToNumber()
+	switch a.typedArray.(type) {
+	case *bigInt64Array, *bigUint64Array:
+		v = toBigInt(v)
+	default:
+		v = v.ToNumber()
+	}
 	if a.isValidIntegerIndex(idx) {
 		a.typedArray.set(idx+a.offset, v)
 	}
@@ -715,7 +853,7 @@ func (a *typedArrayObject) setOwnStr(p unistring.String, v Value, throw bool) bo
 		return true
 	}
 	if idx == 0 {
-		v.ToNumber() // make sure it throws
+		toNumeric(v) // make sure it throws
 		return true
 	}
 	return a.baseObject.setOwnStr(p, v, throw)
@@ -938,6 +1076,14 @@ func (r *Runtime) newFloat64ArrayObject(buf *arrayBufferObject, offset, length i
 	return r._newTypedArrayObject(buf, offset, length, 8, r.global.Float64Array, (*float64Array)(&buf.data), proto)
 }
 
+func (r *Runtime) newBigInt64ArrayObject(buf *arrayBufferObject, offset, length int, proto *Object) *typedArrayObject {
+	return r._newTypedArrayObject(buf, offset, length, 8, r.global.BigInt64Array, (*bigInt64Array)(&buf.data), proto)
+}
+
+func (r *Runtime) newBigUint64ArrayObject(buf *arrayBufferObject, offset, length int, proto *Object) *typedArrayObject {
+	return r._newTypedArrayObject(buf, offset, length, 8, r.global.BigUint64Array, (*bigUint64Array)(&buf.data), proto)
+}
+
 func (o *dataViewObject) getIdxAndByteOrder(getIdx int, littleEndianVal Value, size int) (int, byteOrder) {
 	o.viewedArrayBuf.ensureNotDetached(true)
 	if getIdx+size > o.byteLen {
@@ -1026,6 +1172,52 @@ func (o *arrayBufferObject) setUint32(idx int, val uint32, byteOrder byteOrder)
 	}
 }
 
+func (o *arrayBufferObject) getBigInt64(idx int, byteOrder byteOrder) *big.Int {
+	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 big.NewInt(*((*int64)(unsafe.Pointer(&b[0]))))
+}
+
+func (o *arrayBufferObject) getBigUint64(idx int, byteOrder byteOrder) *big.Int {
+	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 new(big.Int).SetUint64(*((*uint64)(unsafe.Pointer(&b[0]))))
+}
+
+func (o *arrayBufferObject) setBigInt64(idx int, val *big.Int, byteOrder byteOrder) {
+	if byteOrder == nativeEndian {
+		*(*int64)(unsafe.Pointer(&o.data[idx])) = val.Int64()
+	} else {
+		n := val.Int64()
+		b := (*[8]byte)(unsafe.Pointer(&n))
+		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) setBigUint64(idx int, val *big.Int, byteOrder byteOrder) {
+	if byteOrder == nativeEndian {
+		*(*uint64)(unsafe.Pointer(&o.data[idx])) = val.Uint64()
+	} else {
+		n := val.Uint64()
+		b := (*[8]byte)(unsafe.Pointer(&n))
+		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) getUint16(idx int, byteOrder byteOrder) uint16 {
 	var b []byte
 	if byteOrder == nativeEndian {

+ 28 - 0
typedarrays_test.go

@@ -513,4 +513,32 @@ func TestTypedArrayExport(t *testing.T) {
 		}
 	})
 
+	t.Run("bigint64", func(t *testing.T) {
+		v, err := vm.RunString("new BigInt64Array([18446744073709551617n, 2n])")
+		if err != nil {
+			t.Fatal(err)
+		}
+		if a, ok := v.Export().([]int64); ok {
+			if len(a) != 2 || a[0] != 1 || a[1] != 2 {
+				t.Fatal(a)
+			}
+		} else {
+			t.Fatal("Wrong export type")
+		}
+	})
+
+	t.Run("biguint64", func(t *testing.T) {
+		v, err := vm.RunString("new BigUint64Array([18446744073709551617n, 2n])")
+		if err != nil {
+			t.Fatal(err)
+		}
+		if a, ok := v.Export().([]uint64); ok {
+			if len(a) != 2 || a[0] != 1 || a[1] != 2 {
+				t.Fatal(a)
+			}
+		} else {
+			t.Fatal("Wrong export type")
+		}
+	})
+
 }

+ 14 - 1
value.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"hash/maphash"
 	"math"
+	"math/big"
 	"reflect"
 	"strconv"
 	"unsafe"
@@ -141,6 +142,7 @@ type valueProperty struct {
 var (
 	errAccessBeforeInit = referenceError("Cannot access a variable before initialization")
 	errAssignToConst    = typeError("Assignment to constant variable.")
+	errMixBigIntType    = typeError("Cannot mix BigInt and other types, use explicit conversions")
 )
 
 func propGetter(o Value, v Value, r *Runtime) *Object {
@@ -218,6 +220,8 @@ func (i valueInt) Equals(other Value) bool {
 	switch o := other.(type) {
 	case valueInt:
 		return i == o
+	case *valueBigInt:
+		return (*big.Int)(o).Cmp(big.NewInt(int64(i))) == 0
 	case valueFloat:
 		return float64(i) == float64(o)
 	case String:
@@ -643,6 +647,15 @@ func (f valueFloat) Equals(other Value) bool {
 		return f == o
 	case valueInt:
 		return float64(f) == float64(o)
+	case *valueBigInt:
+		if IsInfinity(f) || math.IsNaN(float64(f)) {
+			return false
+		}
+		if f := big.NewFloat(float64(f)); f.IsInt() {
+			i, _ := f.Int(nil)
+			return (*big.Int)(o).Cmp(i) == 0
+		}
+		return false
 	case String, valueBool:
 		return float64(f) == o.ToFloat()
 	case *Object:
@@ -728,7 +741,7 @@ func (o *Object) Equals(other Value) bool {
 	}
 
 	switch o1 := other.(type) {
-	case valueInt, valueFloat, String, *Symbol:
+	case valueInt, valueFloat, *valueBigInt, String, *Symbol:
 		return o.toPrimitive().Equals(other)
 	case valueBool:
 		return o.Equals(o1.ToNumber())

+ 363 - 85
vm.go

@@ -3,6 +3,7 @@ package goja
 import (
 	"fmt"
 	"math"
+	"math/big"
 	"strconv"
 	"strings"
 	"sync"
@@ -415,17 +416,20 @@ func floatToValue(f float64) (result Value) {
 	return valueFloat(f)
 }
 
-func assertInt64(v Value) (int64, bool) {
-	num := v.ToNumber()
-	if i, ok := num.(valueInt); ok {
-		return int64(i), true
-	}
-	if f, ok := num.(valueFloat); ok {
-		if i, ok := floatToInt(float64(f)); ok {
-			return i, true
+func toNumeric(value Value) Value {
+	switch v := value.(type) {
+	case valueInt, *valueBigInt:
+		return v
+	case valueFloat:
+		return floatToValue(float64(v))
+	case *Object:
+		primValue := v.toPrimitiveNumber()
+		if bigint, ok := primValue.(*valueBigInt); ok {
+			return bigint
 		}
+		return primValue.ToNumber()
 	}
-	return 0, false
+	return value.ToNumber()
 }
 
 func (s *valueStack) expand(idx int) {
@@ -1236,7 +1240,7 @@ type _toNumber struct{}
 var toNumber _toNumber
 
 func (_toNumber) exec(vm *vm) {
-	vm.stack[vm.sp-1] = vm.stack[vm.sp-1].ToNumber()
+	vm.stack[vm.sp-1] = toNumeric(vm.stack[vm.sp-1])
 	vm.pc++
 }
 
@@ -1270,13 +1274,26 @@ func (_add) exec(vm *vm) {
 		}
 		ret = leftString.Concat(rightString)
 	} else {
-		if leftInt, ok := left.(valueInt); ok {
-			if rightInt, ok := right.(valueInt); ok {
-				ret = intToValue(int64(leftInt) + int64(rightInt))
+		switch left := left.(type) {
+		case valueInt:
+			switch right := right.(type) {
+			case valueInt:
+				ret = intToValue(int64(left) + int64(right))
+			case *valueBigInt:
+				panic(errMixBigIntType)
+			default:
+				ret = floatToValue(float64(left) + right.ToFloat())
+			}
+		case *valueBigInt:
+			if right, ok := right.(*valueBigInt); ok {
+				ret = (*valueBigInt)(new(big.Int).Add((*big.Int)(left), (*big.Int)(right)))
 			} else {
-				ret = floatToValue(float64(leftInt) + right.ToFloat())
+				panic(errMixBigIntType)
+			}
+		default:
+			if _, ok := right.(*valueBigInt); ok {
+				panic(errMixBigIntType)
 			}
-		} else {
 			ret = floatToValue(left.ToFloat() + right.ToFloat())
 		}
 	}
@@ -1294,13 +1311,30 @@ func (_sub) exec(vm *vm) {
 	right := vm.stack[vm.sp-1]
 	left := vm.stack[vm.sp-2]
 
+	left = toNumeric(left)
+	right = toNumeric(right)
+
 	var result Value
 
-	if left, ok := left.(valueInt); ok {
-		if right, ok := right.(valueInt); ok {
+	switch left := left.(type) {
+	case valueInt:
+		switch right := right.(type) {
+		case valueInt:
 			result = intToValue(int64(left) - int64(right))
 			goto end
+		case *valueBigInt:
+			panic(errMixBigIntType)
+		}
+	case valueFloat:
+		if _, ok := right.(*valueBigInt); ok {
+			panic(errMixBigIntType)
+		}
+	case *valueBigInt:
+		if right, ok := right.(*valueBigInt); ok {
+			result = (*valueBigInt)(new(big.Int).Sub((*big.Int)(left), (*big.Int)(right)))
+			goto end
 		}
+		panic(errMixBigIntType)
 	}
 
 	result = floatToValue(left.ToFloat() - right.ToFloat())
@@ -1315,13 +1349,15 @@ type _mul struct{}
 var mul _mul
 
 func (_mul) exec(vm *vm) {
-	left := vm.stack[vm.sp-2]
-	right := vm.stack[vm.sp-1]
+	left := toNumeric(vm.stack[vm.sp-2])
+	right := toNumeric(vm.stack[vm.sp-1])
 
 	var result Value
 
-	if left, ok := assertInt64(left); ok {
-		if right, ok := assertInt64(right); ok {
+	switch left := left.(type) {
+	case valueInt:
+		switch right := right.(type) {
+		case valueInt:
 			if left == 0 && right == -1 || left == -1 && right == 0 {
 				result = _negativeZero
 				goto end
@@ -1329,11 +1365,22 @@ func (_mul) exec(vm *vm) {
 			res := left * right
 			// check for overflow
 			if left == 0 || right == 0 || res/left == right {
-				result = intToValue(res)
+				result = intToValue(int64(res))
 				goto end
 			}
-
+		case *valueBigInt:
+			panic(errMixBigIntType)
+		}
+	case valueFloat:
+		if _, ok := right.(*valueBigInt); ok {
+			panic(errMixBigIntType)
 		}
+	case *valueBigInt:
+		if right, ok := right.(*valueBigInt); ok {
+			result = (*valueBigInt)(new(big.Int).Mul((*big.Int)(left), (*big.Int)(right)))
+			goto end
+		}
+		panic(errMixBigIntType)
 	}
 
 	result = floatToValue(left.ToFloat() * right.ToFloat())
@@ -1350,7 +1397,29 @@ var exp _exp
 
 func (_exp) exec(vm *vm) {
 	vm.sp--
-	vm.stack[vm.sp-1] = pow(vm.stack[vm.sp-1], vm.stack[vm.sp])
+	x := vm.stack[vm.sp-1]
+	y := vm.stack[vm.sp]
+
+	x = toNumeric(x)
+	y = toNumeric(y)
+
+	var result Value
+	if x, ok := x.(*valueBigInt); ok {
+		if y, ok := y.(*valueBigInt); ok {
+			if (*big.Int)(y).Cmp(big.NewInt(0)) < 0 {
+				panic(vm.r.newError(vm.r.getRangeError(), "exponent must be positive"))
+			}
+			result = (*valueBigInt)(new(big.Int).Exp((*big.Int)(x), (*big.Int)(y), nil))
+			goto end
+		}
+		panic(errMixBigIntType)
+	} else if _, ok := y.(*valueBigInt); ok {
+		panic(errMixBigIntType)
+	}
+
+	result = pow(x, y)
+end:
+	vm.stack[vm.sp-1] = result
 	vm.pc++
 }
 
@@ -1359,10 +1428,32 @@ type _div struct{}
 var div _div
 
 func (_div) exec(vm *vm) {
-	left := vm.stack[vm.sp-2].ToFloat()
-	right := vm.stack[vm.sp-1].ToFloat()
-
-	var result Value
+	leftValue := toNumeric(vm.stack[vm.sp-2])
+	rightValue := toNumeric(vm.stack[vm.sp-1])
+
+	var (
+		result      Value
+		left, right float64
+	)
+
+	if left, ok := leftValue.(*valueBigInt); ok {
+		if right, ok := rightValue.(*valueBigInt); ok {
+			if (*big.Int)(right).Cmp(big.NewInt(0)) == 0 {
+				panic(vm.r.newError(vm.r.getRangeError(), "Division by zero"))
+			}
+			if (*big.Int)(left).CmpAbs((*big.Int)(right)) < 0 {
+				result = (*valueBigInt)(big.NewInt(0))
+			} else {
+				i, _ := new(big.Int).QuoRem((*big.Int)(left), (*big.Int)(right), big.NewInt(0))
+				result = (*valueBigInt)(i)
+			}
+			goto end
+		}
+		panic(errMixBigIntType)
+	} else if _, ok := rightValue.(*valueBigInt); ok {
+		panic(errMixBigIntType)
+	}
+	left, right = leftValue.ToFloat(), rightValue.ToFloat()
 
 	if math.IsNaN(left) || math.IsNaN(right) {
 		result = _NaN
@@ -1418,25 +1509,48 @@ type _mod struct{}
 var mod _mod
 
 func (_mod) exec(vm *vm) {
-	left := vm.stack[vm.sp-2]
-	right := vm.stack[vm.sp-1]
+	left := toNumeric(vm.stack[vm.sp-2])
+	right := toNumeric(vm.stack[vm.sp-1])
 
 	var result Value
 
-	if leftInt, ok := assertInt64(left); ok {
-		if rightInt, ok := assertInt64(right); ok {
-			if rightInt == 0 {
+	switch left := left.(type) {
+	case valueInt:
+		switch right := right.(type) {
+		case valueInt:
+			if right == 0 {
 				result = _NaN
 				goto end
 			}
-			r := leftInt % rightInt
-			if r == 0 && leftInt < 0 {
+			r := left % right
+			if r == 0 && left < 0 {
 				result = _negativeZero
 			} else {
-				result = intToValue(leftInt % rightInt)
+				result = intToValue(int64(left % right))
+			}
+			goto end
+		case *valueBigInt:
+			panic(errMixBigIntType)
+		}
+	case valueFloat:
+		if _, ok := right.(*valueBigInt); ok {
+			panic(errMixBigIntType)
+		}
+	case *valueBigInt:
+		if right, ok := right.(*valueBigInt); ok {
+			switch {
+			case (*big.Int)(right).Cmp(big.NewInt(0)) == 0:
+				panic(vm.r.newError(vm.r.getRangeError(), "Division by zero"))
+			case (*big.Int)(left).Cmp(big.NewInt(0)) < 0:
+				abs := new(big.Int).Abs((*big.Int)(left))
+				v := new(big.Int).Mod(abs, (*big.Int)(right))
+				result = (*valueBigInt)(v.Neg(v))
+			default:
+				result = (*valueBigInt)(new(big.Int).Mod((*big.Int)(left), (*big.Int)(right)))
 			}
 			goto end
 		}
+		panic(errMixBigIntType)
 	}
 
 	result = floatToValue(math.Mod(left.ToFloat(), right.ToFloat()))
@@ -1455,13 +1569,16 @@ func (_neg) exec(vm *vm) {
 
 	var result Value
 
-	if i, ok := assertInt64(operand); ok {
-		if i == 0 {
+	switch n := toNumeric(operand).(type) {
+	case *valueBigInt:
+		result = (*valueBigInt)(new(big.Int).Neg((*big.Int)(n)))
+	case valueInt:
+		if n == 0 {
 			result = _negativeZero
 		} else {
-			result = valueInt(-i)
+			result = -n
 		}
-	} else {
+	default:
 		f := operand.ToFloat()
 		if !math.IsNaN(f) {
 			f = -f
@@ -1489,14 +1606,15 @@ var inc _inc
 func (_inc) exec(vm *vm) {
 	v := vm.stack[vm.sp-1]
 
-	if i, ok := assertInt64(v); ok {
-		v = intToValue(i + 1)
-		goto end
+	switch n := v.(type) {
+	case *valueBigInt:
+		v = (*valueBigInt)(new(big.Int).Add((*big.Int)(n), big.NewInt(1)))
+	case valueInt:
+		v = intToValue(int64(n + 1))
+	default:
+		v = valueFloat(n.ToFloat() + 1)
 	}
 
-	v = valueFloat(v.ToFloat() + 1)
-
-end:
 	vm.stack[vm.sp-1] = v
 	vm.pc++
 }
@@ -1508,14 +1626,15 @@ var dec _dec
 func (_dec) exec(vm *vm) {
 	v := vm.stack[vm.sp-1]
 
-	if i, ok := assertInt64(v); ok {
-		v = intToValue(i - 1)
-		goto end
+	switch n := v.(type) {
+	case *valueBigInt:
+		v = (*valueBigInt)(new(big.Int).Sub((*big.Int)(n), big.NewInt(1)))
+	case valueInt:
+		v = intToValue(int64(n - 1))
+	default:
+		v = valueFloat(n.ToFloat() - 1)
 	}
 
-	v = valueFloat(v.ToFloat() - 1)
-
-end:
 	vm.stack[vm.sp-1] = v
 	vm.pc++
 }
@@ -1525,9 +1644,23 @@ type _and struct{}
 var and _and
 
 func (_and) exec(vm *vm) {
-	left := toInt32(vm.stack[vm.sp-2])
-	right := toInt32(vm.stack[vm.sp-1])
-	vm.stack[vm.sp-2] = intToValue(int64(left & right))
+	left := toNumeric(vm.stack[vm.sp-2])
+	right := toNumeric(vm.stack[vm.sp-1])
+	var result Value
+
+	if left, ok := left.(*valueBigInt); ok {
+		if right, ok := right.(*valueBigInt); ok {
+			result = (*valueBigInt)(new(big.Int).And((*big.Int)(left), (*big.Int)(right)))
+			goto end
+		}
+		panic(errMixBigIntType)
+	} else if _, ok := right.(*valueBigInt); ok {
+		panic(errMixBigIntType)
+	}
+
+	result = intToValue(int64(toInt32(left) & toInt32(right)))
+end:
+	vm.stack[vm.sp-2] = result
 	vm.sp--
 	vm.pc++
 }
@@ -1537,9 +1670,23 @@ type _or struct{}
 var or _or
 
 func (_or) exec(vm *vm) {
-	left := toInt32(vm.stack[vm.sp-2])
-	right := toInt32(vm.stack[vm.sp-1])
-	vm.stack[vm.sp-2] = intToValue(int64(left | right))
+	left := toNumeric(vm.stack[vm.sp-2])
+	right := toNumeric(vm.stack[vm.sp-1])
+	var result Value
+
+	if left, ok := left.(*valueBigInt); ok {
+		if right, ok := right.(*valueBigInt); ok {
+			result = (*valueBigInt)(new(big.Int).Or((*big.Int)(left), (*big.Int)(right)))
+			goto end
+		}
+		panic(errMixBigIntType)
+	} else if _, ok := right.(*valueBigInt); ok {
+		panic(errMixBigIntType)
+	}
+
+	result = intToValue(int64(toInt32(left) | toInt32(right)))
+end:
+	vm.stack[vm.sp-2] = result
 	vm.sp--
 	vm.pc++
 }
@@ -1549,9 +1696,23 @@ type _xor struct{}
 var xor _xor
 
 func (_xor) exec(vm *vm) {
-	left := toInt32(vm.stack[vm.sp-2])
-	right := toInt32(vm.stack[vm.sp-1])
-	vm.stack[vm.sp-2] = intToValue(int64(left ^ right))
+	left := toNumeric(vm.stack[vm.sp-2])
+	right := toNumeric(vm.stack[vm.sp-1])
+	var result Value
+
+	if left, ok := left.(*valueBigInt); ok {
+		if right, ok := right.(*valueBigInt); ok {
+			result = (*valueBigInt)(new(big.Int).Xor((*big.Int)(left), (*big.Int)(right)))
+			goto end
+		}
+		panic(errMixBigIntType)
+	} else if _, ok := right.(*valueBigInt); ok {
+		panic(errMixBigIntType)
+	}
+
+	result = intToValue(int64(toInt32(left) ^ toInt32(right)))
+end:
+	vm.stack[vm.sp-2] = result
 	vm.sp--
 	vm.pc++
 }
@@ -1561,8 +1722,14 @@ type _bnot struct{}
 var bnot _bnot
 
 func (_bnot) exec(vm *vm) {
-	op := toInt32(vm.stack[vm.sp-1])
-	vm.stack[vm.sp-1] = intToValue(int64(^op))
+	v := vm.stack[vm.sp-1]
+	switch n := toNumeric(v).(type) {
+	case *valueBigInt:
+		v = (*valueBigInt)(new(big.Int).Not((*big.Int)(n)))
+	default:
+		v = intToValue(int64(^toInt32(n)))
+	}
+	vm.stack[vm.sp-1] = v
 	vm.pc++
 }
 
@@ -1571,9 +1738,28 @@ type _sal struct{}
 var sal _sal
 
 func (_sal) exec(vm *vm) {
-	left := toInt32(vm.stack[vm.sp-2])
-	right := toUint32(vm.stack[vm.sp-1])
-	vm.stack[vm.sp-2] = intToValue(int64(left << (right & 0x1F)))
+	left := toNumeric(vm.stack[vm.sp-2])
+	right := toNumeric(vm.stack[vm.sp-1])
+	var result Value
+
+	if left, ok := left.(*valueBigInt); ok {
+		if right, ok := right.(*valueBigInt); ok {
+			n := uint((*big.Int)(right).Uint64())
+			if (*big.Int)(right).Sign() < 0 {
+				result = (*valueBigInt)(new(big.Int).Rsh((*big.Int)(left), n))
+			} else {
+				result = (*valueBigInt)(new(big.Int).Lsh((*big.Int)(left), n))
+			}
+			goto end
+		}
+		panic(errMixBigIntType)
+	} else if _, ok := right.(*valueBigInt); ok {
+		panic(errMixBigIntType)
+	}
+
+	result = intToValue(int64(toInt32(left) << (toUint32(right) & 0x1F)))
+end:
+	vm.stack[vm.sp-2] = result
 	vm.sp--
 	vm.pc++
 }
@@ -1583,9 +1769,28 @@ type _sar struct{}
 var sar _sar
 
 func (_sar) exec(vm *vm) {
-	left := toInt32(vm.stack[vm.sp-2])
-	right := toUint32(vm.stack[vm.sp-1])
-	vm.stack[vm.sp-2] = intToValue(int64(left >> (right & 0x1F)))
+	left := toNumeric(vm.stack[vm.sp-2])
+	right := toNumeric(vm.stack[vm.sp-1])
+	var result Value
+
+	if left, ok := left.(*valueBigInt); ok {
+		if right, ok := right.(*valueBigInt); ok {
+			n := uint((*big.Int)(right).Uint64())
+			if (*big.Int)(right).Sign() < 0 {
+				result = (*valueBigInt)(new(big.Int).Lsh((*big.Int)(left), n))
+			} else {
+				result = (*valueBigInt)(new(big.Int).Rsh((*big.Int)(left), n))
+			}
+			goto end
+		}
+		panic(errMixBigIntType)
+	} else if _, ok := right.(*valueBigInt); ok {
+		panic(errMixBigIntType)
+	}
+
+	result = intToValue(int64(toInt32(left) >> (toUint32(right) & 0x1F)))
+end:
+	vm.stack[vm.sp-2] = result
 	vm.sp--
 	vm.pc++
 }
@@ -1595,9 +1800,17 @@ 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])
-	vm.stack[vm.sp-2] = intToValue(int64(left >> (right & 0x1F)))
+	left := toNumeric(vm.stack[vm.sp-2])
+	right := toNumeric(vm.stack[vm.sp-1])
+
+	if _, ok := left.(*valueBigInt); ok {
+		_ = toNumeric(right)
+		panic(vm.r.NewTypeError("BigInts have no unsigned right shift, use >> instead"))
+	} else if _, ok := right.(*valueBigInt); ok {
+		panic(vm.r.NewTypeError("BigInts have no unsigned right shift, use >> instead"))
+	}
+
+	vm.stack[vm.sp-2] = intToValue(int64(toUint32(left) >> (toUint32(right) & 0x1F)))
 	vm.sp--
 	vm.pc++
 }
@@ -4203,31 +4416,94 @@ func toPrimitive(v Value) Value {
 
 func cmp(px, py Value) Value {
 	var ret bool
-	var nx, ny float64
+	xs, isPxString := px.(String)
+	ys, isPyString := py.(String)
 
-	if xs, ok := px.(String); ok {
-		if ys, ok := py.(String); ok {
-			ret = xs.CompareTo(ys) < 0
+	if isPxString && isPyString {
+		ret = xs.CompareTo(ys) < 0
+		goto end
+	} else {
+		if px, ok := px.(*valueBigInt); ok && isPyString {
+			ny, err := stringToBigInt(ys.toTrimmedUTF8())
+			if err != nil {
+				return _undefined
+			}
+			ret = (*big.Int)(px).Cmp(ny) < 0
+			goto end
+		}
+		if py, ok := py.(*valueBigInt); ok && isPxString {
+			nx, err := stringToBigInt(xs.toTrimmedUTF8())
+			if err != nil {
+				return _undefined
+			}
+			ret = nx.Cmp((*big.Int)(py)) < 0
 			goto end
 		}
 	}
 
-	if xi, ok := px.(valueInt); ok {
-		if yi, ok := py.(valueInt); ok {
-			ret = xi < yi
+	px = toNumeric(px)
+	py = toNumeric(py)
+
+	switch nx := px.(type) {
+	case valueInt:
+		switch ny := py.(type) {
+		case valueInt:
+			ret = nx < ny
+			goto end
+		case *valueBigInt:
+			ret = big.NewInt(int64(nx)).Cmp((*big.Int)(ny)) < 0
+			goto end
+		}
+	case valueFloat:
+		switch ny := py.(type) {
+		case *valueBigInt:
+			switch {
+			case math.IsNaN(float64(nx)):
+				return _undefined
+			case nx == _negativeInf:
+				ret = true
+				goto end
+			}
+			if nx := big.NewFloat(float64(nx)); nx.IsInt() {
+				nx, _ := nx.Int(nil)
+				ret = nx.Cmp((*big.Int)(ny)) < 0
+			} else {
+				ret = nx.Cmp(new(big.Float).SetInt((*big.Int)(ny))) < 0
+			}
+			goto end
+		}
+	case *valueBigInt:
+		switch ny := py.(type) {
+		case valueInt:
+			ret = (*big.Int)(nx).Cmp(big.NewInt(int64(ny))) < 0
+			goto end
+		case valueFloat:
+			switch {
+			case math.IsNaN(float64(ny)):
+				return _undefined
+			case ny == _positiveInf:
+				ret = true
+				goto end
+			}
+			if ny := big.NewFloat(float64(ny)); ny.IsInt() {
+				ny, _ := ny.Int(nil)
+				ret = (*big.Int)(nx).Cmp(ny) < 0
+			} else {
+				ret = new(big.Float).SetInt((*big.Int)(nx)).Cmp(ny) < 0
+			}
+			goto end
+		case *valueBigInt:
+			ret = (*big.Int)(nx).Cmp((*big.Int)(ny)) < 0
 			goto end
 		}
 	}
 
-	nx = px.ToFloat()
-	ny = py.ToFloat()
-
-	if math.IsNaN(nx) || math.IsNaN(ny) {
+	if nx, ny := px.ToFloat(), py.ToFloat(); math.IsNaN(nx) || math.IsNaN(ny) {
 		return _undefined
+	} else {
+		ret = nx < ny
 	}
 
-	ret = nx < ny
-
 end:
 	if ret {
 		return valueTrue
@@ -4581,6 +4857,8 @@ func (_typeof) exec(vm *vm) {
 		r = stringString
 	case valueInt, valueFloat:
 		r = stringNumber
+	case *valueBigInt:
+		r = stringBigInt
 	case *Symbol:
 		r = stringSymbol
 	default: