Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic implementation of BigInt, BigInt64Array and BigUint64Array #338

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions ast/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,12 @@ type (
Value interface{}
}

BigIntLiteral struct {
Idx file.Idx
Literal string
Value interface{}
}

ObjectLiteral struct {
LeftBrace file.Idx
RightBrace file.Idx
Expand Down Expand Up @@ -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() {}
Expand Down Expand Up @@ -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() }
Expand Down Expand Up @@ -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)) }
Expand Down
53 changes: 53 additions & 0 deletions builtin_bigint.go
Original file line number Diff line number Diff line change
@@ -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)
}
10 changes: 9 additions & 1 deletion builtin_string.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
40 changes: 39 additions & 1 deletion builtin_typedarrays.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"math"
"sort"
"strconv"
"unsafe"

"github.com/dop251/goja/unistring"
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
}
}
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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)
}
26 changes: 24 additions & 2 deletions compiler_expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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 {
Expand All @@ -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) {
Expand Down Expand Up @@ -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),
Expand Down
4 changes: 4 additions & 0 deletions destruct.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
27 changes: 27 additions & 0 deletions object.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const (
classSet = "Set"
classFunction = "Function"
classNumber = "Number"
classBigInt = "BigInt"
classString = "String"
classBoolean = "Boolean"
classError = "Error"
Expand All @@ -39,6 +40,7 @@ const (
var (
hintDefault Value = asciiString("default")
hintNumber Value = asciiString("number")
hintBigInt Value = asciiString("bigint")
hintString Value = asciiString("string")
)

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions object_dynamic.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
6 changes: 6 additions & 0 deletions object_lazy.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
39 changes: 32 additions & 7 deletions parser/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -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:
Expand Down
Loading