Browse Source

Basic implementation of BigInt, BigInt64Array and BigUint64Array

Philip Silva 3 years ago
parent
commit
307513316c
20 changed files with 748 additions and 40 deletions
  1. 9 0
      ast/node.go
  2. 53 0
      builtin_bigint.go
  3. 9 1
      builtin_string.go
  4. 39 1
      builtin_typedarrays.go
  5. 24 2
      compiler_expr.go
  6. 4 0
      destruct.go
  7. 27 0
      object.go
  8. 4 0
      object_dynamic.go
  9. 6 0
      object_lazy.go
  10. 32 7
      parser/expression.go
  11. 23 2
      parser/lexer.go
  12. 18 0
      parser/lexer_test.go
  13. 16 0
      parser/parser_test.go
  14. 36 0
      runtime.go
  15. 7 0
      string_ascii.go
  16. 7 0
      string_unicode.go
  17. 24 9
      tc39_test.go
  18. 140 0
      typedarrays.go
  19. 140 0
      value.go
  20. 130 18
      vm.go

+ 9 - 0
ast/node.go

@@ -169,6 +169,12 @@ type (
 		Value   interface{}
 	}
 
+	BigIntLiteral struct {
+		Idx     file.Idx
+		Literal string
+		Value   interface{}
+	}
+
 	ObjectLiteral struct {
 		LeftBrace  file.Idx
 		RightBrace file.Idx
@@ -275,6 +281,7 @@ func (*Identifier) _expressionNode()            {}
 func (*NewExpression) _expressionNode()         {}
 func (*NullLiteral) _expressionNode()           {}
 func (*NumberLiteral) _expressionNode()         {}
+func (*BigIntLiteral) _expressionNode()         {}
 func (*ObjectLiteral) _expressionNode()         {}
 func (*RegExpLiteral) _expressionNode()         {}
 func (*SequenceExpression) _expressionNode()    {}
@@ -567,6 +574,7 @@ func (self *Identifier) Idx0() file.Idx            { return self.Idx }
 func (self *NewExpression) Idx0() file.Idx         { return self.New }
 func (self *NullLiteral) Idx0() file.Idx           { return self.Idx }
 func (self *NumberLiteral) Idx0() file.Idx         { return self.Idx }
+func (self *BigIntLiteral) Idx0() file.Idx         { return self.Idx }
 func (self *ObjectLiteral) Idx0() file.Idx         { return self.LeftBrace }
 func (self *RegExpLiteral) Idx0() file.Idx         { return self.Idx }
 func (self *SequenceExpression) Idx0() file.Idx    { return self.Sequence[0].Idx0() }
@@ -627,6 +635,7 @@ func (self *Identifier) Idx1() file.Idx            { return file.Idx(int(self.Id
 func (self *NewExpression) Idx1() file.Idx         { return self.RightParenthesis + 1 }
 func (self *NullLiteral) Idx1() file.Idx           { return file.Idx(int(self.Idx) + 4) } // "null"
 func (self *NumberLiteral) Idx1() file.Idx         { return file.Idx(int(self.Idx) + len(self.Literal)) }
+func (self *BigIntLiteral) Idx1() file.Idx         { return file.Idx(int(self.Idx) + len(self.Literal)) }
 func (self *ObjectLiteral) Idx1() file.Idx         { return self.RightBrace + 1 }
 func (self *ObjectPattern) Idx1() file.Idx         { return self.RightBrace + 1 }
 func (self *RegExpLiteral) Idx1() file.Idx         { return file.Idx(int(self.Idx) + len(self.Literal)) }

+ 53 - 0
builtin_bigint.go

@@ -0,0 +1,53 @@
+package goja
+
+func (r *Runtime) bigintproto_valueOf(call FunctionCall) Value {
+	this := call.This
+	if !isBigInt(this) {
+		r.typeErrorResult(true, "Value is not a bigint")
+	}
+	switch t := this.(type) {
+	case valueBigInt:
+		return this
+	case *Object:
+		if v, ok := t.self.(*primitiveValueObject); ok {
+			return v.pValue
+		}
+	}
+
+	panic(r.NewTypeError("BigInt.prototype.valueOf is not generic"))
+}
+
+func isBigInt(v Value) bool {
+	switch t := v.(type) {
+	case valueBigInt:
+		return true
+	case *Object:
+		switch t := t.self.(type) {
+		case *primitiveValueObject:
+			return isBigInt(t.pValue)
+		}
+	}
+	return false
+}
+
+func (r *Runtime) bigintproto_toString(call FunctionCall) Value {
+	this := call.This
+	if !isBigInt(this) {
+		r.typeErrorResult(true, "Value is not a bigint")
+	}
+	b := call.This.ToBigInt()
+	if t, ok := b.(valueBigInt); ok {
+		return asciiString(t.Int.String())
+	}
+	panic(r.NewTypeError("BigInt.prototype.toString is not generic"))
+}
+
+func (r *Runtime) initBigInt() {
+	r.global.BigIntPrototype = r.newPrimitiveObject(valueInt(0), r.global.ObjectPrototype, classBigInt)
+	o := r.global.BigIntPrototype.self
+	o._putProp("toString", r.newNativeFunc(r.bigintproto_toString, nil, "toString", nil, 1), true, false, true)
+	o._putProp("valueOf", r.newNativeFunc(r.bigintproto_valueOf, nil, "valueOf", nil, 0), true, false, true)
+
+	r.global.BigInt = r.newNativeFunc(r.builtin_BigInt, r.builtin_newBigInt, "BigInt", r.global.BigIntPrototype, 1)
+	r.addToGlobal("BigInt", r.global.BigInt)
+}

+ 9 - 1
builtin_string.go

@@ -295,7 +295,15 @@ 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()
+	var pos int64
+	posArg := call.Argument(1)
+	if o, ok := posArg.(*Object); ok {
+		posArg = o.toPrimitiveNumber()
+	}
+	if isBigInt(posArg) {
+		panic(r.NewTypeError("pos must be a number"))
+	}
+	pos = posArg.ToInteger()
 
 	if pos < 0 {
 		pos = 0

+ 39 - 1
builtin_typedarrays.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"math"
 	"sort"
+	"strconv"
 	"unsafe"
 
 	"github.com/dop251/goja/unistring"
@@ -458,7 +459,18 @@ func (r *Runtime) typedArrayProto_fill(call FunctionCall) Value {
 			relEnd = l
 		}
 		final := toIntStrict(relToIdx(relEnd, l))
-		value := ta.typedArray.toRaw(call.Argument(0))
+		arg := call.Argument(0)
+		switch ta.typedArray.(type) {
+		case *bigInt64Array, *bigUint64Array:
+			if _, ok := arg.(valueString); !ok {
+				break
+			}
+			_, err := strconv.ParseInt(arg.String(), 0, 64)
+			if err != nil {
+				panic(r.newSyntaxError("cannot convert to bigint", -1))
+			}
+		}
+		value := ta.typedArray.toRaw(arg)
 		ta.viewedArrayBuf.ensureNotDetached(true)
 		for ; k < final; k++ {
 			ta.typedArray.setRaw(ta.offset+k, value)
@@ -895,6 +907,18 @@ func (r *Runtime) typedArrayProto_set(call FunctionCall) Value {
 			for i := 0; i < srcLen; i++ {
 				val := nilSafe(srcObj.self.getIdx(valueInt(i), nil))
 				ta.viewedArrayBuf.ensureNotDetached(true)
+				if _, ok := val.(asciiString); ok {
+					switch ta.typedArray.(type) {
+					case *bigUint64Array:
+						if _, err := strconv.ParseUint(val.String(), 0, 64); err != nil && val.String() != "" {
+	panic(r.newError(r.global.SyntaxError, "cannot convert to bigint"))
+						}
+					case *bigInt64Array:
+						if _, err := strconv.ParseInt(val.String(), 0, 64); err != nil && val.String() != "" {
+	panic(r.newError(r.global.SyntaxError, "cannot convert to bigint"))
+						}
+					}
+				}
 				ta.typedArray.set(targetOffset+i, val)
 			}
 		}
@@ -1239,6 +1263,14 @@ func (r *Runtime) newFloat64Array(args []Value, newTarget *Object) *Object {
 	return r._newTypedArray(args, newTarget, r.newFloat64ArrayObject)
 }
 
+func (r *Runtime) newBigInt64Array(args []Value, newTarget *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newBigInt64ArrayObject)
+}
+
+func (r *Runtime) newBigUint64Array(args []Value, newTarget *Object) *Object {
+	return r._newTypedArray(args, newTarget, r.newBigUint64ArrayObject)
+}
+
 func (r *Runtime) createArrayBufferProto(val *Object) objectImpl {
 	b := newBaseObjectObj(val, r.global.ObjectPrototype, classObject)
 	byteLengthProp := &valueProperty{
@@ -1431,4 +1463,10 @@ func (r *Runtime) initTypedArrays() {
 
 	r.global.Float64Array = r.newLazyObject(r.typedArrayCreator(r.newFloat64Array, "Float64Array", 8))
 	r.addToGlobal("Float64Array", r.global.Float64Array)
+
+	r.global.BigInt64Array = r.newLazyObject(r.typedArrayCreator(r.newBigInt64Array, "BigInt64Array", 8))
+	r.addToGlobal("BigInt64Array", r.global.BigInt64Array)
+
+	r.global.BigUint64Array = r.newLazyObject(r.typedArrayCreator(r.newBigUint64Array, "BigUint64Array", 8))
+	r.addToGlobal("BigUint64Array", r.global.BigUint64Array)
 }

+ 24 - 2
compiler_expr.go

@@ -2,6 +2,7 @@ package goja
 
 import (
 	"fmt"
+	"math/big"
 	"github.com/dop251/goja/ast"
 	"github.com/dop251/goja/file"
 	"github.com/dop251/goja/token"
@@ -203,6 +204,8 @@ func (c *compiler) compileExpression(v ast.Expression) compiledExpr {
 		return c.compileAssignExpression(v)
 	case *ast.NumberLiteral:
 		return c.compileNumberLiteral(v)
+	case *ast.BigIntLiteral:
+		return c.compileBigIntLiteral(v)
 	case *ast.StringLiteral:
 		return c.compileStringLiteral(v)
 	case *ast.TemplateLiteral:
@@ -1417,7 +1420,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 "RangeError", "TypeError":
 			c.emit(loadDynamic(t))
 			msg := o.self.getStr("message", nil)
 			if msg != nil {
@@ -1430,7 +1433,7 @@ func (c *compiler) emitThrow(v Value) {
 			return
 		}
 	}
-	panic(fmt.Errorf("unknown exception type thrown while evaliating constant expression: %s", v.String()))
+	panic(fmt.Errorf("unknown exception type thrown while evaluating constant expression: %s", v.String()))
 }
 
 func (c *compiler) emitConst(expr compiledExpr, putOnStack bool) {
@@ -2064,6 +2067,25 @@ func (c *compiler) compileNumberLiteral(v *ast.NumberLiteral) compiledExpr {
 	return r
 }
 
+func (c *compiler) compileBigIntLiteral(v *ast.BigIntLiteral) compiledExpr {
+	if c.scope.strict && len(v.Literal) > 1 && v.Literal[0] == '0' && v.Literal[1] <= '7' && v.Literal[1] >= '0' {
+		c.throwSyntaxError(int(v.Idx)-1, "Octal literals are not allowed in strict mode")
+		panic("Unreachable")
+	}
+	var val Value
+	switch num := v.Value.(type) {
+	case *big.Int:
+		val = valueBigInt{num}
+	default:
+		panic(fmt.Errorf("Unsupported bigint literal type: %T", v.Value))
+	}
+	r := &compiledLiteral{
+		val: val,
+	}
+	r.init(c, v.Idx0())
+	return r
+}
+
 func (c *compiler) compileStringLiteral(v *ast.StringLiteral) compiledExpr {
 	r := &compiledLiteral{
 		val: stringValueFromRaw(v.Value),

+ 4 - 0
destruct.go

@@ -158,6 +158,10 @@ func (d *destructKeyedSource) toPrimitiveNumber() Value {
 	return d.w().toPrimitiveNumber()
 }
 
+func (d *destructKeyedSource) toPrimitiveBigInt() Value {
+	return d.w().toPrimitiveBigInt()
+}
+
 func (d *destructKeyedSource) toPrimitiveString() Value {
 	return d.w().toPrimitiveString()
 }

+ 27 - 0
object.go

@@ -19,6 +19,7 @@ const (
 	classSet      = "Set"
 	classFunction = "Function"
 	classNumber   = "Number"
+	classBigInt   = "BigInt"
 	classString   = "String"
 	classBoolean  = "Boolean"
 	classError    = "Error"
@@ -39,6 +40,7 @@ const (
 var (
 	hintDefault Value = asciiString("default")
 	hintNumber  Value = asciiString("number")
+	hintBigInt  Value = asciiString("bigint")
 	hintString  Value = asciiString("string")
 )
 
@@ -181,6 +183,7 @@ type objectImpl interface {
 	deleteSym(s *Symbol, throw bool) bool
 
 	toPrimitiveNumber() Value
+	toPrimitiveBigInt() Value
 	toPrimitiveString() Value
 	toPrimitive() Value
 	assertCallable() (call func(FunctionCall) Value, ok bool)
@@ -832,6 +835,22 @@ func (o *baseObject) toPrimitiveNumber() Value {
 	return o.val.genericToPrimitiveNumber()
 }
 
+func (o *Object) genericToPrimitiveBigInt() Value {
+	if v := o.tryPrimitive("valueOf"); v != nil {
+		return v
+	}
+
+	if v := o.tryPrimitive("toString"); v != nil {
+		return v
+	}
+
+	panic(o.runtime.NewTypeError("Could not convert %v to primitive", o.self))
+}
+
+func (o *baseObject) toPrimitiveBigInt() Value {
+	return o.val.genericToPrimitiveBigInt()
+}
+
 func (o *Object) genericToPrimitiveString() Value {
 	if v := o.tryPrimitive("toString"); v != nil {
 		return v
@@ -879,6 +898,14 @@ func (o *Object) toPrimitiveNumber() Value {
 	return o.self.toPrimitiveNumber()
 }
 
+func (o *Object) toPrimitiveBigInt() Value {
+	if v := o.tryExoticToPrimitive(hintBigInt); v != nil {
+		return v
+	}
+
+	return o.self.toPrimitiveBigInt()
+}
+
 func (o *Object) toPrimitiveString() Value {
 	if v := o.tryExoticToPrimitive(hintString); v != nil {
 		return v

+ 4 - 0
object_dynamic.go

@@ -402,6 +402,10 @@ func (o *baseDynamicObject) toPrimitiveNumber() Value {
 	return o.val.genericToPrimitiveNumber()
 }
 
+func (o *baseDynamicObject) toPrimitiveBigInt() Value {
+	return o.val.genericToPrimitiveBigInt()
+}
+
 func (o *baseDynamicObject) toPrimitiveString() Value {
 	return o.val.genericToPrimitiveString()
 }

+ 6 - 0
object_lazy.go

@@ -169,6 +169,12 @@ func (o *lazyObject) toPrimitiveNumber() Value {
 	return obj.toPrimitiveNumber()
 }
 
+func (o *lazyObject) toPrimitiveBigInt() Value {
+	obj := o.create(o.val)
+	o.val.self = obj
+	return obj.toPrimitiveBigInt()
+}
+
 func (o *lazyObject) toPrimitiveString() Value {
 	obj := o.create(o.val)
 	o.val.self = obj

+ 32 - 7
parser/expression.go

@@ -68,6 +68,18 @@ func (self *_parser) parsePrimaryExpression() ast.Expression {
 		}
 	case token.NUMBER:
 		self.next()
+		if literal[len(literal)-1] == 'n' {
+			if value, err := parseBigIntLiteral(literal); err != nil {
+				self.error(idx, err.Error())
+				value = 0
+			} else {
+				return &ast.BigIntLiteral{
+					Idx:     idx,
+					Literal: literal,
+					Value:   value,
+				}
+			}
+		}
 		value, err := parseNumberLiteral(literal)
 		if err != nil {
 			self.error(idx, err.Error())
@@ -292,14 +304,27 @@ func (self *_parser) parseObjectPropertyKey() (unistring.String, ast.Expression,
 			Value:   unistring.String(literal),
 		}
 	case token.NUMBER:
-		num, err := parseNumberLiteral(literal)
-		if err != nil {
-			self.error(idx, err.Error())
+		if literal[len(literal)-1] == 'n' {
+			num, err := parseBigIntLiteral(literal)
+			if err != nil {
+				self.error(idx, err.Error())
+			} else {
+				value = &ast.BigIntLiteral{
+					Idx:     idx,
+					Literal: literal,
+					Value:   num,
+				}
+			}
 		} else {
-			value = &ast.NumberLiteral{
-				Idx:     idx,
-				Literal: literal,
-				Value:   num,
+			num, err := parseNumberLiteral(literal)
+			if err != nil {
+				self.error(idx, err.Error())
+			} else {
+				value = &ast.NumberLiteral{
+					Idx:     idx,
+					Literal: literal,
+					Value:   num,
+				}
 			}
 		}
 	case token.STRING:

+ 23 - 2
parser/lexer.go

@@ -3,6 +3,7 @@ package parser
 import (
 	"errors"
 	"fmt"
+	"math/big"
 	"regexp"
 	"strconv"
 	"strings"
@@ -862,6 +863,23 @@ error:
 	return nil, errors.New("Illegal numeric literal")
 }
 
+func parseBigIntLiteral(literal string) (value interface{}, err error) {
+	if literal[len(literal)-1] != 'n' {
+		return nil, fmt.Errorf("expected suffix n")
+	}
+	literal = literal[:len(literal)-1]
+
+	b := &big.Int{}
+	b, ok := b.SetString(literal, 0)
+	if ok {
+		value = b
+		return
+	} else {
+		err = errors.New("Illegal bigint literal")
+	}
+	return
+}
+
 func parseStringLiteral(literal string, length int, unicode, strict bool) (unistring.String, error) {
 	var sb strings.Builder
 	var chars []uint16
@@ -1066,7 +1084,7 @@ func (self *_parser) scanNumericLiteral(decimalPoint bool) (token.Token, string)
 				base = 8
 			case 'b', 'B':
 				base = 2
-			case '.', 'e', 'E':
+			case '.', 'e', 'E', 'n':
 				// no-op
 			default:
 				// legacy octal
@@ -1103,8 +1121,11 @@ func (self *_parser) scanNumericLiteral(decimalPoint bool) (token.Token, string)
 		}
 	}
 end:
-	if isIdentifierStart(self.chr) || isDecimalDigit(self.chr) {
+	if (isIdentifierStart(self.chr) || isDecimalDigit(self.chr)) && self.chr != 'n' {
 		return token.ILLEGAL, self.str[offset:self.chrOffset]
+	} else if self.chr == 'n' {
+		self.read()
+		return tkn, self.str[offset:self.chrOffset]
 	}
 
 	return tkn, self.str[offset:self.chrOffset]

+ 18 - 0
parser/lexer_test.go

@@ -106,6 +106,24 @@ func TestLexer(t *testing.T) {
 			token.EOF, "", 12,
 		)
 
+		test("abc = 1n / 2n",
+			token.IDENTIFIER, "abc", 1,
+			token.ASSIGN, "", 5,
+			token.NUMBER, "1n", 7,
+			token.SLASH, "", 10,
+			token.NUMBER, "2n", 12,
+			token.EOF, "", 14,
+		)
+
+		test("abc = 0x1n / 0x2n",
+			token.IDENTIFIER, "abc", 1,
+			token.ASSIGN, "", 5,
+			token.NUMBER, "0x1n", 7,
+			token.SLASH, "", 12,
+			token.NUMBER, "0x2n", 14,
+			token.EOF, "", 18,
+		)
+
 		test("xyzzy = 'Nothing happens.'",
 			token.IDENTIFIER, "xyzzy", 1,
 			token.ASSIGN, "", 7,

+ 16 - 0
parser/parser_test.go

@@ -992,6 +992,22 @@ func Test_parseNumberLiteral(t *testing.T) {
 	})
 }
 
+func Test_parseBigIntLiteral(t *testing.T) {
+	tt(t, func() {
+		test := func(input string, expect interface{}) {
+			result, err := parseBigIntLiteral(input)
+			is(err, nil)
+			is(result, expect)
+		}
+
+		test("0n", 0)
+
+		test("100n", 100)
+
+		test("0x100n", 0x100)
+	})
+}
+
 func TestPosition(t *testing.T) {
 	tt(t, func() {
 		parser := newParser("", "// Lorem ipsum")

+ 36 - 0
runtime.go

@@ -8,6 +8,7 @@ import (
 	"go/ast"
 	"hash/maphash"
 	"math"
+	"math/big"
 	"math/bits"
 	"math/rand"
 	"reflect"
@@ -53,6 +54,7 @@ type global struct {
 	Function *Object
 	String   *Object
 	Number   *Object
+	BigInt   *Object
 	Boolean  *Object
 	RegExp   *Object
 	Date     *Object
@@ -72,6 +74,8 @@ type global struct {
 	Int32Array        *Object
 	Float32Array      *Object
 	Float64Array      *Object
+	BigInt64Array     *Object
+	BigUint64Array     *Object
 
 	WeakSet *Object
 	WeakMap *Object
@@ -92,6 +96,7 @@ type global struct {
 	ObjectPrototype   *Object
 	ArrayPrototype    *Object
 	NumberPrototype   *Object
+	BigIntPrototype   *Object
 	StringPrototype   *Object
 	BooleanPrototype  *Object
 	FunctionPrototype *Object
@@ -375,6 +380,7 @@ func (r *Runtime) init() {
 	r.initString()
 	r.initGlobalObject()
 	r.initNumber()
+	r.initBigInt()
 	r.initRegExp()
 	r.initDate()
 	r.initBoolean()
@@ -731,6 +737,24 @@ func (r *Runtime) builtin_newNumber(args []Value, proto *Object) *Object {
 	return r.newPrimitiveObject(v, proto, classNumber)
 }
 
+func (r *Runtime) builtin_BigInt(call FunctionCall) Value {
+	if len(call.Arguments) > 0 {
+		return call.Arguments[0].ToBigInt()
+	} else {
+		return valueBigInt{big.NewInt(0)}
+	}
+}
+
+func (r *Runtime) builtin_newBigInt(args []Value, proto *Object) *Object {
+	var v Value
+	if len(args) > 0 {
+		v = args[0].ToBigInt()
+	} else {
+		v = valueBigInt{big.NewInt(0)}
+	}
+	return r.newPrimitiveObject(v, proto, classNumber)
+}
+
 func (r *Runtime) builtin_Boolean(call FunctionCall) Value {
 	if len(call.Arguments) > 0 {
 		if call.Arguments[0].ToBoolean() {
@@ -1029,6 +1053,11 @@ func toInt64(v Value) int64 {
 			return int64(f)
 		}
 	}
+
+	if b, ok := v.(valueBigInt); ok {
+		return b.Int64()
+	}
+
 	return 0
 }
 
@@ -1044,6 +1073,13 @@ func toUint64(v Value) uint64 {
 			return uint64(int64(f))
 		}
 	}
+
+	if b, ok := v.(valueBigInt); ok {
+		if b.Sign() < 0 {
+			return uint64(b.Int64())
+		}
+		return b.Uint64()
+	}
 	return 0
 }
 

+ 7 - 0
string_ascii.go

@@ -5,6 +5,7 @@ import (
 	"hash/maphash"
 	"io"
 	"math"
+	"math/big"
 	"reflect"
 	"strconv"
 	"strings"
@@ -175,6 +176,12 @@ func (s asciiString) ToNumber() Value {
 	return _NaN
 }
 
+func (s asciiString) ToBigInt() Value {
+	b := &big.Int{}
+	b.SetString(string(s), 0)
+	return valueBigInt{b}
+}
+
 func (s asciiString) ToObject(r *Runtime) *Object {
 	return r._newString(s, r.global.StringPrototype)
 }

+ 7 - 0
string_unicode.go

@@ -6,6 +6,7 @@ import (
 	"hash/maphash"
 	"io"
 	"math"
+	"math/big"
 	"reflect"
 	"strings"
 	"unicode/utf16"
@@ -331,6 +332,12 @@ func (s unicodeString) ToNumber() Value {
 	return asciiString(s.toTrimmedUTF8()).ToNumber()
 }
 
+func (s unicodeString) ToBigInt() Value {
+	b := &big.Int{}
+	b.SetString(s.toTrimmedUTF8(), 0)
+	return valueBigInt{b}
+}
+
 func (s unicodeString) ToObject(r *Runtime) *Object {
 	return r._newString(s, r.global.StringPrototype)
 }

+ 24 - 9
tc39_test.go

@@ -59,23 +59,31 @@ var (
 		"test/language/function-code/each-param-has-own-non-shared-eval-scope.js":     true,
 
 		// These tests are out of date (fixed in https://github.com/tc39/test262/commit/7d998a098e5420cb4b6ee4a05eb8c386d750c596)
-		"test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/key-is-numericindex.js":                   true,
-		"test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/key-is-numericindex-desc-configurable.js": true,
+		"test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/key-is-numericindex.js":                          true,
+		"test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/BigInt/key-is-numericindex.js":                   true,
+		"test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/key-is-numericindex-desc-configurable.js":        true,
+		"test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/BigInt/key-is-numericindex-desc-configurable.js": true,
 
 		// Fixed in https://github.com/tc39/test262/commit/7d998a098e5420cb4b6ee4a05eb8c386d750c596
-		"test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/detached-buffer.js": true,
+		"test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/detached-buffer.js":        true,
+		"test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/BigInt/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,
+		"test/built-ins/TypedArrayConstructors/internals/Get/detached-buffer.js":                                     true,
+		"test/built-ins/TypedArrayConstructors/internals/Get/BigInt/detached-buffer.js":                              true,
+		"test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/tonumber-value-detached-buffer.js":        true,
+		"test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/BigInt/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,
+		"test/built-ins/TypedArrayConstructors/internals/Set/tonumber-value-detached-buffer.js":        true,
+		"test/built-ins/TypedArrayConstructors/internals/Set/BigInt/tonumber-value-detached-buffer.js": true,
+		"test/built-ins/TypedArrayConstructors/internals/Set/key-is-out-of-bounds.js":                  true,
+		"test/built-ins/TypedArrayConstructors/internals/Set/BigInt/key-is-out-of-bounds.js":           true,
 
 		// 167e596a649ede35df11d03cb3c093941c9cf396
-		"test/built-ins/TypedArrayConstructors/internals/Set/detached-buffer.js": true,
+		"test/built-ins/TypedArrayConstructors/internals/Set/detached-buffer.js":        true,
+		"test/built-ins/TypedArrayConstructors/internals/Set/BigInt/detached-buffer.js": true,
 
 		// Anonymous function name property (now always present)
 		"test/language/expressions/function/name.js":                         true,
@@ -298,13 +306,17 @@ 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,
 
+		// multiplicative order of evaluation
+		"test/language/expressions/multiplication/order-of-evaluation.js": true,
+		"test/language/expressions/division/order-of-evaluation.js": true,
+		"test/language/expressions/modulus/order-of-evaluation.js": true,
+
 		// generators
 		"test/annexB/built-ins/RegExp/RegExp-control-escape-russian-letter.js": true,
 	}
 
 	featuresBlackList = []string{
 		"async-iteration",
-		"BigInt",
 		"class",
 		"generators",
 		"String.prototype.replaceAll",
@@ -370,6 +382,7 @@ var (
 
 	esIdPrefixWhiteList = []string{
 		"sec-addition-*",
+		"sec-multiplicative-*",
 		"sec-array",
 		"sec-%typedarray%",
 		"sec-%typedarray%-of",
@@ -380,6 +393,8 @@ var (
 		"sec-json",
 		"sec-number",
 		"sec-math",
+		"sec-postfix-*",
+		"sec-prefix-*",
 		"sec-arraybuffer-length",
 		"sec-arraybuffer",
 		"sec-regexp",

+ 140 - 0
typedarrays.go

@@ -2,6 +2,7 @@ package goja
 
 import (
 	"math"
+	"math/big"
 	"reflect"
 	"strconv"
 	"unsafe"
@@ -63,6 +64,8 @@ type uint32Array []uint32
 type int32Array []int32
 type float32Array []float32
 type float64Array []float64
+type bigInt64Array []int64
+type bigUint64Array []uint64
 
 type typedArrayObject struct {
 	baseObject
@@ -125,6 +128,9 @@ func (a *uint8Array) getRaw(idx int) uint64 {
 }
 
 func (a *uint8Array) set(idx int, value Value) {
+	if _, ok := value.(valueBigInt); ok {
+		panic(typeError("Cannot set bigint value"))
+	}
 	(*a)[idx] = toUint8(value)
 }
 
@@ -160,6 +166,9 @@ func (a *uint8ClampedArray) getRaw(idx int) uint64 {
 }
 
 func (a *uint8ClampedArray) set(idx int, value Value) {
+	if _, ok := value.(valueBigInt); ok {
+		panic(typeError("Cannot set bigint value"))
+	}
 	(*a)[idx] = toUint8Clamp(value)
 }
 
@@ -195,6 +204,9 @@ func (a *int8Array) getRaw(idx int) uint64 {
 }
 
 func (a *int8Array) set(idx int, value Value) {
+	if _, ok := value.(valueBigInt); ok {
+		panic(typeError("Cannot set bigint value"))
+	}
 	(*a)[idx] = toInt8(value)
 }
 
@@ -230,6 +242,9 @@ func (a *uint16Array) getRaw(idx int) uint64 {
 }
 
 func (a *uint16Array) set(idx int, value Value) {
+	if _, ok := value.(valueBigInt); ok {
+		panic(typeError("Cannot set bigint value"))
+	}
 	(*a)[idx] = toUint16(value)
 }
 
@@ -265,6 +280,9 @@ func (a *int16Array) getRaw(idx int) uint64 {
 }
 
 func (a *int16Array) set(idx int, value Value) {
+	if _, ok := value.(valueBigInt); ok {
+		panic(typeError("Cannot set bigint value"))
+	}
 	(*a)[idx] = toInt16(value)
 }
 
@@ -300,6 +318,9 @@ func (a *uint32Array) getRaw(idx int) uint64 {
 }
 
 func (a *uint32Array) set(idx int, value Value) {
+	if _, ok := value.(valueBigInt); ok {
+		panic(typeError("Cannot set bigint value"))
+	}
 	(*a)[idx] = toUint32(value)
 }
 
@@ -335,6 +356,9 @@ func (a *int32Array) getRaw(idx int) uint64 {
 }
 
 func (a *int32Array) set(idx int, value Value) {
+	if _, ok := value.(valueBigInt); ok {
+		panic(typeError("Cannot set bigint value"))
+	}
 	(*a)[idx] = toInt32(value)
 }
 
@@ -370,6 +394,9 @@ func (a *float32Array) getRaw(idx int) uint64 {
 }
 
 func (a *float32Array) set(idx int, value Value) {
+	if _, ok := value.(valueBigInt); ok {
+		panic(typeError("Cannot set bigint value"))
+	}
 	(*a)[idx] = toFloat32(value)
 }
 
@@ -420,6 +447,9 @@ func (a *float64Array) getRaw(idx int) uint64 {
 }
 
 func (a *float64Array) set(idx int, value Value) {
+	if _, ok := value.(valueBigInt); ok {
+		panic(typeError("Cannot set bigint value"))
+	}
 	(*a)[idx] = value.ToFloat()
 }
 
@@ -447,6 +477,108 @@ func (a *float64Array) typeMatch(v Value) bool {
 	return false
 }
 
+func (a *bigInt64Array) get(idx int) Value {
+	x := (*a)[idx]
+	return valueBigInt{big.NewInt(x)}
+}
+
+func (a *bigInt64Array) getRaw(idx int) uint64 {
+	return uint64((*a)[idx])
+}
+
+func (a *bigInt64Array) set(idx int, value Value) {
+	switch value.(type) {
+	case valueNull, valueUndefined:
+		panic(typeError("Cannot set an empty value to a bigint64"))
+	case valueInt, valueFloat:
+		panic(typeError("bigint argument required"))
+	}
+	(*a)[idx] = toInt64(value)
+}
+
+func (a *bigInt64Array) toRaw(v Value) uint64 {
+	switch v.(type) {
+	case valueNull, valueUndefined:
+		panic(typeError("Cannot convert an empty value to a bigint64"))
+	case valueString:
+		i, err := strconv.ParseInt(v.String(), 0, 64)
+		if err != nil {
+			panic(typeError("Cannot convert string to bigint"))
+		}
+		return uint64(i)
+	}
+	return uint64(toInt64(v))
+}
+
+func (a *bigInt64Array) setRaw(idx int, v uint64) {
+	(*a)[idx] = int64(v)
+}
+
+func (a *bigInt64Array) less(i, j int) bool {
+	return (*a)[i] < (*a)[j]
+}
+
+func (a *bigInt64Array) swap(i, j int) {
+	(*a)[i], (*a)[j] = (*a)[j], (*a)[i]
+}
+
+func (a *bigInt64Array) typeMatch(v Value) bool {
+	if _, ok := v.(valueBigInt); ok {
+		return true
+	}
+	return false
+}
+
+func (a *bigUint64Array) get(idx int) Value {
+	x := (*a)[idx]
+	b := &big.Int{}
+	b.SetUint64(x)
+	return valueBigInt{b}
+}
+
+func (a *bigUint64Array) getRaw(idx int) uint64 {
+	return uint64((*a)[idx])
+}
+
+func (a *bigUint64Array) set(idx int, value Value) {
+	switch value.(type) {
+	case valueNull, valueUndefined:
+		panic(typeError("Cannot set an empty value to a biguint64"))
+	case valueInt, valueFloat:
+		panic(typeError("bigint argument required"))
+	}
+	(*a)[idx] = toUint64(value)
+}
+
+func (a *bigUint64Array) toRaw(v Value) uint64 {
+	switch v.(type) {
+	case valueNull, valueUndefined:
+		panic(typeError("Cannot convert an empty value to a biguint64"))
+	case valueInt, valueFloat:
+		panic(typeError("bigint argument required"))
+	}
+	return uint64(toUint64(v))
+}
+
+func (a *bigUint64Array) setRaw(idx int, v uint64) {
+	(*a)[idx] = uint64(v)
+}
+
+func (a *bigUint64Array) less(i, j int) bool {
+	return (*a)[i] < (*a)[j]
+}
+
+func (a *bigUint64Array) swap(i, j int) {
+	(*a)[i], (*a)[j] = (*a)[j], (*a)[i]
+}
+
+func (a *bigUint64Array) typeMatch(v Value) bool {
+	if i, ok := v.(valueBigInt); ok {
+		return i.Sign() >= 0 && true
+	}
+	return false
+}
+
 func (a *typedArrayObject) _getIdx(idx int) Value {
 	if 0 <= idx && idx < a.length {
 		if !a.viewedArrayBuf.ensureNotDetached(false) {
@@ -714,6 +846,14 @@ func (r *Runtime) newFloat64ArrayObject(buf *arrayBufferObject, offset, length i
 	return r._newTypedArrayObject(buf, offset, length, 8, r.global.Float64Array, (*float64Array)(unsafe.Pointer(&buf.data)), proto)
 }
 
+func (r *Runtime) newBigInt64ArrayObject(buf *arrayBufferObject, offset, length int, proto *Object) *typedArrayObject {
+	return r._newTypedArrayObject(buf, offset, length, 8, r.global.BigInt64Array, (*bigInt64Array)(unsafe.Pointer(&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)(unsafe.Pointer(&buf.data)), proto)
+}
+
 func (o *dataViewObject) getIdxAndByteOrder(idxVal, littleEndianVal Value, size int) (int, byteOrder) {
 	getIdx := o.val.runtime.toIndex(idxVal)
 	o.viewedArrayBuf.ensureNotDetached(true)

+ 140 - 0
value.go

@@ -3,6 +3,7 @@ package goja
 import (
 	"hash/maphash"
 	"math"
+	"math/big"
 	"reflect"
 	"strconv"
 	"unsafe"
@@ -46,6 +47,7 @@ var (
 	reflectTypeBool   = reflect.TypeOf(false)
 	reflectTypeNil    = reflect.TypeOf(nil)
 	reflectTypeFloat  = reflect.TypeOf(float64(0))
+	reflectTypeBigInt = reflect.TypeOf(big.Int{})
 	reflectTypeMap    = reflect.TypeOf(map[string]interface{}{})
 	reflectTypeArray  = reflect.TypeOf([]interface{}{})
 	reflectTypeString = reflect.TypeOf("")
@@ -61,6 +63,7 @@ type Value interface {
 	String() string
 	ToFloat() float64
 	ToNumber() Value
+	ToBigInt() Value
 	ToBoolean() bool
 	ToObject(*Runtime) *Object
 	SameAs(Value) bool
@@ -84,6 +87,9 @@ type referenceError string
 
 type valueInt int64
 type valueFloat float64
+type valueBigInt struct {
+	*big.Int
+}
 type valueBool bool
 type valueNull struct{}
 type valueUndefined struct {
@@ -190,6 +196,10 @@ func (i valueInt) ToNumber() Value {
 	return i
 }
 
+func (i valueInt) ToBigInt() Value {
+	return valueBigInt{big.NewInt(int64(i))}
+}
+
 func (i valueInt) SameAs(other Value) bool {
 	return i == other
 }
@@ -289,6 +299,13 @@ func (b valueBool) ToNumber() Value {
 	return valueInt(0)
 }
 
+func (b valueBool) ToBigInt() Value {
+	if b {
+		return valueBigInt{big.NewInt(1)}
+	}
+	return valueBigInt{big.NewInt(0)}
+}
+
 func (b valueBool) SameAs(other Value) bool {
 	if other, ok := other.(valueBool); ok {
 		return b == other
@@ -376,6 +393,10 @@ func (u valueUndefined) ToNumber() Value {
 	return _NaN
 }
 
+func (u valueUndefined) ToBigInt() Value {
+	panic("RangeError: not a bigint")
+}
+
 func (u valueUndefined) SameAs(other Value) bool {
 	_, same := other.(valueUndefined)
 	return same
@@ -412,6 +433,10 @@ func (n valueNull) ToNumber() Value {
 	return intToValue(0)
 }
 
+func (n valueNull) ToBigInt() Value {
+	panic(typeError("Cannot convert null to BigInt"))
+}
+
 func (n valueNull) SameAs(other Value) bool {
 	_, same := other.(valueNull)
 	return same
@@ -482,6 +507,10 @@ func (p *valueProperty) ToNumber() Value {
 	return nil
 }
 
+func (p *valueProperty) ToBigInt() Value {
+	return nil
+}
+
 func (p *valueProperty) isWritable() bool {
 	return p.writable || p.setterFunc != nil
 }
@@ -591,6 +620,11 @@ func (f valueFloat) ToNumber() Value {
 	return f
 }
 
+func (f valueFloat) ToBigInt() Value {
+	i := floatToIntClip(float64(f))
+	return valueBigInt{big.NewInt(i)}
+}
+
 func (f valueFloat) SameAs(other Value) bool {
 	switch o := other.(type) {
 	case valueFloat:
@@ -662,6 +696,99 @@ func (f valueFloat) hash(*maphash.Hash) uint64 {
 	return math.Float64bits(float64(f))
 }
 
+func (b valueBigInt) ToInteger() int64 {
+	return b.Int64()
+}
+
+func (b valueBigInt) toString() valueString {
+	return asciiString(b.String())
+}
+
+func (b valueBigInt) toPrimitiveNumber() Value {
+	return b
+}
+
+func (b valueBigInt) string() unistring.String {
+	return unistring.String(b.String())
+}
+
+func (b valueBigInt) ToString() Value {
+	return b
+}
+
+func (b valueBigInt) String() string {
+	return b.Int.String()
+}
+
+func (b valueBigInt) ToFloat() float64 {
+	return float64(b.Int64())
+}
+
+func (b valueBigInt) ToBoolean() bool {
+	return b.Int64() != 0
+}
+
+func (b valueBigInt) ToObject(r *Runtime) *Object {
+	return r.newPrimitiveObject(b, r.global.BigIntPrototype, "BigInt")
+}
+
+func (b valueBigInt) ToNumber() Value {
+	return b
+}
+
+func (b valueBigInt) ToBigInt() Value {
+	return b
+}
+
+func (b valueBigInt) SameAs(other Value) bool {
+	v, ok := other.(valueBigInt)
+	return ok && b.Int.Cmp(v.Int) == 0
+
+	return false
+}
+
+func (b valueBigInt) Equals(other Value) bool {
+	switch o := other.(type) {
+	case valueBigInt:
+		return b.Cmp(o.Int) == 0
+	case valueFloat:
+		return float64(b.Int64()) == float64(o)
+	case valueInt:
+		return b.Int64() == int64(o)
+	case valueString, valueBool:
+		return b.Int64() == o.ToInteger()
+	case *Object:
+		return b.Equals(o.toPrimitive())
+	}
+
+	return false
+}
+
+func (b valueBigInt) StrictEquals(other Value) bool {
+	switch o := other.(type) {
+	case valueBigInt:
+		return b.Cmp(o.Int) == 0
+	}
+
+	return false
+}
+
+func (b valueBigInt) baseObject(r *Runtime) *Object {
+	return r.global.BigIntPrototype
+}
+
+func (b valueBigInt) Export() interface{} {
+	return (*big.Int)(b.Int)
+}
+
+func (b valueBigInt) ExportType() reflect.Type {
+	return reflectTypeBigInt
+}
+
+func (b valueBigInt) hash(*maphash.Hash) uint64 {
+	return uint64(b.Int64())
+}
+
 func (o *Object) ToInteger() int64 {
 	return o.toPrimitiveNumber().ToNumber().ToInteger()
 }
@@ -698,6 +825,10 @@ func (o *Object) ToNumber() Value {
 	return o.toPrimitiveNumber().ToNumber()
 }
 
+func (o *Object) ToBigInt() Value {
+	return o.toPrimitiveBigInt().ToBigInt()
+}
+
 func (o *Object) SameAs(other Value) bool {
 	if other, ok := other.(*Object); ok {
 		return o == other
@@ -948,6 +1079,11 @@ func (o valueUnresolved) ToNumber() Value {
 	return nil
 }
 
+func (o valueUnresolved) ToBigInt() Value {
+	o.throw()
+	return nil
+}
+
 func (o valueUnresolved) SameAs(Value) bool {
 	o.throw()
 	return false
@@ -1017,6 +1153,10 @@ func (s *Symbol) ToNumber() Value {
 	panic(typeError("Cannot convert a Symbol value to a number"))
 }
 
+func (s *Symbol) ToBigInt() Value {
+	panic(typeError("Cannot convert a Symbol value to a bigint"))
+}
+
 func (s *Symbol) ToBoolean() bool {
 	return true
 }

+ 130 - 18
vm.go

@@ -3,6 +3,7 @@ package goja
 import (
 	"fmt"
 	"math"
+	"math/big"
 	"runtime"
 	"strconv"
 	"strings"
@@ -214,6 +215,14 @@ func assertInt64(v Value) (int64, bool) {
 	return 0, false
 }
 
+func assertBigInt(v Value) (*big.Int, bool) {
+	num := v.ToNumber()
+	if b, ok := num.(valueBigInt); ok {
+		return b.Int, true
+	}
+	return big.NewInt(0), false
+}
+
 func toIntIgnoreNegZero(v Value) (int64, bool) {
 	num := v.ToNumber()
 	if i, ok := num.(valueInt); ok {
@@ -897,6 +906,16 @@ func (_add) exec(vm *vm) {
 			rightString = right.toString()
 		}
 		ret = leftString.concat(rightString)
+	} else if leftBigInt, ok := left.(valueBigInt); ok {
+		rightBigInt, ok := right.(valueBigInt)
+		if !ok {
+			panic(vm.r.NewTypeError("Cannot add BigInt to other type"))
+		}
+		b := &big.Int{}
+		b.Add(leftBigInt.Int, rightBigInt.Int)
+		ret = valueBigInt{b}
+	} else if _, ok := right.(valueBigInt); ok {
+		panic(vm.r.NewTypeError("Cannot add BigInt to other type"))
 	} else {
 		if leftInt, ok := left.(valueInt); ok {
 			if rightInt, ok := right.(valueInt); ok {
@@ -924,13 +943,21 @@ func (_sub) exec(vm *vm) {
 
 	var result Value
 
-	if left, ok := left.(valueInt); ok {
-		if right, ok := right.(valueInt); ok {
-			result = intToValue(int64(left) - int64(right))
+	if leftInt, ok := left.(valueInt); ok {
+		if rightInt, ok := right.(valueInt); ok {
+			result = intToValue(int64(leftInt) - int64(rightInt))
+			goto end
+		}
+	} else if leftBigInt, ok := left.(valueBigInt); ok {
+		if rightBigInt, ok := right.(valueBigInt); ok {
+			b := &big.Int{}
+			b.Sub(leftBigInt.Int, rightBigInt.Int)
+			result = valueBigInt{b}
 			goto end
 		}
+	} else if _, ok := right.(valueBigInt); ok {
+		panic(vm.r.NewTypeError("Cannot subtract BigInt from other type"))
 	}
-
 	result = floatToValue(left.ToFloat() - right.ToFloat())
 end:
 	vm.sp--
@@ -946,8 +973,28 @@ func (_mul) exec(vm *vm) {
 	left := vm.stack[vm.sp-2]
 	right := vm.stack[vm.sp-1]
 
+	if o, ok := left.(*Object); ok {
+		left = o.toPrimitive()
+	}
+
+	if o, ok := right.(*Object); ok {
+		right = o.toPrimitive()
+	}
+
 	var result Value
 
+	if leftBigInt, ok := left.(valueBigInt); ok {
+		if rightBigInt, ok := right.(valueBigInt); ok {
+			b := &big.Int{}
+			b.Mul(leftBigInt.Int, rightBigInt.Int)
+			result = valueBigInt{b}
+			goto end
+		} else {
+			panic(vm.r.NewTypeError("Cannot multiply BigInt with other type"))
+		}
+	} else if _, ok := right.(valueBigInt); ok {
+		panic(vm.r.NewTypeError("Cannot multiply BigInt with other type"))
+	}
 	if left, ok := assertInt64(left); ok {
 		if right, ok := assertInt64(right); ok {
 			if left == 0 && right == -1 || left == -1 && right == 0 {
@@ -977,26 +1024,55 @@ 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()
+	left := vm.stack[vm.sp-2]
+	right := vm.stack[vm.sp-1]
+
+	if o, ok := left.(*Object); ok {
+		left = o.toPrimitive()
+	}
+
+	if o, ok := right.(*Object); ok {
+		right = o.toPrimitive()
+	}
 
 	var result Value
+	var rightFloat float64
+	var leftFloat float64
+
+	if leftBigInt, ok := left.(valueBigInt); ok {
+		rightBigInt, ok := right.(valueBigInt)
+		if !ok {
+			panic(vm.r.NewTypeError("Cannot divide BigInt by other type"))
+		}
+		if rightBigInt.Int64() == 0 {
+			panic(vm.r.newError(vm.r.global.RangeError, "Cannot divide by zero"))
+		}
+		b := &big.Int{}
+		b.Quo(leftBigInt.Int, rightBigInt.Int)
+		result = valueBigInt{b}
+		goto end
+	} else if _, ok := right.(valueBigInt); ok {
+		panic(vm.r.NewTypeError("Cannot divide BigInt by other type"))
+	}
+
+	leftFloat = left.ToFloat()
+	rightFloat = right.ToFloat()
 
-	if math.IsNaN(left) || math.IsNaN(right) {
+	if math.IsNaN(leftFloat) || math.IsNaN(rightFloat) {
 		result = _NaN
 		goto end
 	}
-	if math.IsInf(left, 0) && math.IsInf(right, 0) {
+	if math.IsInf(leftFloat, 0) && math.IsInf(rightFloat, 0) {
 		result = _NaN
 		goto end
 	}
-	if left == 0 && right == 0 {
+	if leftFloat == 0 && rightFloat == 0 {
 		result = _NaN
 		goto end
 	}
 
-	if math.IsInf(left, 0) {
-		if math.Signbit(left) == math.Signbit(right) {
+	if math.IsInf(leftFloat, 0) {
+		if math.Signbit(leftFloat) == math.Signbit(rightFloat) {
 			result = _positiveInf
 			goto end
 		} else {
@@ -1004,8 +1080,8 @@ func (_div) exec(vm *vm) {
 			goto end
 		}
 	}
-	if math.IsInf(right, 0) {
-		if math.Signbit(left) == math.Signbit(right) {
+	if math.IsInf(rightFloat, 0) {
+		if math.Signbit(leftFloat) == math.Signbit(rightFloat) {
 			result = _positiveZero
 			goto end
 		} else {
@@ -1013,8 +1089,8 @@ func (_div) exec(vm *vm) {
 			goto end
 		}
 	}
-	if right == 0 {
-		if math.Signbit(left) == math.Signbit(right) {
+	if rightFloat == 0 {
+		if math.Signbit(leftFloat) == math.Signbit(rightFloat) {
 			result = _positiveInf
 			goto end
 		} else {
@@ -1023,7 +1099,7 @@ func (_div) exec(vm *vm) {
 		}
 	}
 
-	result = floatToValue(left / right)
+	result = floatToValue(leftFloat / rightFloat)
 
 end:
 	vm.sp--
@@ -1039,9 +1115,31 @@ func (_mod) exec(vm *vm) {
 	left := vm.stack[vm.sp-2]
 	right := vm.stack[vm.sp-1]
 
+	if o, ok := left.(*Object); ok {
+		left = o.toPrimitive()
+	}
+
+	if o, ok := right.(*Object); ok {
+		right = o.toPrimitive()
+	}
+
 	var result Value
 
-	if leftInt, ok := assertInt64(left); ok {
+	if leftBigInt, ok := left.(valueBigInt); ok {
+		rightBigInt, ok := right.(valueBigInt)
+		if !ok {
+			panic(vm.r.NewTypeError("Cannot get modulus of BigInt with other type"))
+		}
+		if rightBigInt.Int64() == 0 {
+			panic(vm.r.newError(vm.r.global.RangeError, "Cannot divide by zero"))
+		}
+		b := &big.Int{}
+		b.Rem(leftBigInt.Int, rightBigInt.Int)
+		result = valueBigInt{b}
+		goto end
+	} else if _, ok := right.(valueBigInt); ok {
+		panic(vm.r.NewTypeError("Cannot get modulus of BigInt with other type"))
+	} else if leftInt, ok := assertInt64(left); ok {
 		if rightInt, ok := assertInt64(right); ok {
 			if rightInt == 0 {
 				result = _NaN
@@ -1073,7 +1171,11 @@ func (_neg) exec(vm *vm) {
 
 	var result Value
 
-	if i, ok := assertInt64(operand); ok {
+	if b, ok := assertBigInt(operand); ok {
+		nb := &big.Int{}
+		nb.Neg(b)
+		result = valueBigInt{nb}
+	} else if i, ok := assertInt64(operand); ok {
 		if i == 0 {
 			result = _negativeZero
 		} else {
@@ -1110,6 +1212,11 @@ func (_inc) exec(vm *vm) {
 	if i, ok := assertInt64(v); ok {
 		v = intToValue(i + 1)
 		goto end
+	} else if bi, ok := v.(valueBigInt); ok {
+		b := &big.Int{}
+		b.Add(bi.Int, big.NewInt(1))
+		v = valueBigInt{b}
+		goto end
 	}
 
 	v = valueFloat(v.ToFloat() + 1)
@@ -1129,6 +1236,11 @@ func (_dec) exec(vm *vm) {
 	if i, ok := assertInt64(v); ok {
 		v = intToValue(i - 1)
 		goto end
+	} else if bi, ok := v.(valueBigInt); ok {
+		b := &big.Int{}
+		b.Sub(bi.Int, big.NewInt(1))
+		v = valueBigInt{b}
+		goto end
 	}
 
 	v = valueFloat(v.ToFloat() - 1)