This commit is contained in:
Star 2024-10-10 16:44:10 +08:00
commit 702ea0fbe9
104 changed files with 57561 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.*
!.gitignore
go.sum

9
LICENSE Normal file
View File

@ -0,0 +1,9 @@
MIT License
Copyright (c) 2024 apigo
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

82
README.md Normal file
View File

@ -0,0 +1,82 @@
# GoJS
The project is a low-code framework implemented in Go, with a low-code language written in JavaScript. The framework allows developers to create modules in Go that can be called from the JavaScript code. The entire project can be compiled into a standalone executable file, making it easy to deploy and use.
## create a module
```go
package apigo.cc/yourorg/modulename
import "apigo.cc/gojs"
func init() {
defaultObject := Object{id: "o-00"}
gojs.Register("apigo.cc/yourorg/modulename", gojs.Module{
Object: map[string]any{
"name": "abc",
"plus": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
args := gojs.MakeArgs(&argsIn, vm).Check(2)
return vm.ToValue(args.Int(0) + args.Int(1))
},
"getName": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
args := gojs.MakeArgs(&argsIn, vm)
this := args.ThisObj()
return vm.ToValue(this.Str("name"))
},
},
})
}
```
## usage for inline js code
```go
package main
import (
"fmt"
"apigo.cc/gojs"
_ "apigo.cc/yourorg/modulename"
)
func main() {
r, err := gojs.Run(`
import mod from 'apigo.cc/yourorg/modulename'
function main(args){
return mod.getName()
}
`, "test.js")
fmt.Println(r, err, r == "abc")
}
```
## usage for separate js file
main.go
```go
package main
import (
"fmt"
"apigo.cc/gojs"
_ "apigo.cc/yourorg/modulename"
)
func main() {
r, err := gojs.RunFile("plus.js", 1, 2)
fmt.Println(r, err, r == 3)
}
```
plus.js
```javascript
import mod from 'apigo.cc/yourorg/modulename'
function main(args){
return mod.plus(args[0], args[1])
}
```

344
args.go Normal file
View File

@ -0,0 +1,344 @@
package gojs
import (
"errors"
"path/filepath"
"apigo.cc/gojs/dop251/goja"
"github.com/ssgo/log"
"github.com/ssgo/u"
)
type Arr struct {
This goja.Value
Arguments []goja.Value
vm *goja.Runtime
Logger *log.Logger
}
type Obj struct {
This goja.Value
vm *goja.Runtime
Logger *log.Logger
O *goja.Object
}
func GetLogger(vm *goja.Runtime) *log.Logger {
var logger *log.Logger
if vm.GoData["logger"] != nil {
if logger1, ok := vm.GoData["logger"].(*log.Logger); ok {
logger = logger1
}
}
if logger == nil {
logger = log.DefaultLogger
}
return logger
}
func FindPath(vm *goja.Runtime, filename string) string {
if u.FileExists(filename) {
return filename
}
if !filepath.IsAbs(filename) {
startPath := u.String(vm.GoData["startPath"])
if startPath != "" {
tryFilename := filepath.Join(startPath, filename)
if u.FileExists(tryFilename) {
return tryFilename
}
}
}
return filename
}
func MakeArgs(args *goja.FunctionCall, vm *goja.Runtime) *Arr {
return &Arr{
This: args.This,
Arguments: args.Arguments,
vm: vm,
Logger: GetLogger(vm),
}
}
func (args *Arr) ThisObj() *Obj {
return &Obj{
This: args.This,
vm: args.vm,
O: args.This.ToObject(args.vm),
Logger: GetLogger(args.vm),
}
}
func (args *Arr) Check(num int) *Arr {
if len(args.Arguments) < num {
panic(args.vm.NewGoError(errors.New("arguments need " + u.String(num) + ", but given " + u.String(len(args.Arguments)))))
}
return args
}
func (args *Arr) Int(index int) int {
if len(args.Arguments) > index {
return u.Int(args.Arguments[index].Export())
}
return 0
}
func (args *Arr) Int64(index int) int64 {
if len(args.Arguments) > index {
return u.Int64(args.Arguments[index].Export())
}
return 0
}
func (args *Arr) Any(index int) any {
if len(args.Arguments) > index {
return args.Arguments[index].Export()
}
return nil
}
func (args *Arr) Str(index int) string {
if len(args.Arguments) > index {
return u.String(args.Arguments[index].Export())
}
return ""
}
func (args *Arr) Bytes(index int) []byte {
if len(args.Arguments) > index {
return u.Bytes(args.Arguments[index].Export())
}
return []byte{}
}
func (args *Arr) Bool(index int) bool {
if len(args.Arguments) > index {
return u.Bool(args.Arguments[index].Export())
}
return false
}
func (args *Arr) Map(index int) map[string]any {
out := map[string]any{}
if len(args.Arguments) > index {
u.Convert(args.Arguments[index].Export(), &out)
}
return out
}
func (args *Arr) StrArray(startIndex int) []string {
if len(args.Arguments) > startIndex {
a := make([]string, len(args.Arguments)-startIndex)
for index := startIndex; index < len(args.Arguments); index++ {
a[index-startIndex] = u.String(args.Arguments[index].Export())
}
return a
}
return make([]string, 0)
}
func (args *Arr) Array(startIndex int) []any {
if len(args.Arguments) > startIndex {
a := make([]any, len(args.Arguments)-startIndex)
for index := startIndex; index < len(args.Arguments); index++ {
a[index-startIndex] = u.String(args.Arguments[index].Export())
}
return a
}
return make([]any, 0)
}
func (args *Arr) Map2StrArray(index int) []string {
headerMap := args.Map(index)
headers := make([]string, len(headerMap)*2)
i := 0
for k, v := range headerMap {
headers[i] = k
headers[i+1] = u.String(v)
i += 2
}
return headers
}
func (args *Arr) Map2Array(index int) []any {
arrMap := args.Map(index)
arr := make([]any, len(arrMap)*2)
i := 0
for k, v := range arrMap {
arr[i] = k
arr[i+1] = u.String(v)
i += 2
}
return arr
}
func (args *Arr) Path(index int) string {
return FindPath(args.vm, args.Str(index))
}
func GetFunc(v goja.Value) goja.Callable {
if cb, ok := goja.AssertFunction(v); ok {
return cb
}
return nil
}
func (args *Arr) Func(index int) goja.Callable {
if len(args.Arguments) > index {
return GetFunc(args.Arguments[index])
}
return nil
}
func (args *Arr) Obj(index int) *Obj {
if len(args.Arguments) > index {
return &Obj{
This: args.This,
vm: args.vm,
Logger: args.Logger,
O: args.Arguments[index].ToObject(args.vm),
}
}
return nil
}
func (args *Arr) Arr(index int) *Arr {
arr := make([]goja.Value, 0)
if len(args.Arguments) > index {
v := args.Arguments[index]
if v != nil {
args.vm.ForOf(v, func(v goja.Value) bool {
arr = append(arr, v)
return true
})
}
}
return &Arr{
This: args.This,
Arguments: arr,
vm: args.vm,
Logger: args.Logger,
}
}
func (args *Arr) Object(index int) *goja.Object {
if len(args.Arguments) > index {
return args.Arguments[index].ToObject(args.vm)
}
return nil
}
// -------- Object
func (obj *Obj) Get(name string) goja.Value {
return obj.O.Get(name)
}
func (obj *Obj) Int(name string) int {
v := obj.O.Get(name)
if v != nil {
return u.Int(v.Export())
}
return 0
}
func (obj *Obj) Int64(name string) int64 {
v := obj.O.Get(name)
if v != nil {
return u.Int64(v.Export())
}
return 0
}
func (obj *Obj) Any(name string) any {
v := obj.O.Get(name)
if v != nil {
return v.Export()
}
return nil
}
func (obj *Obj) Str(name string) string {
v := obj.O.Get(name)
if v != nil {
return u.String(v.Export())
}
return ""
}
func (obj *Obj) Bytes(name string) []byte {
v := obj.O.Get(name)
if v != nil {
return u.Bytes(v.Export())
}
return []byte{}
}
func (obj *Obj) Bool(name string) bool {
v := obj.O.Get(name)
if v != nil {
return u.Bool(v.Export())
}
return false
}
func (obj *Obj) Map(name string) map[string]any {
v := obj.O.Get(name)
out := map[string]any{}
if v != nil {
u.Convert(v.Export(), &out)
}
return out
}
func (obj *Obj) Path(name string) string {
return FindPath(obj.vm, obj.Str(name))
}
func (obj *Obj) Func(name string) goja.Callable {
v := obj.O.Get(name)
if v != nil {
return GetFunc(v)
}
return nil
}
func (obj *Obj) Obj(name string) *goja.Object {
v := obj.O.Get(name)
if v != nil {
return v.ToObject(obj.vm)
}
return nil
}
func (obj *Obj) Arr(name string) *Arr {
v := obj.O.Get(name)
arr := make([]goja.Value, 0)
if v != nil {
obj.vm.ForOf(v, func(v goja.Value) bool {
arr = append(arr, v)
return true
})
}
return &Arr{
This: obj.This,
Arguments: arr,
vm: obj.vm,
Logger: obj.Logger,
}
}
func (obj *Obj) Array(name string) []any {
v := obj.O.Get(name)
if v != nil {
arr := make([]any, 0)
obj.vm.ForOf(v, func(v goja.Value) bool {
arr = append(arr, v.Export())
return true
})
return arr
}
return nil
}

51
common.go Normal file
View File

@ -0,0 +1,51 @@
package gojs
import (
"reflect"
"apigo.cc/gojs/dop251/goja"
"github.com/ssgo/log"
"github.com/ssgo/u"
)
func MakeLogger(logger *log.Logger) Map {
return map[string]any{
"info": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
args := MakeArgs(&argsIn, vm).Check(1)
logger.Info(args.Str(0), args.Map2Array(1)...)
return nil
},
"warn": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
args := MakeArgs(&argsIn, vm).Check(1)
logger.Warning(args.Str(0), args.Map2Array(1)...)
return nil
},
"error": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
args := MakeArgs(&argsIn, vm).Check(1)
logger.Error(args.Str(0), args.Map2Array(1)...)
return nil
},
"debug": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
args := MakeArgs(&argsIn, vm).Check(1)
logger.Debug(args.Str(0), args.Map2Array(1)...)
return nil
},
}
}
func MakeMap(structObj any) Map {
m := Map{}
from := u.RealValue(reflect.ValueOf(structObj))
if from.Kind() == reflect.Struct {
st := u.FlatStruct(structObj)
for k, v := range st.Values {
k = u.GetLowerName(k)
m[k] = v.Interface()
}
for k, v := range st.MethodValues {
k = u.GetLowerName(k)
m[k] = v.Interface()
}
}
return m
}

View File

@ -0,0 +1,22 @@
// Code generated by generate_decode_map.go. DO NOT EDIT.
package base64dec
var decodeMap = [256]byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0x3e, 0xff, 0x3f,
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0x3f,
0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
}

223
dop251/base64dec/decoder.go Normal file
View File

@ -0,0 +1,223 @@
//go:generate go run generate_decode_map.go
// Package base64dec contains a universal base64 decoder that works on both the standard and url-safe variants, padded and raw.
// The code is based on the standard encoding/base64 package.
package base64dec
import (
"encoding/base64"
"encoding/binary"
"strconv"
)
const padChar = '='
type ByteSeq interface {
[]byte | string
}
// DecodeBase64 decodes src and writes at most base64.RawStdEncoding.DecodedLen(len(src))
// bytes to dst and returns the number of bytes written. If src contains invalid base64 data, it will return the
// number of bytes successfully written and base64.CorruptInputError.
// New line characters (\r and \n) are ignored.
// The input can be in the standard or the alternate (aka url-safe) encoding. It can be padded or un-padded.
// If there is a correct padding, it is consumed and no error is returned. If there is no padding where it's required,
// no error is returned. If there is an incorrect padding (i.e. too many or too few characters) it is treated
// as garbage at the end (i.e. the error will point to the first padding character).
func DecodeBase64[T ByteSeq](dst []byte, src T) (n int, err error) {
if len(src) == 0 {
return 0, nil
}
si := 0
for strconv.IntSize >= 64 && len(src)-si >= 8 && len(dst)-n >= 8 {
src2 := src[si : si+8]
if dn, ok := assemble64(
decodeMap[src2[0]],
decodeMap[src2[1]],
decodeMap[src2[2]],
decodeMap[src2[3]],
decodeMap[src2[4]],
decodeMap[src2[5]],
decodeMap[src2[6]],
decodeMap[src2[7]],
); ok {
binary.BigEndian.PutUint64(dst[n:], dn)
n += 6
si += 8
} else {
var ninc int
si, ninc, err = decodeQuantum(dst[n:], src, si)
n += ninc
if err != nil {
return n, err
}
}
}
for len(src)-si >= 4 && len(dst)-n >= 4 {
src2 := src[si : si+4]
if dn, ok := assemble32(
decodeMap[src2[0]],
decodeMap[src2[1]],
decodeMap[src2[2]],
decodeMap[src2[3]],
); ok {
binary.BigEndian.PutUint32(dst[n:], dn)
n += 3
si += 4
} else {
var ninc int
si, ninc, err = decodeQuantum(dst[n:], src, si)
n += ninc
if err != nil {
return n, err
}
}
}
for si < len(src) {
var ninc int
si, ninc, err = decodeQuantum(dst[n:], src, si)
n += ninc
if err != nil {
return n, err
}
}
return n, err
}
// assemble32 assembles 4 base64 digits into 3 bytes.
// Each digit comes from the decode map, and will be 0xff
// if it came from an invalid character.
func assemble32(n1, n2, n3, n4 byte) (dn uint32, ok bool) {
// Check that all the digits are valid. If any of them was 0xff, their
// bitwise OR will be 0xff.
if n1|n2|n3|n4 == 0xff {
return 0, false
}
return uint32(n1)<<26 |
uint32(n2)<<20 |
uint32(n3)<<14 |
uint32(n4)<<8,
true
}
// assemble64 assembles 8 base64 digits into 6 bytes.
// Each digit comes from the decode map, and will be 0xff
// if it came from an invalid character.
func assemble64(n1, n2, n3, n4, n5, n6, n7, n8 byte) (dn uint64, ok bool) {
// Check that all the digits are valid. If any of them was 0xff, their
// bitwise OR will be 0xff.
if n1|n2|n3|n4|n5|n6|n7|n8 == 0xff {
return 0, false
}
return uint64(n1)<<58 |
uint64(n2)<<52 |
uint64(n3)<<46 |
uint64(n4)<<40 |
uint64(n5)<<34 |
uint64(n6)<<28 |
uint64(n7)<<22 |
uint64(n8)<<16,
true
}
// decodeQuantum decodes up to 4 base64 bytes. The received parameters are
// the destination buffer dst, the source buffer src and an index in the
// source buffer si.
// It returns the number of bytes read from src, the number of bytes written
// to dst, and an error, if any.
func decodeQuantum[T ByteSeq](dst []byte, src T, si int) (nsi, n int, err error) {
// Decode quantum using the base64 alphabet
var dbuf [4]byte
dlen := 4
for j := 0; j < len(dbuf); j++ {
if len(src) == si {
if j == 0 {
return si, 0, nil
}
dlen = j
break
}
in := src[si]
si++
out := decodeMap[in]
if out != 0xff {
dbuf[j] = out
continue
}
if in == '\n' || in == '\r' {
j--
continue
}
dlen = j
if rune(in) != padChar {
err = base64.CorruptInputError(si - 1)
break
}
// We've reached the end and there's padding
switch j {
case 0, 1:
// incorrect padding
err = base64.CorruptInputError(si - 1)
case 2:
// "==" is expected, the first "=" is already consumed.
// skip over newlines
for si < len(src) && (src[si] == '\n' || src[si] == '\r') {
si++
}
if si == len(src) {
// not enough padding
err = base64.CorruptInputError(si - 1)
break
} else if rune(src[si]) != padChar {
// incorrect padding
err = base64.CorruptInputError(si - 1)
break
}
si++
}
if err == nil {
// skip over newlines
for si < len(src) && (src[si] == '\n' || src[si] == '\r') {
si++
}
if si < len(src) {
// trailing garbage
err = base64.CorruptInputError(si)
}
}
break
}
if dlen == 0 {
return si, 0, err
}
// Convert 4x 6bit source bytes into 3 bytes
val := uint(dbuf[0])<<18 | uint(dbuf[1])<<12 | uint(dbuf[2])<<6 | uint(dbuf[3])
dbuf[2], dbuf[1], dbuf[0] = byte(val>>0), byte(val>>8), byte(val>>16)
switch dlen {
case 4:
dst[2] = dbuf[2]
dbuf[2] = 0
fallthrough
case 3:
dst[1] = dbuf[1]
dbuf[1] = 0
fallthrough
case 2:
dst[0] = dbuf[0]
}
return si, dlen - 1, err
}

View File

@ -0,0 +1,58 @@
//go:build ignore
package main
import (
"bytes"
"fmt"
"log"
"os"
)
func generateDecodeMap() [256]byte {
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
var decodeMap [256]byte
for i := 0; i < len(decodeMap); i++ {
decodeMap[i] = 0xff
}
for i := 0; i < len(alphabet); i++ {
decodeMap[alphabet[i]] = byte(i)
}
// for URL encoding
decodeMap['-'] = decodeMap['+']
decodeMap['_'] = decodeMap['/']
return decodeMap
}
func main() {
f, err := os.OpenFile("decode_map.go", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
log.Fatal(err)
}
m := generateDecodeMap()
var b bytes.Buffer
fmt.Fprintf(&b, "// Code generated by generate_decode_map.go. DO NOT EDIT.\n\n")
fmt.Fprintf(&b, "package base64dec\n\n")
fmt.Fprintf(&b, "var decodeMap = [256]byte{")
for i, v := range m {
if i%16 == 0 {
fmt.Fprintf(&b, "\n\t")
} else {
fmt.Fprint(&b, " ")
}
fmt.Fprintf(&b, "0x%02x,", v)
}
fmt.Fprintf(&b, "\n}\n")
_, err = f.Write(b.Bytes())
if err != nil {
log.Fatal(err)
}
err = f.Close()
if err != nil {
log.Fatal(err)
}
}

565
dop251/goja/array.go Normal file
View File

@ -0,0 +1,565 @@
package goja
import (
"fmt"
"math"
"math/bits"
"reflect"
"strconv"
"apigo.cc/gojs/dop251/goja/unistring"
)
type arrayIterObject struct {
baseObject
obj *Object
nextIdx int64
kind iterationKind
}
func (ai *arrayIterObject) next() Value {
if ai.obj == nil {
return ai.val.runtime.createIterResultObject(_undefined, true)
}
if ta, ok := ai.obj.self.(*typedArrayObject); ok {
ta.viewedArrayBuf.ensureNotDetached(true)
}
l := toLength(ai.obj.self.getStr("length", nil))
index := ai.nextIdx
if index >= l {
ai.obj = nil
return ai.val.runtime.createIterResultObject(_undefined, true)
}
ai.nextIdx++
idxVal := valueInt(index)
if ai.kind == iterationKindKey {
return ai.val.runtime.createIterResultObject(idxVal, false)
}
elementValue := nilSafe(ai.obj.self.getIdx(idxVal, nil))
var result Value
if ai.kind == iterationKindValue {
result = elementValue
} else {
result = ai.val.runtime.newArrayValues([]Value{idxVal, elementValue})
}
return ai.val.runtime.createIterResultObject(result, false)
}
func (r *Runtime) createArrayIterator(iterObj *Object, kind iterationKind) Value {
o := &Object{runtime: r}
ai := &arrayIterObject{
obj: iterObj,
kind: kind,
}
ai.class = classObject
ai.val = o
ai.extensible = true
o.self = ai
ai.prototype = r.getArrayIteratorPrototype()
ai.init()
return o
}
type arrayObject struct {
baseObject
values []Value
length uint32
objCount int
propValueCount int
lengthProp valueProperty
}
func (a *arrayObject) init() {
a.baseObject.init()
a.lengthProp.writable = true
a._put("length", &a.lengthProp)
}
func (a *arrayObject) _setLengthInt(l uint32, throw bool) bool {
ret := true
if l <= a.length {
if a.propValueCount > 0 {
// Slow path
for i := len(a.values) - 1; i >= int(l); i-- {
if prop, ok := a.values[i].(*valueProperty); ok {
if !prop.configurable {
l = uint32(i) + 1
ret = false
break
}
a.propValueCount--
}
}
}
}
if l <= uint32(len(a.values)) {
if l >= 16 && l < uint32(cap(a.values))>>2 {
ar := make([]Value, l)
copy(ar, a.values)
a.values = ar
} else {
ar := a.values[l:len(a.values)]
for i := range ar {
ar[i] = nil
}
a.values = a.values[:l]
}
}
a.length = l
if !ret {
a.val.runtime.typeErrorResult(throw, "Cannot redefine property: length")
}
return ret
}
func (a *arrayObject) setLengthInt(l uint32, throw bool) bool {
if l == a.length {
return true
}
if !a.lengthProp.writable {
a.val.runtime.typeErrorResult(throw, "length is not writable")
return false
}
return a._setLengthInt(l, throw)
}
func (a *arrayObject) setLength(v uint32, throw bool) bool {
if !a.lengthProp.writable {
a.val.runtime.typeErrorResult(throw, "length is not writable")
return false
}
return a._setLengthInt(v, throw)
}
func (a *arrayObject) getIdx(idx valueInt, receiver Value) Value {
prop := a.getOwnPropIdx(idx)
if prop == nil {
if a.prototype != nil {
if receiver == nil {
return a.prototype.self.getIdx(idx, a.val)
}
return a.prototype.self.getIdx(idx, receiver)
}
}
if prop, ok := prop.(*valueProperty); ok {
if receiver == nil {
return prop.get(a.val)
}
return prop.get(receiver)
}
return prop
}
func (a *arrayObject) getOwnPropStr(name unistring.String) Value {
if len(a.values) > 0 {
if i := strToArrayIdx(name); i != math.MaxUint32 {
if i < uint32(len(a.values)) {
return a.values[i]
}
}
}
if name == "length" {
return a.getLengthProp()
}
return a.baseObject.getOwnPropStr(name)
}
func (a *arrayObject) getOwnPropIdx(idx valueInt) Value {
if i := toIdx(idx); i != math.MaxUint32 {
if i < uint32(len(a.values)) {
return a.values[i]
}
return nil
}
return a.baseObject.getOwnPropStr(idx.string())
}
func (a *arrayObject) sortLen() int {
return len(a.values)
}
func (a *arrayObject) sortGet(i int) Value {
v := a.values[i]
if p, ok := v.(*valueProperty); ok {
v = p.get(a.val)
}
return v
}
func (a *arrayObject) swap(i int, j int) {
a.values[i], a.values[j] = a.values[j], a.values[i]
}
func (a *arrayObject) getStr(name unistring.String, receiver Value) Value {
return a.getStrWithOwnProp(a.getOwnPropStr(name), name, receiver)
}
func (a *arrayObject) getLengthProp() *valueProperty {
a.lengthProp.value = intToValue(int64(a.length))
return &a.lengthProp
}
func (a *arrayObject) setOwnIdx(idx valueInt, val Value, throw bool) bool {
if i := toIdx(idx); i != math.MaxUint32 {
return a._setOwnIdx(i, val, throw)
} else {
return a.baseObject.setOwnStr(idx.string(), val, throw)
}
}
func (a *arrayObject) _setOwnIdx(idx uint32, val Value, throw bool) bool {
var prop Value
if idx < uint32(len(a.values)) {
prop = a.values[idx]
}
if prop == nil {
if proto := a.prototype; proto != nil {
// we know it's foreign because prototype loops are not allowed
if res, ok := proto.self.setForeignIdx(valueInt(idx), val, a.val, throw); ok {
return res
}
}
// new property
if !a.extensible {
a.val.runtime.typeErrorResult(throw, "Cannot add property %d, object is not extensible", idx)
return false
} else {
if idx >= a.length {
if !a.setLengthInt(idx+1, throw) {
return false
}
}
if idx >= uint32(len(a.values)) {
if !a.expand(idx) {
a.val.self.(*sparseArrayObject).add(idx, val)
return true
}
}
a.objCount++
}
} else {
if prop, ok := prop.(*valueProperty); ok {
if !prop.isWritable() {
a.val.runtime.typeErrorResult(throw)
return false
}
prop.set(a.val, val)
return true
}
}
a.values[idx] = val
return true
}
func (a *arrayObject) setOwnStr(name unistring.String, val Value, throw bool) bool {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
return a._setOwnIdx(idx, val, throw)
} else {
if name == "length" {
return a.setLength(a.val.runtime.toLengthUint32(val), throw)
} else {
return a.baseObject.setOwnStr(name, val, throw)
}
}
}
func (a *arrayObject) setForeignIdx(idx valueInt, val, receiver Value, throw bool) (bool, bool) {
return a._setForeignIdx(idx, a.getOwnPropIdx(idx), val, receiver, throw)
}
func (a *arrayObject) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) {
return a._setForeignStr(name, a.getOwnPropStr(name), val, receiver, throw)
}
type arrayPropIter struct {
a *arrayObject
limit int
idx int
}
func (i *arrayPropIter) next() (propIterItem, iterNextFunc) {
for i.idx < len(i.a.values) && i.idx < i.limit {
name := asciiString(strconv.Itoa(i.idx))
prop := i.a.values[i.idx]
i.idx++
if prop != nil {
return propIterItem{name: name, value: prop}, i.next
}
}
return i.a.baseObject.iterateStringKeys()()
}
func (a *arrayObject) iterateStringKeys() iterNextFunc {
return (&arrayPropIter{
a: a,
limit: len(a.values),
}).next
}
func (a *arrayObject) stringKeys(all bool, accum []Value) []Value {
for i, prop := range a.values {
name := strconv.Itoa(i)
if prop != nil {
if !all {
if prop, ok := prop.(*valueProperty); ok && !prop.enumerable {
continue
}
}
accum = append(accum, asciiString(name))
}
}
return a.baseObject.stringKeys(all, accum)
}
func (a *arrayObject) hasOwnPropertyStr(name unistring.String) bool {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
return idx < uint32(len(a.values)) && a.values[idx] != nil
} else {
return a.baseObject.hasOwnPropertyStr(name)
}
}
func (a *arrayObject) hasOwnPropertyIdx(idx valueInt) bool {
if idx := toIdx(idx); idx != math.MaxUint32 {
return idx < uint32(len(a.values)) && a.values[idx] != nil
}
return a.baseObject.hasOwnPropertyStr(idx.string())
}
func (a *arrayObject) hasPropertyIdx(idx valueInt) bool {
if a.hasOwnPropertyIdx(idx) {
return true
}
if a.prototype != nil {
return a.prototype.self.hasPropertyIdx(idx)
}
return false
}
func (a *arrayObject) expand(idx uint32) bool {
targetLen := idx + 1
if targetLen > uint32(len(a.values)) {
if targetLen < uint32(cap(a.values)) {
a.values = a.values[:targetLen]
} else {
if idx > 4096 && (a.objCount == 0 || idx/uint32(a.objCount) > 10) {
//log.Println("Switching standard->sparse")
sa := &sparseArrayObject{
baseObject: a.baseObject,
length: a.length,
propValueCount: a.propValueCount,
}
sa.setValues(a.values, a.objCount+1)
sa.val.self = sa
sa.lengthProp.writable = a.lengthProp.writable
sa._put("length", &sa.lengthProp)
return false
} else {
if bits.UintSize == 32 {
if targetLen >= math.MaxInt32 {
panic(a.val.runtime.NewTypeError("Array index overflows int"))
}
}
tl := int(targetLen)
newValues := make([]Value, tl, growCap(tl, len(a.values), cap(a.values)))
copy(newValues, a.values)
a.values = newValues
}
}
}
return true
}
func (r *Runtime) defineArrayLength(prop *valueProperty, descr PropertyDescriptor, setter func(uint32, bool) bool, throw bool) bool {
var newLen uint32
ret := true
if descr.Value != nil {
newLen = r.toLengthUint32(descr.Value)
}
if descr.Configurable == FLAG_TRUE || descr.Enumerable == FLAG_TRUE || descr.Getter != nil || descr.Setter != nil {
ret = false
goto Reject
}
if descr.Value != nil {
oldLen := uint32(prop.value.ToInteger())
if oldLen != newLen {
ret = setter(newLen, false)
}
} else {
ret = true
}
if descr.Writable != FLAG_NOT_SET {
w := descr.Writable.Bool()
if prop.writable {
prop.writable = w
} else {
if w {
ret = false
goto Reject
}
}
}
Reject:
if !ret {
r.typeErrorResult(throw, "Cannot redefine property: length")
}
return ret
}
func (a *arrayObject) _defineIdxProperty(idx uint32, desc PropertyDescriptor, throw bool) bool {
var existing Value
if idx < uint32(len(a.values)) {
existing = a.values[idx]
}
prop, ok := a.baseObject._defineOwnProperty(unistring.String(strconv.FormatUint(uint64(idx), 10)), existing, desc, throw)
if ok {
if idx >= a.length {
if !a.setLengthInt(idx+1, throw) {
return false
}
}
if a.expand(idx) {
a.values[idx] = prop
a.objCount++
if _, ok := prop.(*valueProperty); ok {
a.propValueCount++
}
} else {
a.val.self.(*sparseArrayObject).add(idx, prop)
}
}
return ok
}
func (a *arrayObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
return a._defineIdxProperty(idx, descr, throw)
}
if name == "length" {
return a.val.runtime.defineArrayLength(a.getLengthProp(), descr, a.setLength, throw)
}
return a.baseObject.defineOwnPropertyStr(name, descr, throw)
}
func (a *arrayObject) defineOwnPropertyIdx(idx valueInt, descr PropertyDescriptor, throw bool) bool {
if idx := toIdx(idx); idx != math.MaxUint32 {
return a._defineIdxProperty(idx, descr, throw)
}
return a.baseObject.defineOwnPropertyStr(idx.string(), descr, throw)
}
func (a *arrayObject) _deleteIdxProp(idx uint32, throw bool) bool {
if idx < uint32(len(a.values)) {
if v := a.values[idx]; v != nil {
if p, ok := v.(*valueProperty); ok {
if !p.configurable {
a.val.runtime.typeErrorResult(throw, "Cannot delete property '%d' of %s", idx, a.val.toString())
return false
}
a.propValueCount--
}
a.values[idx] = nil
a.objCount--
}
}
return true
}
func (a *arrayObject) deleteStr(name unistring.String, throw bool) bool {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
return a._deleteIdxProp(idx, throw)
}
return a.baseObject.deleteStr(name, throw)
}
func (a *arrayObject) deleteIdx(idx valueInt, throw bool) bool {
if idx := toIdx(idx); idx != math.MaxUint32 {
return a._deleteIdxProp(idx, throw)
}
return a.baseObject.deleteStr(idx.string(), throw)
}
func (a *arrayObject) export(ctx *objectExportCtx) interface{} {
if v, exists := ctx.get(a.val); exists {
return v
}
arr := make([]interface{}, a.length)
ctx.put(a.val, arr)
if a.propValueCount == 0 && a.length == uint32(len(a.values)) && uint32(a.objCount) == a.length {
for i, v := range a.values {
if v != nil {
arr[i] = exportValue(v, ctx)
}
}
} else {
for i := uint32(0); i < a.length; i++ {
v := a.getIdx(valueInt(i), nil)
if v != nil {
arr[i] = exportValue(v, ctx)
}
}
}
return arr
}
func (a *arrayObject) exportType() reflect.Type {
return reflectTypeArray
}
func (a *arrayObject) exportToArrayOrSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error {
r := a.val.runtime
if iter := a.getSym(SymIterator, nil); iter == r.getArrayValues() || iter == nil {
l := toIntStrict(int64(a.length))
if typ.Kind() == reflect.Array {
if dst.Len() != l {
return fmt.Errorf("cannot convert an Array into an array, lengths mismatch (have %d, need %d)", l, dst.Len())
}
} else {
dst.Set(reflect.MakeSlice(typ, l, l))
}
ctx.putTyped(a.val, typ, dst.Interface())
for i := 0; i < l; i++ {
if i >= len(a.values) {
break
}
val := a.values[i]
if p, ok := val.(*valueProperty); ok {
val = p.get(a.val)
}
err := r.toReflectValue(val, dst.Index(i), ctx)
if err != nil {
return fmt.Errorf("could not convert array element %v to %v at %d: %w", val, typ, i, err)
}
}
return nil
}
return a.baseObject.exportToArrayOrSlice(dst, typ, ctx)
}
func (a *arrayObject) setValuesFromSparse(items []sparseArrayItem, newMaxIdx int) {
a.values = make([]Value, newMaxIdx+1)
for _, item := range items {
a.values[item.idx] = item.value
}
a.objCount = len(items)
}
func toIdx(v valueInt) uint32 {
if v >= 0 && v < math.MaxUint32 {
return uint32(v)
}
return math.MaxUint32
}

500
dop251/goja/array_sparse.go Normal file
View File

@ -0,0 +1,500 @@
package goja
import (
"fmt"
"math"
"math/bits"
"reflect"
"sort"
"strconv"
"apigo.cc/gojs/dop251/goja/unistring"
)
type sparseArrayItem struct {
idx uint32
value Value
}
type sparseArrayObject struct {
baseObject
items []sparseArrayItem
length uint32
propValueCount int
lengthProp valueProperty
}
func (a *sparseArrayObject) findIdx(idx uint32) int {
return sort.Search(len(a.items), func(i int) bool {
return a.items[i].idx >= idx
})
}
func (a *sparseArrayObject) _setLengthInt(l uint32, throw bool) bool {
ret := true
if l <= a.length {
if a.propValueCount > 0 {
// Slow path
for i := len(a.items) - 1; i >= 0; i-- {
item := a.items[i]
if item.idx <= l {
break
}
if prop, ok := item.value.(*valueProperty); ok {
if !prop.configurable {
l = item.idx + 1
ret = false
break
}
a.propValueCount--
}
}
}
}
idx := a.findIdx(l)
aa := a.items[idx:]
for i := range aa {
aa[i].value = nil
}
a.items = a.items[:idx]
a.length = l
if !ret {
a.val.runtime.typeErrorResult(throw, "Cannot redefine property: length")
}
return ret
}
func (a *sparseArrayObject) setLengthInt(l uint32, throw bool) bool {
if l == a.length {
return true
}
if !a.lengthProp.writable {
a.val.runtime.typeErrorResult(throw, "length is not writable")
return false
}
return a._setLengthInt(l, throw)
}
func (a *sparseArrayObject) setLength(v uint32, throw bool) bool {
if !a.lengthProp.writable {
a.val.runtime.typeErrorResult(throw, "length is not writable")
return false
}
return a._setLengthInt(v, throw)
}
func (a *sparseArrayObject) _getIdx(idx uint32) Value {
i := a.findIdx(idx)
if i < len(a.items) && a.items[i].idx == idx {
return a.items[i].value
}
return nil
}
func (a *sparseArrayObject) getStr(name unistring.String, receiver Value) Value {
return a.getStrWithOwnProp(a.getOwnPropStr(name), name, receiver)
}
func (a *sparseArrayObject) getIdx(idx valueInt, receiver Value) Value {
prop := a.getOwnPropIdx(idx)
if prop == nil {
if a.prototype != nil {
if receiver == nil {
return a.prototype.self.getIdx(idx, a.val)
}
return a.prototype.self.getIdx(idx, receiver)
}
}
if prop, ok := prop.(*valueProperty); ok {
if receiver == nil {
return prop.get(a.val)
}
return prop.get(receiver)
}
return prop
}
func (a *sparseArrayObject) getLengthProp() *valueProperty {
a.lengthProp.value = intToValue(int64(a.length))
return &a.lengthProp
}
func (a *sparseArrayObject) getOwnPropStr(name unistring.String) Value {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
return a._getIdx(idx)
}
if name == "length" {
return a.getLengthProp()
}
return a.baseObject.getOwnPropStr(name)
}
func (a *sparseArrayObject) getOwnPropIdx(idx valueInt) Value {
if idx := toIdx(idx); idx != math.MaxUint32 {
return a._getIdx(idx)
}
return a.baseObject.getOwnPropStr(idx.string())
}
func (a *sparseArrayObject) add(idx uint32, val Value) {
i := a.findIdx(idx)
a.items = append(a.items, sparseArrayItem{})
copy(a.items[i+1:], a.items[i:])
a.items[i] = sparseArrayItem{
idx: idx,
value: val,
}
}
func (a *sparseArrayObject) _setOwnIdx(idx uint32, val Value, throw bool) bool {
var prop Value
i := a.findIdx(idx)
if i < len(a.items) && a.items[i].idx == idx {
prop = a.items[i].value
}
if prop == nil {
if proto := a.prototype; proto != nil {
// we know it's foreign because prototype loops are not allowed
if res, ok := proto.self.setForeignIdx(valueInt(idx), val, a.val, throw); ok {
return res
}
}
// new property
if !a.extensible {
a.val.runtime.typeErrorResult(throw, "Cannot add property %d, object is not extensible", idx)
return false
}
if idx >= a.length {
if !a.setLengthInt(idx+1, throw) {
return false
}
}
if a.expand(idx) {
a.items = append(a.items, sparseArrayItem{})
copy(a.items[i+1:], a.items[i:])
a.items[i] = sparseArrayItem{
idx: idx,
value: val,
}
} else {
ar := a.val.self.(*arrayObject)
ar.values[idx] = val
ar.objCount++
return true
}
} else {
if prop, ok := prop.(*valueProperty); ok {
if !prop.isWritable() {
a.val.runtime.typeErrorResult(throw)
return false
}
prop.set(a.val, val)
} else {
a.items[i].value = val
}
}
return true
}
func (a *sparseArrayObject) setOwnStr(name unistring.String, val Value, throw bool) bool {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
return a._setOwnIdx(idx, val, throw)
} else {
if name == "length" {
return a.setLength(a.val.runtime.toLengthUint32(val), throw)
} else {
return a.baseObject.setOwnStr(name, val, throw)
}
}
}
func (a *sparseArrayObject) setOwnIdx(idx valueInt, val Value, throw bool) bool {
if idx := toIdx(idx); idx != math.MaxUint32 {
return a._setOwnIdx(idx, val, throw)
}
return a.baseObject.setOwnStr(idx.string(), val, throw)
}
func (a *sparseArrayObject) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) {
return a._setForeignStr(name, a.getOwnPropStr(name), val, receiver, throw)
}
func (a *sparseArrayObject) setForeignIdx(name valueInt, val, receiver Value, throw bool) (bool, bool) {
return a._setForeignIdx(name, a.getOwnPropIdx(name), val, receiver, throw)
}
type sparseArrayPropIter struct {
a *sparseArrayObject
idx int
}
func (i *sparseArrayPropIter) next() (propIterItem, iterNextFunc) {
for i.idx < len(i.a.items) {
name := asciiString(strconv.Itoa(int(i.a.items[i.idx].idx)))
prop := i.a.items[i.idx].value
i.idx++
if prop != nil {
return propIterItem{name: name, value: prop}, i.next
}
}
return i.a.baseObject.iterateStringKeys()()
}
func (a *sparseArrayObject) iterateStringKeys() iterNextFunc {
return (&sparseArrayPropIter{
a: a,
}).next
}
func (a *sparseArrayObject) stringKeys(all bool, accum []Value) []Value {
if all {
for _, item := range a.items {
accum = append(accum, asciiString(strconv.FormatUint(uint64(item.idx), 10)))
}
} else {
for _, item := range a.items {
if prop, ok := item.value.(*valueProperty); ok && !prop.enumerable {
continue
}
accum = append(accum, asciiString(strconv.FormatUint(uint64(item.idx), 10)))
}
}
return a.baseObject.stringKeys(all, accum)
}
func (a *sparseArrayObject) setValues(values []Value, objCount int) {
a.items = make([]sparseArrayItem, 0, objCount)
for i, val := range values {
if val != nil {
a.items = append(a.items, sparseArrayItem{
idx: uint32(i),
value: val,
})
}
}
}
func (a *sparseArrayObject) hasOwnPropertyStr(name unistring.String) bool {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
i := a.findIdx(idx)
return i < len(a.items) && a.items[i].idx == idx
} else {
return a.baseObject.hasOwnPropertyStr(name)
}
}
func (a *sparseArrayObject) hasOwnPropertyIdx(idx valueInt) bool {
if idx := toIdx(idx); idx != math.MaxUint32 {
i := a.findIdx(idx)
return i < len(a.items) && a.items[i].idx == idx
}
return a.baseObject.hasOwnPropertyStr(idx.string())
}
func (a *sparseArrayObject) hasPropertyIdx(idx valueInt) bool {
if a.hasOwnPropertyIdx(idx) {
return true
}
if a.prototype != nil {
return a.prototype.self.hasPropertyIdx(idx)
}
return false
}
func (a *sparseArrayObject) expand(idx uint32) bool {
if l := len(a.items); l >= 1024 {
if ii := a.items[l-1].idx; ii > idx {
idx = ii
}
if (bits.UintSize == 64 || idx < math.MaxInt32) && int(idx)>>3 < l {
//log.Println("Switching sparse->standard")
ar := &arrayObject{
baseObject: a.baseObject,
length: a.length,
propValueCount: a.propValueCount,
}
ar.setValuesFromSparse(a.items, int(idx))
ar.val.self = ar
ar.lengthProp.writable = a.lengthProp.writable
a._put("length", &ar.lengthProp)
return false
}
}
return true
}
func (a *sparseArrayObject) _defineIdxProperty(idx uint32, desc PropertyDescriptor, throw bool) bool {
var existing Value
i := a.findIdx(idx)
if i < len(a.items) && a.items[i].idx == idx {
existing = a.items[i].value
}
prop, ok := a.baseObject._defineOwnProperty(unistring.String(strconv.FormatUint(uint64(idx), 10)), existing, desc, throw)
if ok {
if idx >= a.length {
if !a.setLengthInt(idx+1, throw) {
return false
}
}
if i >= len(a.items) || a.items[i].idx != idx {
if a.expand(idx) {
a.items = append(a.items, sparseArrayItem{})
copy(a.items[i+1:], a.items[i:])
a.items[i] = sparseArrayItem{
idx: idx,
value: prop,
}
if idx >= a.length {
a.length = idx + 1
}
} else {
a.val.self.(*arrayObject).values[idx] = prop
}
} else {
a.items[i].value = prop
}
if _, ok := prop.(*valueProperty); ok {
a.propValueCount++
}
}
return ok
}
func (a *sparseArrayObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
return a._defineIdxProperty(idx, descr, throw)
}
if name == "length" {
return a.val.runtime.defineArrayLength(a.getLengthProp(), descr, a.setLength, throw)
}
return a.baseObject.defineOwnPropertyStr(name, descr, throw)
}
func (a *sparseArrayObject) defineOwnPropertyIdx(idx valueInt, descr PropertyDescriptor, throw bool) bool {
if idx := toIdx(idx); idx != math.MaxUint32 {
return a._defineIdxProperty(idx, descr, throw)
}
return a.baseObject.defineOwnPropertyStr(idx.string(), descr, throw)
}
func (a *sparseArrayObject) _deleteIdxProp(idx uint32, throw bool) bool {
i := a.findIdx(idx)
if i < len(a.items) && a.items[i].idx == idx {
if p, ok := a.items[i].value.(*valueProperty); ok {
if !p.configurable {
a.val.runtime.typeErrorResult(throw, "Cannot delete property '%d' of %s", idx, a.val.toString())
return false
}
a.propValueCount--
}
copy(a.items[i:], a.items[i+1:])
a.items[len(a.items)-1].value = nil
a.items = a.items[:len(a.items)-1]
}
return true
}
func (a *sparseArrayObject) deleteStr(name unistring.String, throw bool) bool {
if idx := strToArrayIdx(name); idx != math.MaxUint32 {
return a._deleteIdxProp(idx, throw)
}
return a.baseObject.deleteStr(name, throw)
}
func (a *sparseArrayObject) deleteIdx(idx valueInt, throw bool) bool {
if idx := toIdx(idx); idx != math.MaxUint32 {
return a._deleteIdxProp(idx, throw)
}
return a.baseObject.deleteStr(idx.string(), throw)
}
func (a *sparseArrayObject) sortLen() int {
if len(a.items) > 0 {
return toIntStrict(int64(a.items[len(a.items)-1].idx) + 1)
}
return 0
}
func (a *sparseArrayObject) export(ctx *objectExportCtx) interface{} {
if v, exists := ctx.get(a.val); exists {
return v
}
arr := make([]interface{}, a.length)
ctx.put(a.val, arr)
var prevIdx uint32
for _, item := range a.items {
idx := item.idx
for i := prevIdx; i < idx; i++ {
if a.prototype != nil {
if v := a.prototype.self.getIdx(valueInt(i), nil); v != nil {
arr[i] = exportValue(v, ctx)
}
}
}
v := item.value
if v != nil {
if prop, ok := v.(*valueProperty); ok {
v = prop.get(a.val)
}
arr[idx] = exportValue(v, ctx)
}
prevIdx = idx + 1
}
for i := prevIdx; i < a.length; i++ {
if a.prototype != nil {
if v := a.prototype.self.getIdx(valueInt(i), nil); v != nil {
arr[i] = exportValue(v, ctx)
}
}
}
return arr
}
func (a *sparseArrayObject) exportType() reflect.Type {
return reflectTypeArray
}
func (a *sparseArrayObject) exportToArrayOrSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error {
r := a.val.runtime
if iter := a.getSym(SymIterator, nil); iter == r.getArrayValues() || iter == nil {
l := toIntStrict(int64(a.length))
if typ.Kind() == reflect.Array {
if dst.Len() != l {
return fmt.Errorf("cannot convert an Array into an array, lengths mismatch (have %d, need %d)", l, dst.Len())
}
} else {
dst.Set(reflect.MakeSlice(typ, l, l))
}
ctx.putTyped(a.val, typ, dst.Interface())
for _, item := range a.items {
val := item.value
if p, ok := val.(*valueProperty); ok {
val = p.get(a.val)
}
idx := toIntStrict(int64(item.idx))
if idx >= l {
break
}
err := r.toReflectValue(val, dst.Index(idx), ctx)
if err != nil {
return fmt.Errorf("could not convert array element %v to %v at %d: %w", item.value, typ, idx, err)
}
}
return nil
}
return a.baseObject.exportToArrayOrSlice(dst, typ, ctx)
}

876
dop251/goja/ast/node.go Normal file
View File

@ -0,0 +1,876 @@
/*
Package ast declares types representing a JavaScript AST.
# Warning
The parser and AST interfaces are still works-in-progress (particularly where
node types are concerned) and may change in the future.
*/
package ast
import (
"apigo.cc/gojs/dop251/goja/file"
"apigo.cc/gojs/dop251/goja/token"
"apigo.cc/gojs/dop251/goja/unistring"
)
type PropertyKind string
const (
PropertyKindValue PropertyKind = "value"
PropertyKindGet PropertyKind = "get"
PropertyKindSet PropertyKind = "set"
PropertyKindMethod PropertyKind = "method"
)
// All nodes implement the Node interface.
type Node interface {
Idx0() file.Idx // The index of the first character belonging to the node
Idx1() file.Idx // The index of the first character immediately after the node
}
// ========== //
// Expression //
// ========== //
type (
// All expression nodes implement the Expression interface.
Expression interface {
Node
_expressionNode()
}
BindingTarget interface {
Expression
_bindingTarget()
}
Binding struct {
Target BindingTarget
Initializer Expression
}
Pattern interface {
BindingTarget
_pattern()
}
YieldExpression struct {
Yield file.Idx
Argument Expression
Delegate bool
}
AwaitExpression struct {
Await file.Idx
Argument Expression
}
ArrayLiteral struct {
LeftBracket file.Idx
RightBracket file.Idx
Value []Expression
}
ArrayPattern struct {
LeftBracket file.Idx
RightBracket file.Idx
Elements []Expression
Rest Expression
}
AssignExpression struct {
Operator token.Token
Left Expression
Right Expression
}
BadExpression struct {
From file.Idx
To file.Idx
}
BinaryExpression struct {
Operator token.Token
Left Expression
Right Expression
Comparison bool
}
BooleanLiteral struct {
Idx file.Idx
Literal string
Value bool
}
BracketExpression struct {
Left Expression
Member Expression
LeftBracket file.Idx
RightBracket file.Idx
}
CallExpression struct {
Callee Expression
LeftParenthesis file.Idx
ArgumentList []Expression
RightParenthesis file.Idx
}
ConditionalExpression struct {
Test Expression
Consequent Expression
Alternate Expression
}
DotExpression struct {
Left Expression
Identifier Identifier
}
PrivateDotExpression struct {
Left Expression
Identifier PrivateIdentifier
}
OptionalChain struct {
Expression
}
Optional struct {
Expression
}
FunctionLiteral struct {
Function file.Idx
Name *Identifier
ParameterList *ParameterList
Body *BlockStatement
Source string
DeclarationList []*VariableDeclaration
Async, Generator bool
}
ClassLiteral struct {
Class file.Idx
RightBrace file.Idx
Name *Identifier
SuperClass Expression
Body []ClassElement
Source string
}
ConciseBody interface {
Node
_conciseBody()
}
ExpressionBody struct {
Expression Expression
}
ArrowFunctionLiteral struct {
Start file.Idx
ParameterList *ParameterList
Body ConciseBody
Source string
DeclarationList []*VariableDeclaration
Async bool
}
Identifier struct {
Name unistring.String
Idx file.Idx
}
PrivateIdentifier struct {
Identifier
}
NewExpression struct {
New file.Idx
Callee Expression
LeftParenthesis file.Idx
ArgumentList []Expression
RightParenthesis file.Idx
}
NullLiteral struct {
Idx file.Idx
Literal string
}
NumberLiteral struct {
Idx file.Idx
Literal string
Value interface{}
}
ObjectLiteral struct {
LeftBrace file.Idx
RightBrace file.Idx
Value []Property
}
ObjectPattern struct {
LeftBrace file.Idx
RightBrace file.Idx
Properties []Property
Rest Expression
}
ParameterList struct {
Opening file.Idx
List []*Binding
Rest Expression
Closing file.Idx
}
Property interface {
Expression
_property()
}
PropertyShort struct {
Name Identifier
Initializer Expression
}
PropertyKeyed struct {
Key Expression
Kind PropertyKind
Value Expression
Computed bool
}
SpreadElement struct {
Expression
}
RegExpLiteral struct {
Idx file.Idx
Literal string
Pattern string
Flags string
}
SequenceExpression struct {
Sequence []Expression
}
StringLiteral struct {
Idx file.Idx
Literal string
Value unistring.String
}
TemplateElement struct {
Idx file.Idx
Literal string
Parsed unistring.String
Valid bool
}
TemplateLiteral struct {
OpenQuote file.Idx
CloseQuote file.Idx
Tag Expression
Elements []*TemplateElement
Expressions []Expression
}
ThisExpression struct {
Idx file.Idx
}
SuperExpression struct {
Idx file.Idx
}
UnaryExpression struct {
Operator token.Token
Idx file.Idx // If a prefix operation
Operand Expression
Postfix bool
}
MetaProperty struct {
Meta, Property *Identifier
Idx file.Idx
}
)
// _expressionNode
func (*ArrayLiteral) _expressionNode() {}
func (*AssignExpression) _expressionNode() {}
func (*YieldExpression) _expressionNode() {}
func (*AwaitExpression) _expressionNode() {}
func (*BadExpression) _expressionNode() {}
func (*BinaryExpression) _expressionNode() {}
func (*BooleanLiteral) _expressionNode() {}
func (*BracketExpression) _expressionNode() {}
func (*CallExpression) _expressionNode() {}
func (*ConditionalExpression) _expressionNode() {}
func (*DotExpression) _expressionNode() {}
func (*PrivateDotExpression) _expressionNode() {}
func (*FunctionLiteral) _expressionNode() {}
func (*ClassLiteral) _expressionNode() {}
func (*ArrowFunctionLiteral) _expressionNode() {}
func (*Identifier) _expressionNode() {}
func (*NewExpression) _expressionNode() {}
func (*NullLiteral) _expressionNode() {}
func (*NumberLiteral) _expressionNode() {}
func (*ObjectLiteral) _expressionNode() {}
func (*RegExpLiteral) _expressionNode() {}
func (*SequenceExpression) _expressionNode() {}
func (*StringLiteral) _expressionNode() {}
func (*TemplateLiteral) _expressionNode() {}
func (*ThisExpression) _expressionNode() {}
func (*SuperExpression) _expressionNode() {}
func (*UnaryExpression) _expressionNode() {}
func (*MetaProperty) _expressionNode() {}
func (*ObjectPattern) _expressionNode() {}
func (*ArrayPattern) _expressionNode() {}
func (*Binding) _expressionNode() {}
func (*PropertyShort) _expressionNode() {}
func (*PropertyKeyed) _expressionNode() {}
// ========= //
// Statement //
// ========= //
type (
// All statement nodes implement the Statement interface.
Statement interface {
Node
_statementNode()
}
BadStatement struct {
From file.Idx
To file.Idx
}
BlockStatement struct {
LeftBrace file.Idx
List []Statement
RightBrace file.Idx
}
BranchStatement struct {
Idx file.Idx
Token token.Token
Label *Identifier
}
CaseStatement struct {
Case file.Idx
Test Expression
Consequent []Statement
}
CatchStatement struct {
Catch file.Idx
Parameter BindingTarget
Body *BlockStatement
}
DebuggerStatement struct {
Debugger file.Idx
}
DoWhileStatement struct {
Do file.Idx
Test Expression
Body Statement
RightParenthesis file.Idx
}
EmptyStatement struct {
Semicolon file.Idx
}
ExpressionStatement struct {
Expression Expression
}
ForInStatement struct {
For file.Idx
Into ForInto
Source Expression
Body Statement
}
ForOfStatement struct {
For file.Idx
Into ForInto
Source Expression
Body Statement
}
ForStatement struct {
For file.Idx
Initializer ForLoopInitializer
Update Expression
Test Expression
Body Statement
}
IfStatement struct {
If file.Idx
Test Expression
Consequent Statement
Alternate Statement
}
LabelledStatement struct {
Label *Identifier
Colon file.Idx
Statement Statement
}
ReturnStatement struct {
Return file.Idx
Argument Expression
}
SwitchStatement struct {
Switch file.Idx
Discriminant Expression
Default int
Body []*CaseStatement
RightBrace file.Idx
}
ThrowStatement struct {
Throw file.Idx
Argument Expression
}
TryStatement struct {
Try file.Idx
Body *BlockStatement
Catch *CatchStatement
Finally *BlockStatement
}
VariableStatement struct {
Var file.Idx
List []*Binding
}
LexicalDeclaration struct {
Idx file.Idx
Token token.Token
List []*Binding
}
WhileStatement struct {
While file.Idx
Test Expression
Body Statement
}
WithStatement struct {
With file.Idx
Object Expression
Body Statement
}
FunctionDeclaration struct {
Function *FunctionLiteral
}
ClassDeclaration struct {
Class *ClassLiteral
}
)
// _statementNode
func (*BadStatement) _statementNode() {}
func (*BlockStatement) _statementNode() {}
func (*BranchStatement) _statementNode() {}
func (*CaseStatement) _statementNode() {}
func (*CatchStatement) _statementNode() {}
func (*DebuggerStatement) _statementNode() {}
func (*DoWhileStatement) _statementNode() {}
func (*EmptyStatement) _statementNode() {}
func (*ExpressionStatement) _statementNode() {}
func (*ForInStatement) _statementNode() {}
func (*ForOfStatement) _statementNode() {}
func (*ForStatement) _statementNode() {}
func (*IfStatement) _statementNode() {}
func (*LabelledStatement) _statementNode() {}
func (*ReturnStatement) _statementNode() {}
func (*SwitchStatement) _statementNode() {}
func (*ThrowStatement) _statementNode() {}
func (*TryStatement) _statementNode() {}
func (*VariableStatement) _statementNode() {}
func (*WhileStatement) _statementNode() {}
func (*WithStatement) _statementNode() {}
func (*LexicalDeclaration) _statementNode() {}
func (*FunctionDeclaration) _statementNode() {}
func (*ClassDeclaration) _statementNode() {}
// =========== //
// Declaration //
// =========== //
type (
VariableDeclaration struct {
Var file.Idx
List []*Binding
}
ClassElement interface {
Node
_classElement()
}
FieldDefinition struct {
Idx file.Idx
Key Expression
Initializer Expression
Computed bool
Static bool
}
MethodDefinition struct {
Idx file.Idx
Key Expression
Kind PropertyKind // "method", "get" or "set"
Body *FunctionLiteral
Computed bool
Static bool
}
ClassStaticBlock struct {
Static file.Idx
Block *BlockStatement
Source string
DeclarationList []*VariableDeclaration
}
)
type (
ForLoopInitializer interface {
Node
_forLoopInitializer()
}
ForLoopInitializerExpression struct {
Expression Expression
}
ForLoopInitializerVarDeclList struct {
Var file.Idx
List []*Binding
}
ForLoopInitializerLexicalDecl struct {
LexicalDeclaration LexicalDeclaration
}
ForInto interface {
Node
_forInto()
}
ForIntoVar struct {
Binding *Binding
}
ForDeclaration struct {
Idx file.Idx
IsConst bool
Target BindingTarget
}
ForIntoExpression struct {
Expression Expression
}
)
func (*ForLoopInitializerExpression) _forLoopInitializer() {}
func (*ForLoopInitializerVarDeclList) _forLoopInitializer() {}
func (*ForLoopInitializerLexicalDecl) _forLoopInitializer() {}
func (*ForIntoVar) _forInto() {}
func (*ForDeclaration) _forInto() {}
func (*ForIntoExpression) _forInto() {}
func (*ArrayPattern) _pattern() {}
func (*ArrayPattern) _bindingTarget() {}
func (*ObjectPattern) _pattern() {}
func (*ObjectPattern) _bindingTarget() {}
func (*BadExpression) _bindingTarget() {}
func (*PropertyShort) _property() {}
func (*PropertyKeyed) _property() {}
func (*SpreadElement) _property() {}
func (*Identifier) _bindingTarget() {}
func (*BlockStatement) _conciseBody() {}
func (*ExpressionBody) _conciseBody() {}
func (*FieldDefinition) _classElement() {}
func (*MethodDefinition) _classElement() {}
func (*ClassStaticBlock) _classElement() {}
// ==== //
// Node //
// ==== //
type Program struct {
Body []Statement
DeclarationList []*VariableDeclaration
File *file.File
}
// ==== //
// Idx0 //
// ==== //
func (self *ArrayLiteral) Idx0() file.Idx { return self.LeftBracket }
func (self *ArrayPattern) Idx0() file.Idx { return self.LeftBracket }
func (self *YieldExpression) Idx0() file.Idx { return self.Yield }
func (self *AwaitExpression) Idx0() file.Idx { return self.Await }
func (self *ObjectPattern) Idx0() file.Idx { return self.LeftBrace }
func (self *ParameterList) Idx0() file.Idx { return self.Opening }
func (self *AssignExpression) Idx0() file.Idx { return self.Left.Idx0() }
func (self *BadExpression) Idx0() file.Idx { return self.From }
func (self *BinaryExpression) Idx0() file.Idx { return self.Left.Idx0() }
func (self *BooleanLiteral) Idx0() file.Idx { return self.Idx }
func (self *BracketExpression) Idx0() file.Idx { return self.Left.Idx0() }
func (self *CallExpression) Idx0() file.Idx { return self.Callee.Idx0() }
func (self *ConditionalExpression) Idx0() file.Idx { return self.Test.Idx0() }
func (self *DotExpression) Idx0() file.Idx { return self.Left.Idx0() }
func (self *PrivateDotExpression) Idx0() file.Idx { return self.Left.Idx0() }
func (self *FunctionLiteral) Idx0() file.Idx { return self.Function }
func (self *ClassLiteral) Idx0() file.Idx { return self.Class }
func (self *ArrowFunctionLiteral) Idx0() file.Idx { return self.Start }
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 *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() }
func (self *StringLiteral) Idx0() file.Idx { return self.Idx }
func (self *TemplateElement) Idx0() file.Idx { return self.Idx }
func (self *TemplateLiteral) Idx0() file.Idx { return self.OpenQuote }
func (self *ThisExpression) Idx0() file.Idx { return self.Idx }
func (self *SuperExpression) Idx0() file.Idx { return self.Idx }
func (self *UnaryExpression) Idx0() file.Idx {
if self.Postfix {
return self.Operand.Idx0()
}
return self.Idx
}
func (self *MetaProperty) Idx0() file.Idx { return self.Idx }
func (self *BadStatement) Idx0() file.Idx { return self.From }
func (self *BlockStatement) Idx0() file.Idx { return self.LeftBrace }
func (self *BranchStatement) Idx0() file.Idx { return self.Idx }
func (self *CaseStatement) Idx0() file.Idx { return self.Case }
func (self *CatchStatement) Idx0() file.Idx { return self.Catch }
func (self *DebuggerStatement) Idx0() file.Idx { return self.Debugger }
func (self *DoWhileStatement) Idx0() file.Idx { return self.Do }
func (self *EmptyStatement) Idx0() file.Idx { return self.Semicolon }
func (self *ExpressionStatement) Idx0() file.Idx { return self.Expression.Idx0() }
func (self *ForInStatement) Idx0() file.Idx { return self.For }
func (self *ForOfStatement) Idx0() file.Idx { return self.For }
func (self *ForStatement) Idx0() file.Idx { return self.For }
func (self *IfStatement) Idx0() file.Idx { return self.If }
func (self *LabelledStatement) Idx0() file.Idx { return self.Label.Idx0() }
func (self *Program) Idx0() file.Idx { return self.Body[0].Idx0() }
func (self *ReturnStatement) Idx0() file.Idx { return self.Return }
func (self *SwitchStatement) Idx0() file.Idx { return self.Switch }
func (self *ThrowStatement) Idx0() file.Idx { return self.Throw }
func (self *TryStatement) Idx0() file.Idx { return self.Try }
func (self *VariableStatement) Idx0() file.Idx { return self.Var }
func (self *WhileStatement) Idx0() file.Idx { return self.While }
func (self *WithStatement) Idx0() file.Idx { return self.With }
func (self *LexicalDeclaration) Idx0() file.Idx { return self.Idx }
func (self *FunctionDeclaration) Idx0() file.Idx { return self.Function.Idx0() }
func (self *ClassDeclaration) Idx0() file.Idx { return self.Class.Idx0() }
func (self *Binding) Idx0() file.Idx { return self.Target.Idx0() }
func (self *ForLoopInitializerExpression) Idx0() file.Idx { return self.Expression.Idx0() }
func (self *ForLoopInitializerVarDeclList) Idx0() file.Idx { return self.List[0].Idx0() }
func (self *ForLoopInitializerLexicalDecl) Idx0() file.Idx { return self.LexicalDeclaration.Idx0() }
func (self *PropertyShort) Idx0() file.Idx { return self.Name.Idx }
func (self *PropertyKeyed) Idx0() file.Idx { return self.Key.Idx0() }
func (self *ExpressionBody) Idx0() file.Idx { return self.Expression.Idx0() }
func (self *VariableDeclaration) Idx0() file.Idx { return self.Var }
func (self *FieldDefinition) Idx0() file.Idx { return self.Idx }
func (self *MethodDefinition) Idx0() file.Idx { return self.Idx }
func (self *ClassStaticBlock) Idx0() file.Idx { return self.Static }
func (self *ForDeclaration) Idx0() file.Idx { return self.Idx }
func (self *ForIntoVar) Idx0() file.Idx { return self.Binding.Idx0() }
func (self *ForIntoExpression) Idx0() file.Idx { return self.Expression.Idx0() }
// ==== //
// Idx1 //
// ==== //
func (self *ArrayLiteral) Idx1() file.Idx { return self.RightBracket + 1 }
func (self *ArrayPattern) Idx1() file.Idx { return self.RightBracket + 1 }
func (self *AssignExpression) Idx1() file.Idx { return self.Right.Idx1() }
func (self *AwaitExpression) Idx1() file.Idx { return self.Argument.Idx1() }
func (self *BadExpression) Idx1() file.Idx { return self.To }
func (self *BinaryExpression) Idx1() file.Idx { return self.Right.Idx1() }
func (self *BooleanLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) }
func (self *BracketExpression) Idx1() file.Idx { return self.RightBracket + 1 }
func (self *CallExpression) Idx1() file.Idx { return self.RightParenthesis + 1 }
func (self *ConditionalExpression) Idx1() file.Idx { return self.Alternate.Idx1() }
func (self *DotExpression) Idx1() file.Idx { return self.Identifier.Idx1() }
func (self *PrivateDotExpression) Idx1() file.Idx { return self.Identifier.Idx1() }
func (self *FunctionLiteral) Idx1() file.Idx { return self.Body.Idx1() }
func (self *ClassLiteral) Idx1() file.Idx { return self.RightBrace + 1 }
func (self *ArrowFunctionLiteral) Idx1() file.Idx { return self.Body.Idx1() }
func (self *Identifier) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Name)) }
func (self *NewExpression) Idx1() file.Idx {
if self.ArgumentList != nil {
return self.RightParenthesis + 1
} else {
return self.Callee.Idx1()
}
}
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 *ObjectLiteral) Idx1() file.Idx { return self.RightBrace + 1 }
func (self *ObjectPattern) Idx1() file.Idx { return self.RightBrace + 1 }
func (self *ParameterList) Idx1() file.Idx { return self.Closing + 1 }
func (self *RegExpLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) }
func (self *SequenceExpression) Idx1() file.Idx { return self.Sequence[len(self.Sequence)-1].Idx1() }
func (self *StringLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) }
func (self *TemplateElement) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) }
func (self *TemplateLiteral) Idx1() file.Idx { return self.CloseQuote + 1 }
func (self *ThisExpression) Idx1() file.Idx { return self.Idx + 4 }
func (self *SuperExpression) Idx1() file.Idx { return self.Idx + 5 }
func (self *UnaryExpression) Idx1() file.Idx {
if self.Postfix {
return self.Operand.Idx1() + 2 // ++ --
}
return self.Operand.Idx1()
}
func (self *MetaProperty) Idx1() file.Idx {
return self.Property.Idx1()
}
func (self *BadStatement) Idx1() file.Idx { return self.To }
func (self *BlockStatement) Idx1() file.Idx { return self.RightBrace + 1 }
func (self *BranchStatement) Idx1() file.Idx {
if self.Label == nil {
return file.Idx(int(self.Idx) + len(self.Token.String()))
}
return self.Label.Idx1()
}
func (self *CaseStatement) Idx1() file.Idx { return self.Consequent[len(self.Consequent)-1].Idx1() }
func (self *CatchStatement) Idx1() file.Idx { return self.Body.Idx1() }
func (self *DebuggerStatement) Idx1() file.Idx { return self.Debugger + 8 }
func (self *DoWhileStatement) Idx1() file.Idx { return self.RightParenthesis + 1 }
func (self *EmptyStatement) Idx1() file.Idx { return self.Semicolon + 1 }
func (self *ExpressionStatement) Idx1() file.Idx { return self.Expression.Idx1() }
func (self *ForInStatement) Idx1() file.Idx { return self.Body.Idx1() }
func (self *ForOfStatement) Idx1() file.Idx { return self.Body.Idx1() }
func (self *ForStatement) Idx1() file.Idx { return self.Body.Idx1() }
func (self *IfStatement) Idx1() file.Idx {
if self.Alternate != nil {
return self.Alternate.Idx1()
}
return self.Consequent.Idx1()
}
func (self *LabelledStatement) Idx1() file.Idx { return self.Statement.Idx1() }
func (self *Program) Idx1() file.Idx { return self.Body[len(self.Body)-1].Idx1() }
func (self *ReturnStatement) Idx1() file.Idx {
if self.Argument != nil {
return self.Argument.Idx1()
}
return self.Return + 6
}
func (self *SwitchStatement) Idx1() file.Idx { return self.RightBrace + 1 }
func (self *ThrowStatement) Idx1() file.Idx { return self.Argument.Idx1() }
func (self *TryStatement) Idx1() file.Idx {
if self.Finally != nil {
return self.Finally.Idx1()
}
if self.Catch != nil {
return self.Catch.Idx1()
}
return self.Body.Idx1()
}
func (self *VariableStatement) Idx1() file.Idx { return self.List[len(self.List)-1].Idx1() }
func (self *WhileStatement) Idx1() file.Idx { return self.Body.Idx1() }
func (self *WithStatement) Idx1() file.Idx { return self.Body.Idx1() }
func (self *LexicalDeclaration) Idx1() file.Idx { return self.List[len(self.List)-1].Idx1() }
func (self *FunctionDeclaration) Idx1() file.Idx { return self.Function.Idx1() }
func (self *ClassDeclaration) Idx1() file.Idx { return self.Class.Idx1() }
func (self *Binding) Idx1() file.Idx {
if self.Initializer != nil {
return self.Initializer.Idx1()
}
return self.Target.Idx1()
}
func (self *ForLoopInitializerExpression) Idx1() file.Idx { return self.Expression.Idx1() }
func (self *ForLoopInitializerVarDeclList) Idx1() file.Idx { return self.List[len(self.List)-1].Idx1() }
func (self *ForLoopInitializerLexicalDecl) Idx1() file.Idx { return self.LexicalDeclaration.Idx1() }
func (self *PropertyShort) Idx1() file.Idx {
if self.Initializer != nil {
return self.Initializer.Idx1()
}
return self.Name.Idx1()
}
func (self *PropertyKeyed) Idx1() file.Idx { return self.Value.Idx1() }
func (self *ExpressionBody) Idx1() file.Idx { return self.Expression.Idx1() }
func (self *VariableDeclaration) Idx1() file.Idx {
if len(self.List) > 0 {
return self.List[len(self.List)-1].Idx1()
}
return self.Var + 3
}
func (self *FieldDefinition) Idx1() file.Idx {
if self.Initializer != nil {
return self.Initializer.Idx1()
}
return self.Key.Idx1()
}
func (self *MethodDefinition) Idx1() file.Idx {
return self.Body.Idx1()
}
func (self *ClassStaticBlock) Idx1() file.Idx {
return self.Block.Idx1()
}
func (self *YieldExpression) Idx1() file.Idx {
if self.Argument != nil {
return self.Argument.Idx1()
}
return self.Yield + 5
}
func (self *ForDeclaration) Idx1() file.Idx { return self.Target.Idx1() }
func (self *ForIntoVar) Idx1() file.Idx { return self.Binding.Idx1() }
func (self *ForIntoExpression) Idx1() file.Idx { return self.Expression.Idx1() }

1794
dop251/goja/builtin_array.go Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,369 @@
package goja
import (
"fmt"
"hash/maphash"
"math"
"math/big"
"reflect"
"strconv"
"sync"
"apigo.cc/gojs/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
}

View File

@ -0,0 +1,75 @@
package goja
func (r *Runtime) booleanproto_toString(call FunctionCall) Value {
var b bool
switch o := call.This.(type) {
case valueBool:
b = bool(o)
goto success
case *Object:
if p, ok := o.self.(*primitiveValueObject); ok {
if b1, ok := p.pValue.(valueBool); ok {
b = bool(b1)
goto success
}
}
if o, ok := o.self.(*objectGoReflect); ok {
if o.class == classBoolean && o.toString != nil {
return o.toString()
}
}
}
r.typeErrorResult(true, "Method Boolean.prototype.toString is called on incompatible receiver")
success:
if b {
return stringTrue
}
return stringFalse
}
func (r *Runtime) booleanproto_valueOf(call FunctionCall) Value {
switch o := call.This.(type) {
case valueBool:
return o
case *Object:
if p, ok := o.self.(*primitiveValueObject); ok {
if b, ok := p.pValue.(valueBool); ok {
return b
}
}
if o, ok := o.self.(*objectGoReflect); ok {
if o.class == classBoolean && o.valueOf != nil {
return o.valueOf()
}
}
}
r.typeErrorResult(true, "Method Boolean.prototype.valueOf is called on incompatible receiver")
return nil
}
func (r *Runtime) getBooleanPrototype() *Object {
ret := r.global.BooleanPrototype
if ret == nil {
ret = r.newPrimitiveObject(valueFalse, r.global.ObjectPrototype, classBoolean)
r.global.BooleanPrototype = ret
o := ret.self
o._putProp("toString", r.newNativeFunc(r.booleanproto_toString, "toString", 0), true, false, true)
o._putProp("valueOf", r.newNativeFunc(r.booleanproto_valueOf, "valueOf", 0), true, false, true)
o._putProp("constructor", r.getBoolean(), true, false, true)
}
return ret
}
func (r *Runtime) getBoolean() *Object {
ret := r.global.Boolean
if ret == nil {
ret = &Object{runtime: r}
r.global.Boolean = ret
proto := r.getBooleanPrototype()
r.newNativeFuncAndConstruct(ret, r.builtin_Boolean,
r.wrapNativeConstruct(r.builtin_newBoolean, ret, proto), proto, "Boolean", intToValue(1))
}
return ret
}

1058
dop251/goja/builtin_date.go Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,314 @@
package goja
import "apigo.cc/gojs/dop251/goja/unistring"
const propNameStack = "stack"
type errorObject struct {
baseObject
stack []StackFrame
stackPropAdded bool
}
func (e *errorObject) formatStack() String {
var b StringBuilder
val := writeErrorString(&b, e.val)
if val != nil {
b.WriteString(val)
}
b.WriteRune('\n')
for _, frame := range e.stack {
b.writeASCII("\tat ")
frame.WriteToValueBuilder(&b)
b.WriteRune('\n')
}
return b.String()
}
func (e *errorObject) addStackProp() Value {
if !e.stackPropAdded {
res := e._putProp(propNameStack, e.formatStack(), true, false, true)
if len(e.propNames) > 1 {
// reorder property names to ensure 'stack' is the first one
copy(e.propNames[1:], e.propNames)
e.propNames[0] = propNameStack
}
e.stackPropAdded = true
return res
}
return nil
}
func (e *errorObject) getStr(p unistring.String, receiver Value) Value {
return e.getStrWithOwnProp(e.getOwnPropStr(p), p, receiver)
}
func (e *errorObject) getOwnPropStr(name unistring.String) Value {
res := e.baseObject.getOwnPropStr(name)
if res == nil && name == propNameStack {
return e.addStackProp()
}
return res
}
func (e *errorObject) setOwnStr(name unistring.String, val Value, throw bool) bool {
if name == propNameStack {
e.addStackProp()
}
return e.baseObject.setOwnStr(name, val, throw)
}
func (e *errorObject) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) {
return e._setForeignStr(name, e.getOwnPropStr(name), val, receiver, throw)
}
func (e *errorObject) deleteStr(name unistring.String, throw bool) bool {
if name == propNameStack {
e.addStackProp()
}
return e.baseObject.deleteStr(name, throw)
}
func (e *errorObject) defineOwnPropertyStr(name unistring.String, desc PropertyDescriptor, throw bool) bool {
if name == propNameStack {
e.addStackProp()
}
return e.baseObject.defineOwnPropertyStr(name, desc, throw)
}
func (e *errorObject) hasOwnPropertyStr(name unistring.String) bool {
if e.baseObject.hasOwnPropertyStr(name) {
return true
}
return name == propNameStack && !e.stackPropAdded
}
func (e *errorObject) stringKeys(all bool, accum []Value) []Value {
if all && !e.stackPropAdded {
accum = append(accum, asciiString(propNameStack))
}
return e.baseObject.stringKeys(all, accum)
}
func (e *errorObject) iterateStringKeys() iterNextFunc {
e.addStackProp()
return e.baseObject.iterateStringKeys()
}
func (e *errorObject) init() {
e.baseObject.init()
vm := e.val.runtime.vm
e.stack = vm.captureStack(make([]StackFrame, 0, len(vm.callStack)+1), 0)
}
func (r *Runtime) newErrorObject(proto *Object, class string) *errorObject {
obj := &Object{runtime: r}
o := &errorObject{
baseObject: baseObject{
class: class,
val: obj,
extensible: true,
prototype: proto,
},
}
obj.self = o
o.init()
return o
}
func (r *Runtime) builtin_Error(args []Value, proto *Object) *Object {
obj := r.newErrorObject(proto, classError)
if len(args) > 0 && args[0] != _undefined {
obj._putProp("message", args[0].ToString(), true, false, true)
}
if len(args) > 1 && args[1] != _undefined {
if options, ok := args[1].(*Object); ok {
if options.hasProperty(asciiString("cause")) {
obj.defineOwnPropertyStr("cause", PropertyDescriptor{
Writable: FLAG_TRUE,
Enumerable: FLAG_FALSE,
Configurable: FLAG_TRUE,
Value: options.Get("cause"),
}, true)
}
}
}
return obj.val
}
func (r *Runtime) builtin_AggregateError(args []Value, proto *Object) *Object {
obj := r.newErrorObject(proto, classError)
if len(args) > 1 && args[1] != nil && args[1] != _undefined {
obj._putProp("message", args[1].toString(), true, false, true)
}
var errors []Value
if len(args) > 0 {
errors = r.iterableToList(args[0], nil)
}
obj._putProp("errors", r.newArrayValues(errors), true, false, true)
if len(args) > 2 && args[2] != _undefined {
if options, ok := args[2].(*Object); ok {
if options.hasProperty(asciiString("cause")) {
obj.defineOwnPropertyStr("cause", PropertyDescriptor{
Writable: FLAG_TRUE,
Enumerable: FLAG_FALSE,
Configurable: FLAG_TRUE,
Value: options.Get("cause"),
}, true)
}
}
}
return obj.val
}
func writeErrorString(sb *StringBuilder, obj *Object) String {
var nameStr, msgStr String
name := obj.self.getStr("name", nil)
if name == nil || name == _undefined {
nameStr = asciiString("Error")
} else {
nameStr = name.toString()
}
msg := obj.self.getStr("message", nil)
if msg == nil || msg == _undefined {
msgStr = stringEmpty
} else {
msgStr = msg.toString()
}
if nameStr.Length() == 0 {
return msgStr
}
if msgStr.Length() == 0 {
return nameStr
}
sb.WriteString(nameStr)
sb.WriteString(asciiString(": "))
sb.WriteString(msgStr)
return nil
}
func (r *Runtime) error_toString(call FunctionCall) Value {
var sb StringBuilder
val := writeErrorString(&sb, r.toObject(call.This))
if val != nil {
return val
}
return sb.String()
}
func (r *Runtime) createErrorPrototype(name String, ctor *Object) *Object {
o := r.newBaseObject(r.getErrorPrototype(), classObject)
o._putProp("message", stringEmpty, true, false, true)
o._putProp("name", name, true, false, true)
o._putProp("constructor", ctor, true, false, true)
return o.val
}
func (r *Runtime) getErrorPrototype() *Object {
ret := r.global.ErrorPrototype
if ret == nil {
ret = r.NewObject()
r.global.ErrorPrototype = ret
o := ret.self
o._putProp("message", stringEmpty, true, false, true)
o._putProp("name", stringError, true, false, true)
o._putProp("toString", r.newNativeFunc(r.error_toString, "toString", 0), true, false, true)
o._putProp("constructor", r.getError(), true, false, true)
}
return ret
}
func (r *Runtime) getError() *Object {
ret := r.global.Error
if ret == nil {
ret = &Object{runtime: r}
r.global.Error = ret
r.newNativeFuncConstruct(ret, r.builtin_Error, "Error", r.getErrorPrototype(), 1)
}
return ret
}
func (r *Runtime) getAggregateError() *Object {
ret := r.global.AggregateError
if ret == nil {
ret = &Object{runtime: r}
r.global.AggregateError = ret
r.newNativeFuncConstructProto(ret, r.builtin_AggregateError, "AggregateError", r.createErrorPrototype(stringAggregateError, ret), r.getError(), 2)
}
return ret
}
func (r *Runtime) getTypeError() *Object {
ret := r.global.TypeError
if ret == nil {
ret = &Object{runtime: r}
r.global.TypeError = ret
r.newNativeFuncConstructProto(ret, r.builtin_Error, "TypeError", r.createErrorPrototype(stringTypeError, ret), r.getError(), 1)
}
return ret
}
func (r *Runtime) getReferenceError() *Object {
ret := r.global.ReferenceError
if ret == nil {
ret = &Object{runtime: r}
r.global.ReferenceError = ret
r.newNativeFuncConstructProto(ret, r.builtin_Error, "ReferenceError", r.createErrorPrototype(stringReferenceError, ret), r.getError(), 1)
}
return ret
}
func (r *Runtime) getSyntaxError() *Object {
ret := r.global.SyntaxError
if ret == nil {
ret = &Object{runtime: r}
r.global.SyntaxError = ret
r.newNativeFuncConstructProto(ret, r.builtin_Error, "SyntaxError", r.createErrorPrototype(stringSyntaxError, ret), r.getError(), 1)
}
return ret
}
func (r *Runtime) getRangeError() *Object {
ret := r.global.RangeError
if ret == nil {
ret = &Object{runtime: r}
r.global.RangeError = ret
r.newNativeFuncConstructProto(ret, r.builtin_Error, "RangeError", r.createErrorPrototype(stringRangeError, ret), r.getError(), 1)
}
return ret
}
func (r *Runtime) getEvalError() *Object {
ret := r.global.EvalError
if ret == nil {
ret = &Object{runtime: r}
r.global.EvalError = ret
r.newNativeFuncConstructProto(ret, r.builtin_Error, "EvalError", r.createErrorPrototype(stringEvalError, ret), r.getError(), 1)
}
return ret
}
func (r *Runtime) getURIError() *Object {
ret := r.global.URIError
if ret == nil {
ret = &Object{runtime: r}
r.global.URIError = ret
r.newNativeFuncConstructProto(ret, r.builtin_Error, "URIError", r.createErrorPrototype(stringURIError, ret), r.getError(), 1)
}
return ret
}
func (r *Runtime) getGoError() *Object {
ret := r.global.GoError
if ret == nil {
ret = &Object{runtime: r}
r.global.GoError = ret
r.newNativeFuncConstructProto(ret, r.builtin_Error, "GoError", r.createErrorPrototype(stringGoError, ret), r.getError(), 1)
}
return ret
}

View File

@ -0,0 +1,416 @@
package goja
import (
"math"
"sync"
)
func (r *Runtime) functionCtor(args []Value, proto *Object, async, generator bool) *Object {
var sb StringBuilder
if async {
if generator {
sb.WriteString(asciiString("(async function* anonymous("))
} else {
sb.WriteString(asciiString("(async function anonymous("))
}
} else {
if generator {
sb.WriteString(asciiString("(function* anonymous("))
} else {
sb.WriteString(asciiString("(function anonymous("))
}
}
if len(args) > 1 {
ar := args[:len(args)-1]
for i, arg := range ar {
sb.WriteString(arg.toString())
if i < len(ar)-1 {
sb.WriteRune(',')
}
}
}
sb.WriteString(asciiString("\n) {\n"))
if len(args) > 0 {
sb.WriteString(args[len(args)-1].toString())
}
sb.WriteString(asciiString("\n})"))
ret := r.toObject(r.eval(sb.String(), false, false))
ret.self.setProto(proto, true)
return ret
}
func (r *Runtime) builtin_Function(args []Value, proto *Object) *Object {
return r.functionCtor(args, proto, false, false)
}
func (r *Runtime) builtin_asyncFunction(args []Value, proto *Object) *Object {
return r.functionCtor(args, proto, true, false)
}
func (r *Runtime) builtin_generatorFunction(args []Value, proto *Object) *Object {
return r.functionCtor(args, proto, false, true)
}
func (r *Runtime) functionproto_toString(call FunctionCall) Value {
obj := r.toObject(call.This)
switch f := obj.self.(type) {
case funcObjectImpl:
return f.source()
case *proxyObject:
if _, ok := f.target.self.(funcObjectImpl); ok {
return asciiString("function () { [native code] }")
}
}
panic(r.NewTypeError("Function.prototype.toString requires that 'this' be a Function"))
}
func (r *Runtime) functionproto_hasInstance(call FunctionCall) Value {
if o, ok := call.This.(*Object); ok {
if _, ok = o.self.assertCallable(); ok {
return r.toBoolean(o.self.hasInstance(call.Argument(0)))
}
}
return valueFalse
}
func (r *Runtime) createListFromArrayLike(a Value) []Value {
o := r.toObject(a)
if arr := r.checkStdArrayObj(o); arr != nil {
return arr.values
}
l := toLength(o.self.getStr("length", nil))
res := make([]Value, 0, l)
for k := int64(0); k < l; k++ {
res = append(res, nilSafe(o.self.getIdx(valueInt(k), nil)))
}
return res
}
func (r *Runtime) functionproto_apply(call FunctionCall) Value {
var args []Value
if len(call.Arguments) >= 2 {
args = r.createListFromArrayLike(call.Arguments[1])
}
f := r.toCallable(call.This)
return f(FunctionCall{
This: call.Argument(0),
Arguments: args,
})
}
func (r *Runtime) functionproto_call(call FunctionCall) Value {
var args []Value
if len(call.Arguments) > 0 {
args = call.Arguments[1:]
}
f := r.toCallable(call.This)
return f(FunctionCall{
This: call.Argument(0),
Arguments: args,
})
}
func (r *Runtime) boundCallable(target func(FunctionCall) Value, boundArgs []Value) func(FunctionCall) Value {
var this Value
var args []Value
if len(boundArgs) > 0 {
this = boundArgs[0]
args = make([]Value, len(boundArgs)-1)
copy(args, boundArgs[1:])
} else {
this = _undefined
}
return func(call FunctionCall) Value {
a := append(args, call.Arguments...)
return target(FunctionCall{
This: this,
Arguments: a,
})
}
}
func (r *Runtime) boundConstruct(f *Object, target func([]Value, *Object) *Object, boundArgs []Value) func([]Value, *Object) *Object {
if target == nil {
return nil
}
var args []Value
if len(boundArgs) > 1 {
args = make([]Value, len(boundArgs)-1)
copy(args, boundArgs[1:])
}
return func(fargs []Value, newTarget *Object) *Object {
a := append(args, fargs...)
if newTarget == f {
newTarget = nil
}
return target(a, newTarget)
}
}
func (r *Runtime) functionproto_bind(call FunctionCall) Value {
obj := r.toObject(call.This)
fcall := r.toCallable(call.This)
construct := obj.self.assertConstructor()
var l = _positiveZero
if obj.self.hasOwnPropertyStr("length") {
var li int64
switch lenProp := nilSafe(obj.self.getStr("length", nil)).(type) {
case valueInt:
li = lenProp.ToInteger()
case valueFloat:
switch lenProp {
case _positiveInf:
l = lenProp
goto lenNotInt
case _negativeInf:
goto lenNotInt
case _negativeZero:
// no-op, li == 0
default:
if !math.IsNaN(float64(lenProp)) {
li = int64(math.Abs(float64(lenProp)))
} // else li = 0
}
}
if len(call.Arguments) > 1 {
li -= int64(len(call.Arguments)) - 1
}
if li < 0 {
li = 0
}
l = intToValue(li)
}
lenNotInt:
name := obj.self.getStr("name", nil)
nameStr := stringBound_
if s, ok := name.(String); ok {
nameStr = nameStr.Concat(s)
}
v := &Object{runtime: r}
ff := r.newNativeFuncAndConstruct(v, r.boundCallable(fcall, call.Arguments), r.boundConstruct(v, construct, call.Arguments), nil, nameStr.string(), l)
bf := &boundFuncObject{
nativeFuncObject: *ff,
wrapped: obj,
}
bf.prototype = obj.self.proto()
v.self = bf
return v
}
func (r *Runtime) getThrower() *Object {
ret := r.global.thrower
if ret == nil {
ret = r.newNativeFunc(r.builtin_thrower, "", 0)
r.global.thrower = ret
r.object_freeze(FunctionCall{Arguments: []Value{ret}})
}
return ret
}
func (r *Runtime) newThrowerProperty(configurable bool) Value {
thrower := r.getThrower()
return &valueProperty{
getterFunc: thrower,
setterFunc: thrower,
accessor: true,
configurable: configurable,
}
}
func createFunctionProtoTemplate() *objectTemplate {
t := newObjectTemplate()
t.protoFactory = func(r *Runtime) *Object {
return r.global.ObjectPrototype
}
t.putStr("constructor", func(r *Runtime) Value { return valueProp(r.getFunction(), true, false, true) })
t.putStr("length", func(r *Runtime) Value { return valueProp(_positiveZero, false, false, true) })
t.putStr("name", func(r *Runtime) Value { return valueProp(stringEmpty, false, false, true) })
t.putStr("apply", func(r *Runtime) Value { return r.methodProp(r.functionproto_apply, "apply", 2) })
t.putStr("bind", func(r *Runtime) Value { return r.methodProp(r.functionproto_bind, "bind", 1) })
t.putStr("call", func(r *Runtime) Value { return r.methodProp(r.functionproto_call, "call", 1) })
t.putStr("toString", func(r *Runtime) Value { return r.methodProp(r.functionproto_toString, "toString", 0) })
t.putStr("caller", func(r *Runtime) Value { return r.newThrowerProperty(true) })
t.putStr("arguments", func(r *Runtime) Value { return r.newThrowerProperty(true) })
t.putSym(SymHasInstance, func(r *Runtime) Value {
return valueProp(r.newNativeFunc(r.functionproto_hasInstance, "[Symbol.hasInstance]", 1), false, false, false)
})
return t
}
var functionProtoTemplate *objectTemplate
var functionProtoTemplateOnce sync.Once
func getFunctionProtoTemplate() *objectTemplate {
functionProtoTemplateOnce.Do(func() {
functionProtoTemplate = createFunctionProtoTemplate()
})
return functionProtoTemplate
}
func (r *Runtime) getFunctionPrototype() *Object {
ret := r.global.FunctionPrototype
if ret == nil {
ret = &Object{runtime: r}
r.global.FunctionPrototype = ret
r.newTemplatedFuncObject(getFunctionProtoTemplate(), ret, func(FunctionCall) Value {
return _undefined
}, nil)
}
return ret
}
func (r *Runtime) createFunction(v *Object) objectImpl {
return r.newNativeFuncConstructObj(v, r.builtin_Function, "Function", r.getFunctionPrototype(), 1)
}
func (r *Runtime) createAsyncFunctionProto(val *Object) objectImpl {
o := &baseObject{
class: classObject,
val: val,
extensible: true,
prototype: r.getFunctionPrototype(),
}
o.init()
o._putProp("constructor", r.getAsyncFunction(), true, false, true)
o._putSym(SymToStringTag, valueProp(asciiString(classAsyncFunction), false, false, true))
return o
}
func (r *Runtime) getAsyncFunctionPrototype() *Object {
var o *Object
if o = r.global.AsyncFunctionPrototype; o == nil {
o = &Object{runtime: r}
r.global.AsyncFunctionPrototype = o
o.self = r.createAsyncFunctionProto(o)
}
return o
}
func (r *Runtime) createAsyncFunction(val *Object) objectImpl {
o := r.newNativeFuncConstructObj(val, r.builtin_asyncFunction, "AsyncFunction", r.getAsyncFunctionPrototype(), 1)
return o
}
func (r *Runtime) getAsyncFunction() *Object {
var o *Object
if o = r.global.AsyncFunction; o == nil {
o = &Object{runtime: r}
r.global.AsyncFunction = o
o.self = r.createAsyncFunction(o)
}
return o
}
func (r *Runtime) builtin_genproto_next(call FunctionCall) Value {
if o, ok := call.This.(*Object); ok {
if gen, ok := o.self.(*generatorObject); ok {
return gen.next(call.Argument(0))
}
}
panic(r.NewTypeError("Method [Generator].prototype.next called on incompatible receiver"))
}
func (r *Runtime) builtin_genproto_return(call FunctionCall) Value {
if o, ok := call.This.(*Object); ok {
if gen, ok := o.self.(*generatorObject); ok {
return gen._return(call.Argument(0))
}
}
panic(r.NewTypeError("Method [Generator].prototype.return called on incompatible receiver"))
}
func (r *Runtime) builtin_genproto_throw(call FunctionCall) Value {
if o, ok := call.This.(*Object); ok {
if gen, ok := o.self.(*generatorObject); ok {
return gen.throw(call.Argument(0))
}
}
panic(r.NewTypeError("Method [Generator].prototype.throw called on incompatible receiver"))
}
func (r *Runtime) createGeneratorFunctionProto(val *Object) objectImpl {
o := newBaseObjectObj(val, r.getFunctionPrototype(), classObject)
o._putProp("constructor", r.getGeneratorFunction(), false, false, true)
o._putProp("prototype", r.getGeneratorPrototype(), false, false, true)
o._putSym(SymToStringTag, valueProp(asciiString(classGeneratorFunction), false, false, true))
return o
}
func (r *Runtime) getGeneratorFunctionPrototype() *Object {
var o *Object
if o = r.global.GeneratorFunctionPrototype; o == nil {
o = &Object{runtime: r}
r.global.GeneratorFunctionPrototype = o
o.self = r.createGeneratorFunctionProto(o)
}
return o
}
func (r *Runtime) createGeneratorFunction(val *Object) objectImpl {
o := r.newNativeFuncConstructObj(val, r.builtin_generatorFunction, "GeneratorFunction", r.getGeneratorFunctionPrototype(), 1)
return o
}
func (r *Runtime) getGeneratorFunction() *Object {
var o *Object
if o = r.global.GeneratorFunction; o == nil {
o = &Object{runtime: r}
r.global.GeneratorFunction = o
o.self = r.createGeneratorFunction(o)
}
return o
}
func (r *Runtime) createGeneratorProto(val *Object) objectImpl {
o := newBaseObjectObj(val, r.getIteratorPrototype(), classObject)
o._putProp("constructor", r.getGeneratorFunctionPrototype(), false, false, true)
o._putProp("next", r.newNativeFunc(r.builtin_genproto_next, "next", 1), true, false, true)
o._putProp("return", r.newNativeFunc(r.builtin_genproto_return, "return", 1), true, false, true)
o._putProp("throw", r.newNativeFunc(r.builtin_genproto_throw, "throw", 1), true, false, true)
o._putSym(SymToStringTag, valueProp(asciiString(classGenerator), false, false, true))
return o
}
func (r *Runtime) getGeneratorPrototype() *Object {
var o *Object
if o = r.global.GeneratorPrototype; o == nil {
o = &Object{runtime: r}
r.global.GeneratorPrototype = o
o.self = r.createGeneratorProto(o)
}
return o
}
func (r *Runtime) getFunction() *Object {
ret := r.global.Function
if ret == nil {
ret = &Object{runtime: r}
r.global.Function = ret
ret.self = r.createFunction(ret)
}
return ret
}

View File

@ -0,0 +1,576 @@
package goja
import (
"errors"
"io"
"math"
"regexp"
"strconv"
"strings"
"sync"
"unicode/utf8"
"apigo.cc/gojs/dop251/goja/unistring"
)
const hexUpper = "0123456789ABCDEF"
var (
parseFloatRegexp = regexp.MustCompile(`^([+-]?(?:Infinity|[0-9]*\.?[0-9]*(?:[eE][+-]?[0-9]+)?))`)
)
func (r *Runtime) builtin_isNaN(call FunctionCall) Value {
if math.IsNaN(call.Argument(0).ToFloat()) {
return valueTrue
} else {
return valueFalse
}
}
func (r *Runtime) builtin_parseInt(call FunctionCall) Value {
str := call.Argument(0).toString().toTrimmedUTF8()
radix := int(toInt32(call.Argument(1)))
v, _ := parseInt(str, radix)
return v
}
func (r *Runtime) builtin_parseFloat(call FunctionCall) Value {
m := parseFloatRegexp.FindStringSubmatch(call.Argument(0).toString().toTrimmedUTF8())
if len(m) == 2 {
if s := m[1]; s != "" && s != "+" && s != "-" {
switch s {
case "+", "-":
case "Infinity", "+Infinity":
return _positiveInf
case "-Infinity":
return _negativeInf
default:
f, err := strconv.ParseFloat(s, 64)
if err == nil || isRangeErr(err) {
return floatToValue(f)
}
}
}
}
return _NaN
}
func (r *Runtime) builtin_isFinite(call FunctionCall) Value {
f := call.Argument(0).ToFloat()
if math.IsNaN(f) || math.IsInf(f, 0) {
return valueFalse
}
return valueTrue
}
func (r *Runtime) _encode(uriString String, unescaped *[256]bool) String {
reader := uriString.Reader()
utf8Buf := make([]byte, utf8.UTFMax)
needed := false
l := 0
for {
rn, _, err := reader.ReadRune()
if err != nil {
if err != io.EOF {
panic(r.newError(r.getURIError(), "Malformed URI"))
}
break
}
if rn >= utf8.RuneSelf {
needed = true
l += utf8.EncodeRune(utf8Buf, rn) * 3
} else if !unescaped[rn] {
needed = true
l += 3
} else {
l++
}
}
if !needed {
return uriString
}
buf := make([]byte, l)
i := 0
reader = uriString.Reader()
for {
rn, _, err := reader.ReadRune()
if err == io.EOF {
break
}
if rn >= utf8.RuneSelf {
n := utf8.EncodeRune(utf8Buf, rn)
for _, b := range utf8Buf[:n] {
buf[i] = '%'
buf[i+1] = hexUpper[b>>4]
buf[i+2] = hexUpper[b&15]
i += 3
}
} else if !unescaped[rn] {
buf[i] = '%'
buf[i+1] = hexUpper[rn>>4]
buf[i+2] = hexUpper[rn&15]
i += 3
} else {
buf[i] = byte(rn)
i++
}
}
return asciiString(buf)
}
func (r *Runtime) _decode(sv String, reservedSet *[256]bool) String {
s := sv.String()
hexCount := 0
for i := 0; i < len(s); {
switch s[i] {
case '%':
if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
panic(r.newError(r.getURIError(), "Malformed URI"))
}
c := unhex(s[i+1])<<4 | unhex(s[i+2])
if !reservedSet[c] {
hexCount++
}
i += 3
default:
i++
}
}
if hexCount == 0 {
return sv
}
t := make([]byte, len(s)-hexCount*2)
j := 0
isUnicode := false
for i := 0; i < len(s); {
ch := s[i]
switch ch {
case '%':
c := unhex(s[i+1])<<4 | unhex(s[i+2])
if reservedSet[c] {
t[j] = s[i]
t[j+1] = s[i+1]
t[j+2] = s[i+2]
j += 3
} else {
t[j] = c
if c >= utf8.RuneSelf {
isUnicode = true
}
j++
}
i += 3
default:
if ch >= utf8.RuneSelf {
isUnicode = true
}
t[j] = ch
j++
i++
}
}
if !isUnicode {
return asciiString(t)
}
us := make([]rune, 0, len(s))
for len(t) > 0 {
rn, size := utf8.DecodeRune(t)
if rn == utf8.RuneError {
if size != 3 || t[0] != 0xef || t[1] != 0xbf || t[2] != 0xbd {
panic(r.newError(r.getURIError(), "Malformed URI"))
}
}
us = append(us, rn)
t = t[size:]
}
return unicodeStringFromRunes(us)
}
func ishex(c byte) bool {
switch {
case '0' <= c && c <= '9':
return true
case 'a' <= c && c <= 'f':
return true
case 'A' <= c && c <= 'F':
return true
}
return false
}
func unhex(c byte) byte {
switch {
case '0' <= c && c <= '9':
return c - '0'
case 'a' <= c && c <= 'f':
return c - 'a' + 10
case 'A' <= c && c <= 'F':
return c - 'A' + 10
}
return 0
}
func (r *Runtime) builtin_decodeURI(call FunctionCall) Value {
uriString := call.Argument(0).toString()
return r._decode(uriString, &uriReservedHash)
}
func (r *Runtime) builtin_decodeURIComponent(call FunctionCall) Value {
uriString := call.Argument(0).toString()
return r._decode(uriString, &emptyEscapeSet)
}
func (r *Runtime) builtin_encodeURI(call FunctionCall) Value {
uriString := call.Argument(0).toString()
return r._encode(uriString, &uriReservedUnescapedHash)
}
func (r *Runtime) builtin_encodeURIComponent(call FunctionCall) Value {
uriString := call.Argument(0).toString()
return r._encode(uriString, &uriUnescaped)
}
func (r *Runtime) builtin_escape(call FunctionCall) Value {
s := call.Argument(0).toString()
var sb strings.Builder
l := s.Length()
for i := 0; i < l; i++ {
r := s.CharAt(i)
if r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' || r >= '0' && r <= '9' ||
r == '@' || r == '*' || r == '_' || r == '+' || r == '-' || r == '.' || r == '/' {
sb.WriteByte(byte(r))
} else if r <= 0xff {
sb.WriteByte('%')
sb.WriteByte(hexUpper[r>>4])
sb.WriteByte(hexUpper[r&0xf])
} else {
sb.WriteString("%u")
sb.WriteByte(hexUpper[r>>12])
sb.WriteByte(hexUpper[(r>>8)&0xf])
sb.WriteByte(hexUpper[(r>>4)&0xf])
sb.WriteByte(hexUpper[r&0xf])
}
}
return asciiString(sb.String())
}
func (r *Runtime) builtin_unescape(call FunctionCall) Value {
s := call.Argument(0).toString()
l := s.Length()
var asciiBuf []byte
var unicodeBuf []uint16
_, u := devirtualizeString(s)
unicode := u != nil
if unicode {
unicodeBuf = make([]uint16, 1, l+1)
unicodeBuf[0] = unistring.BOM
} else {
asciiBuf = make([]byte, 0, l)
}
for i := 0; i < l; {
r := s.CharAt(i)
if r == '%' {
if i <= l-6 && s.CharAt(i+1) == 'u' {
c0 := s.CharAt(i + 2)
c1 := s.CharAt(i + 3)
c2 := s.CharAt(i + 4)
c3 := s.CharAt(i + 5)
if c0 <= 0xff && ishex(byte(c0)) &&
c1 <= 0xff && ishex(byte(c1)) &&
c2 <= 0xff && ishex(byte(c2)) &&
c3 <= 0xff && ishex(byte(c3)) {
r = uint16(unhex(byte(c0)))<<12 |
uint16(unhex(byte(c1)))<<8 |
uint16(unhex(byte(c2)))<<4 |
uint16(unhex(byte(c3)))
i += 5
goto out
}
}
if i <= l-3 {
c0 := s.CharAt(i + 1)
c1 := s.CharAt(i + 2)
if c0 <= 0xff && ishex(byte(c0)) &&
c1 <= 0xff && ishex(byte(c1)) {
r = uint16(unhex(byte(c0))<<4 | unhex(byte(c1)))
i += 2
}
}
}
out:
if r >= utf8.RuneSelf && !unicode {
unicodeBuf = make([]uint16, 1, l+1)
unicodeBuf[0] = unistring.BOM
for _, b := range asciiBuf {
unicodeBuf = append(unicodeBuf, uint16(b))
}
asciiBuf = nil
unicode = true
}
if unicode {
unicodeBuf = append(unicodeBuf, r)
} else {
asciiBuf = append(asciiBuf, byte(r))
}
i++
}
if unicode {
return unicodeString(unicodeBuf)
}
return asciiString(asciiBuf)
}
func createGlobalObjectTemplate() *objectTemplate {
t := newObjectTemplate()
t.protoFactory = func(r *Runtime) *Object {
return r.global.ObjectPrototype
}
t.putStr("Object", func(r *Runtime) Value { return valueProp(r.getObject(), true, false, true) })
t.putStr("Function", func(r *Runtime) Value { return valueProp(r.getFunction(), true, false, true) })
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) })
t.putStr("Proxy", func(r *Runtime) Value { return valueProp(r.getProxy(), true, false, true) })
t.putStr("Reflect", func(r *Runtime) Value { return valueProp(r.getReflect(), true, false, true) })
t.putStr("Error", func(r *Runtime) Value { return valueProp(r.getError(), true, false, true) })
t.putStr("AggregateError", func(r *Runtime) Value { return valueProp(r.getAggregateError(), true, false, true) })
t.putStr("TypeError", func(r *Runtime) Value { return valueProp(r.getTypeError(), true, false, true) })
t.putStr("ReferenceError", func(r *Runtime) Value { return valueProp(r.getReferenceError(), true, false, true) })
t.putStr("SyntaxError", func(r *Runtime) Value { return valueProp(r.getSyntaxError(), true, false, true) })
t.putStr("RangeError", func(r *Runtime) Value { return valueProp(r.getRangeError(), true, false, true) })
t.putStr("EvalError", func(r *Runtime) Value { return valueProp(r.getEvalError(), true, false, true) })
t.putStr("URIError", func(r *Runtime) Value { return valueProp(r.getURIError(), true, false, true) })
t.putStr("GoError", func(r *Runtime) Value { return valueProp(r.getGoError(), true, false, true) })
t.putStr("eval", func(r *Runtime) Value { return valueProp(r.getEval(), true, false, true) })
t.putStr("Math", func(r *Runtime) Value { return valueProp(r.getMath(), true, false, true) })
t.putStr("JSON", func(r *Runtime) Value { return valueProp(r.getJSON(), true, false, true) })
addTypedArrays(t)
t.putStr("Symbol", func(r *Runtime) Value { return valueProp(r.getSymbol(), true, false, true) })
t.putStr("WeakSet", func(r *Runtime) Value { return valueProp(r.getWeakSet(), true, false, true) })
t.putStr("WeakMap", func(r *Runtime) Value { return valueProp(r.getWeakMap(), true, false, true) })
t.putStr("Map", func(r *Runtime) Value { return valueProp(r.getMap(), true, false, true) })
t.putStr("Set", func(r *Runtime) Value { return valueProp(r.getSet(), true, false, true) })
t.putStr("Promise", func(r *Runtime) Value { return valueProp(r.getPromise(), true, false, true) })
t.putStr("globalThis", func(r *Runtime) Value { return valueProp(r.globalObject, true, false, true) })
t.putStr("NaN", func(r *Runtime) Value { return valueProp(_NaN, false, false, false) })
t.putStr("undefined", func(r *Runtime) Value { return valueProp(_undefined, false, false, false) })
t.putStr("Infinity", func(r *Runtime) Value { return valueProp(_positiveInf, false, false, false) })
t.putStr("isNaN", func(r *Runtime) Value { return r.methodProp(r.builtin_isNaN, "isNaN", 1) })
t.putStr("parseInt", func(r *Runtime) Value { return valueProp(r.getParseInt(), true, false, true) })
t.putStr("parseFloat", func(r *Runtime) Value { return valueProp(r.getParseFloat(), true, false, true) })
t.putStr("isFinite", func(r *Runtime) Value { return r.methodProp(r.builtin_isFinite, "isFinite", 1) })
t.putStr("decodeURI", func(r *Runtime) Value { return r.methodProp(r.builtin_decodeURI, "decodeURI", 1) })
t.putStr("decodeURIComponent", func(r *Runtime) Value { return r.methodProp(r.builtin_decodeURIComponent, "decodeURIComponent", 1) })
t.putStr("encodeURI", func(r *Runtime) Value { return r.methodProp(r.builtin_encodeURI, "encodeURI", 1) })
t.putStr("encodeURIComponent", func(r *Runtime) Value { return r.methodProp(r.builtin_encodeURIComponent, "encodeURIComponent", 1) })
t.putStr("escape", func(r *Runtime) Value { return r.methodProp(r.builtin_escape, "escape", 1) })
t.putStr("unescape", func(r *Runtime) Value { return r.methodProp(r.builtin_unescape, "unescape", 1) })
// TODO: Annex B
t.putSym(SymToStringTag, func(r *Runtime) Value { return valueProp(asciiString(classGlobal), false, false, true) })
return t
}
var globalObjectTemplate *objectTemplate
var globalObjectTemplateOnce sync.Once
func getGlobalObjectTemplate() *objectTemplate {
globalObjectTemplateOnce.Do(func() {
globalObjectTemplate = createGlobalObjectTemplate()
})
return globalObjectTemplate
}
func (r *Runtime) getEval() *Object {
ret := r.global.Eval
if ret == nil {
ret = r.newNativeFunc(r.builtin_eval, "eval", 1)
r.global.Eval = ret
}
return ret
}
func digitVal(d byte) int {
var v byte
switch {
case '0' <= d && d <= '9':
v = d - '0'
case 'a' <= d && d <= 'z':
v = d - 'a' + 10
case 'A' <= d && d <= 'Z':
v = d - 'A' + 10
default:
return 36
}
return int(v)
}
// ECMAScript compatible version of strconv.ParseInt
func parseInt(s string, base int) (Value, error) {
var n int64
var err error
var cutoff, maxVal int64
var sign bool
i := 0
if len(s) < 1 {
err = strconv.ErrSyntax
goto Error
}
switch s[0] {
case '-':
sign = true
s = s[1:]
case '+':
s = s[1:]
}
if len(s) < 1 {
err = strconv.ErrSyntax
goto Error
}
// Look for hex prefix.
if s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X') {
if base == 0 || base == 16 {
base = 16
s = s[2:]
}
}
switch {
case len(s) < 1:
err = strconv.ErrSyntax
goto Error
case 2 <= base && base <= 36:
// valid base; nothing to do
case base == 0:
// Look for hex prefix.
switch {
case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
if len(s) < 3 {
err = strconv.ErrSyntax
goto Error
}
base = 16
s = s[2:]
default:
base = 10
}
default:
err = errors.New("invalid base " + strconv.Itoa(base))
goto Error
}
// Cutoff is the smallest number such that cutoff*base > maxInt64.
// Use compile-time constants for common cases.
switch base {
case 10:
cutoff = math.MaxInt64/10 + 1
case 16:
cutoff = math.MaxInt64/16 + 1
default:
cutoff = math.MaxInt64/int64(base) + 1
}
maxVal = math.MaxInt64
for ; i < len(s); i++ {
if n >= cutoff {
// n*base overflows
return parseLargeInt(float64(n), s[i:], base, sign)
}
v := digitVal(s[i])
if v >= base {
break
}
n *= int64(base)
n1 := n + int64(v)
if n1 < n || n1 > maxVal {
// n+v overflows
return parseLargeInt(float64(n)+float64(v), s[i+1:], base, sign)
}
n = n1
}
if i == 0 {
err = strconv.ErrSyntax
goto Error
}
if sign {
n = -n
}
return intToValue(n), nil
Error:
return _NaN, err
}
func parseLargeInt(n float64, s string, base int, sign bool) (Value, error) {
i := 0
b := float64(base)
for ; i < len(s); i++ {
v := digitVal(s[i])
if v >= base {
break
}
n = n*b + float64(v)
}
if sign {
n = -n
}
// We know it can't be represented as int, so use valueFloat instead of floatToValue
return valueFloat(n), nil
}
var (
uriUnescaped [256]bool
uriReserved [256]bool
uriReservedHash [256]bool
uriReservedUnescapedHash [256]bool
emptyEscapeSet [256]bool
)
func init() {
for _, c := range "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()" {
uriUnescaped[c] = true
}
for _, c := range ";/?:@&=+$," {
uriReserved[c] = true
}
for i := 0; i < 256; i++ {
if uriUnescaped[i] || uriReserved[i] {
uriReservedUnescapedHash[i] = true
}
uriReservedHash[i] = uriReserved[i]
}
uriReservedUnescapedHash['#'] = true
uriReservedHash['#'] = true
}

542
dop251/goja/builtin_json.go Normal file
View File

@ -0,0 +1,542 @@
package goja
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"math"
"strconv"
"strings"
"unicode/utf16"
"unicode/utf8"
"apigo.cc/gojs/dop251/goja/unistring"
)
const hex = "0123456789abcdef"
func (r *Runtime) builtinJSON_parse(call FunctionCall) Value {
d := json.NewDecoder(strings.NewReader(call.Argument(0).toString().String()))
value, err := r.builtinJSON_decodeValue(d)
if errors.Is(err, io.EOF) {
panic(r.newError(r.getSyntaxError(), "Unexpected end of JSON input (%v)", err.Error()))
}
if err != nil {
panic(r.newError(r.getSyntaxError(), err.Error()))
}
if tok, err := d.Token(); err != io.EOF {
panic(r.newError(r.getSyntaxError(), "Unexpected token at the end: %v", tok))
}
var reviver func(FunctionCall) Value
if arg1 := call.Argument(1); arg1 != _undefined {
reviver, _ = arg1.ToObject(r).self.assertCallable()
}
if reviver != nil {
root := r.NewObject()
createDataPropertyOrThrow(root, stringEmpty, value)
return r.builtinJSON_reviveWalk(reviver, root, stringEmpty)
}
return value
}
func (r *Runtime) builtinJSON_decodeToken(d *json.Decoder, tok json.Token) (Value, error) {
switch tok := tok.(type) {
case json.Delim:
switch tok {
case '{':
return r.builtinJSON_decodeObject(d)
case '[':
return r.builtinJSON_decodeArray(d)
}
case nil:
return _null, nil
case string:
return newStringValue(tok), nil
case float64:
return floatToValue(tok), nil
case bool:
if tok {
return valueTrue, nil
}
return valueFalse, nil
}
return nil, fmt.Errorf("Unexpected token (%T): %v", tok, tok)
}
func (r *Runtime) builtinJSON_decodeValue(d *json.Decoder) (Value, error) {
tok, err := d.Token()
if err != nil {
return nil, err
}
return r.builtinJSON_decodeToken(d, tok)
}
func (r *Runtime) builtinJSON_decodeObject(d *json.Decoder) (*Object, error) {
object := r.NewObject()
for {
key, end, err := r.builtinJSON_decodeObjectKey(d)
if err != nil {
return nil, err
}
if end {
break
}
value, err := r.builtinJSON_decodeValue(d)
if err != nil {
return nil, err
}
object.self._putProp(unistring.NewFromString(key), value, true, true, true)
}
return object, nil
}
func (r *Runtime) builtinJSON_decodeObjectKey(d *json.Decoder) (string, bool, error) {
tok, err := d.Token()
if err != nil {
return "", false, err
}
switch tok := tok.(type) {
case json.Delim:
if tok == '}' {
return "", true, nil
}
case string:
return tok, false, nil
}
return "", false, fmt.Errorf("Unexpected token (%T): %v", tok, tok)
}
func (r *Runtime) builtinJSON_decodeArray(d *json.Decoder) (*Object, error) {
var arrayValue []Value
for {
tok, err := d.Token()
if err != nil {
return nil, err
}
if delim, ok := tok.(json.Delim); ok {
if delim == ']' {
break
}
}
value, err := r.builtinJSON_decodeToken(d, tok)
if err != nil {
return nil, err
}
arrayValue = append(arrayValue, value)
}
return r.newArrayValues(arrayValue), nil
}
func (r *Runtime) builtinJSON_reviveWalk(reviver func(FunctionCall) Value, holder *Object, name Value) Value {
value := nilSafe(holder.get(name, nil))
if object, ok := value.(*Object); ok {
if isArray(object) {
length := toLength(object.self.getStr("length", nil))
for index := int64(0); index < length; index++ {
name := asciiString(strconv.FormatInt(index, 10))
value := r.builtinJSON_reviveWalk(reviver, object, name)
if value == _undefined {
object.delete(name, false)
} else {
createDataProperty(object, name, value)
}
}
} else {
for _, name := range object.self.stringKeys(false, nil) {
value := r.builtinJSON_reviveWalk(reviver, object, name)
if value == _undefined {
object.self.deleteStr(name.string(), false)
} else {
createDataProperty(object, name, value)
}
}
}
}
return reviver(FunctionCall{
This: holder,
Arguments: []Value{name, value},
})
}
type _builtinJSON_stringifyContext struct {
r *Runtime
stack []*Object
propertyList []Value
replacerFunction func(FunctionCall) Value
gap, indent string
buf bytes.Buffer
allAscii bool
}
func (r *Runtime) builtinJSON_stringify(call FunctionCall) Value {
ctx := _builtinJSON_stringifyContext{
r: r,
allAscii: true,
}
replacer, _ := call.Argument(1).(*Object)
if replacer != nil {
if isArray(replacer) {
length := toLength(replacer.self.getStr("length", nil))
seen := map[string]bool{}
propertyList := make([]Value, length)
length = 0
for index := range propertyList {
var name string
value := replacer.self.getIdx(valueInt(int64(index)), nil)
switch v := value.(type) {
case valueFloat, valueInt, String:
name = value.String()
case *Object:
switch v.self.className() {
case classNumber, classString:
name = value.String()
default:
continue
}
default:
continue
}
if seen[name] {
continue
}
seen[name] = true
propertyList[length] = newStringValue(name)
length += 1
}
ctx.propertyList = propertyList[0:length]
} else if c, ok := replacer.self.assertCallable(); ok {
ctx.replacerFunction = c
}
}
if spaceValue := call.Argument(2); spaceValue != _undefined {
if o, ok := spaceValue.(*Object); ok {
switch oImpl := o.self.(type) {
case *primitiveValueObject:
switch oImpl.pValue.(type) {
case valueInt, valueFloat:
spaceValue = o.ToNumber()
}
case *stringObject:
spaceValue = o.ToString()
}
}
isNum := false
var num int64
if i, ok := spaceValue.(valueInt); ok {
num = int64(i)
isNum = true
} else if f, ok := spaceValue.(valueFloat); ok {
num = int64(f)
isNum = true
}
if isNum {
if num > 0 {
if num > 10 {
num = 10
}
ctx.gap = strings.Repeat(" ", int(num))
}
} else {
if s, ok := spaceValue.(String); ok {
str := s.String()
if len(str) > 10 {
ctx.gap = str[:10]
} else {
ctx.gap = str
}
}
}
}
if ctx.do(call.Argument(0)) {
if ctx.allAscii {
return asciiString(ctx.buf.String())
} else {
return &importedString{
s: ctx.buf.String(),
}
}
}
return _undefined
}
func (ctx *_builtinJSON_stringifyContext) do(v Value) bool {
holder := ctx.r.NewObject()
createDataPropertyOrThrow(holder, stringEmpty, v)
return ctx.str(stringEmpty, holder)
}
func (ctx *_builtinJSON_stringifyContext) str(key Value, holder *Object) bool {
value := nilSafe(holder.get(key, nil))
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,
Arguments: []Value{key},
})
}
}
}
if ctx.replacerFunction != nil {
value = ctx.replacerFunction(FunctionCall{
This: holder,
Arguments: []Value{key, value},
})
}
if o, ok := value.(*Object); ok {
switch o1 := o.self.(type) {
case *primitiveValueObject:
switch pValue := o1.pValue.(type) {
case valueInt, valueFloat:
value = o.ToNumber()
default:
value = pValue
}
case *stringObject:
value = o.toString()
case *objectGoReflect:
if o1.toJson != nil {
value = ctx.r.ToValue(o1.toJson())
} else if v, ok := o1.origValue.Interface().(json.Marshaler); ok {
b, err := v.MarshalJSON()
if err != nil {
panic(ctx.r.NewGoError(err))
}
ctx.buf.Write(b)
ctx.allAscii = false
return true
} else {
switch o1.className() {
case classNumber:
value = o1.val.ordinaryToPrimitiveNumber()
case classString:
value = o1.val.ordinaryToPrimitiveString()
case classBoolean:
if o.ToInteger() != 0 {
value = valueTrue
} else {
value = valueFalse
}
}
if o1.exportType() == typeBigInt {
value = o1.val.ordinaryToPrimitiveNumber()
}
}
}
}
switch value1 := value.(type) {
case valueBool:
if value1 {
ctx.buf.WriteString("true")
} else {
ctx.buf.WriteString("false")
}
case String:
ctx.quote(value1)
case valueInt:
ctx.buf.WriteString(value.String())
case valueFloat:
if !math.IsNaN(float64(value1)) && !math.IsInf(float64(value1), 0) {
ctx.buf.WriteString(value.String())
} else {
ctx.buf.WriteString("null")
}
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) {
ctx.r.typeErrorResult(true, "Converting circular structure to JSON")
}
}
ctx.stack = append(ctx.stack, value1)
defer func() { ctx.stack = ctx.stack[:len(ctx.stack)-1] }()
if _, ok := value1.self.assertCallable(); !ok {
if isArray(value1) {
ctx.ja(value1)
} else {
ctx.jo(value1)
}
} else {
return false
}
default:
return false
}
return true
}
func (ctx *_builtinJSON_stringifyContext) ja(array *Object) {
var stepback string
if ctx.gap != "" {
stepback = ctx.indent
ctx.indent += ctx.gap
}
length := toLength(array.self.getStr("length", nil))
if length == 0 {
ctx.buf.WriteString("[]")
return
}
ctx.buf.WriteByte('[')
var separator string
if ctx.gap != "" {
ctx.buf.WriteByte('\n')
ctx.buf.WriteString(ctx.indent)
separator = ",\n" + ctx.indent
} else {
separator = ","
}
for i := int64(0); i < length; i++ {
if !ctx.str(asciiString(strconv.FormatInt(i, 10)), array) {
ctx.buf.WriteString("null")
}
if i < length-1 {
ctx.buf.WriteString(separator)
}
}
if ctx.gap != "" {
ctx.buf.WriteByte('\n')
ctx.buf.WriteString(stepback)
ctx.indent = stepback
}
ctx.buf.WriteByte(']')
}
func (ctx *_builtinJSON_stringifyContext) jo(object *Object) {
var stepback string
if ctx.gap != "" {
stepback = ctx.indent
ctx.indent += ctx.gap
}
ctx.buf.WriteByte('{')
mark := ctx.buf.Len()
var separator string
if ctx.gap != "" {
ctx.buf.WriteByte('\n')
ctx.buf.WriteString(ctx.indent)
separator = ",\n" + ctx.indent
} else {
separator = ","
}
var props []Value
if ctx.propertyList == nil {
props = object.self.stringKeys(false, nil)
} else {
props = ctx.propertyList
}
empty := true
for _, name := range props {
off := ctx.buf.Len()
if !empty {
ctx.buf.WriteString(separator)
}
ctx.quote(name.toString())
if ctx.gap != "" {
ctx.buf.WriteString(": ")
} else {
ctx.buf.WriteByte(':')
}
if ctx.str(name, object) {
if empty {
empty = false
}
} else {
ctx.buf.Truncate(off)
}
}
if empty {
ctx.buf.Truncate(mark)
} else {
if ctx.gap != "" {
ctx.buf.WriteByte('\n')
ctx.buf.WriteString(stepback)
ctx.indent = stepback
}
}
ctx.buf.WriteByte('}')
}
func (ctx *_builtinJSON_stringifyContext) quote(str String) {
ctx.buf.WriteByte('"')
reader := &lenientUtf16Decoder{utf16Reader: str.utf16Reader()}
for {
r, _, err := reader.ReadRune()
if err != nil {
break
}
switch r {
case '"', '\\':
ctx.buf.WriteByte('\\')
ctx.buf.WriteByte(byte(r))
case 0x08:
ctx.buf.WriteString(`\b`)
case 0x09:
ctx.buf.WriteString(`\t`)
case 0x0A:
ctx.buf.WriteString(`\n`)
case 0x0C:
ctx.buf.WriteString(`\f`)
case 0x0D:
ctx.buf.WriteString(`\r`)
default:
if r < 0x20 {
ctx.buf.WriteString(`\u00`)
ctx.buf.WriteByte(hex[r>>4])
ctx.buf.WriteByte(hex[r&0xF])
} else {
if utf16.IsSurrogate(r) {
ctx.buf.WriteString(`\u`)
ctx.buf.WriteByte(hex[r>>12])
ctx.buf.WriteByte(hex[(r>>8)&0xF])
ctx.buf.WriteByte(hex[(r>>4)&0xF])
ctx.buf.WriteByte(hex[r&0xF])
} else {
ctx.buf.WriteRune(r)
if ctx.allAscii && r >= utf8.RuneSelf {
ctx.allAscii = false
}
}
}
}
}
ctx.buf.WriteByte('"')
}
func (r *Runtime) getJSON() *Object {
ret := r.global.JSON
if ret == nil {
JSON := r.newBaseObject(r.global.ObjectPrototype, classObject)
ret = JSON.val
r.global.JSON = ret
JSON._putProp("parse", r.newNativeFunc(r.builtinJSON_parse, "parse", 2), true, false, true)
JSON._putProp("stringify", r.newNativeFunc(r.builtinJSON_stringify, "stringify", 3), true, false, true)
JSON._putSym(SymToStringTag, valueProp(asciiString(classJSON), false, false, true))
}
return ret
}

342
dop251/goja/builtin_map.go Normal file
View File

@ -0,0 +1,342 @@
package goja
import (
"reflect"
)
var mapExportType = reflect.TypeOf([][2]interface{}{})
type mapObject struct {
baseObject
m *orderedMap
}
type mapIterObject struct {
baseObject
iter *orderedMapIter
kind iterationKind
}
func (o *mapIterObject) next() Value {
if o.iter == nil {
return o.val.runtime.createIterResultObject(_undefined, true)
}
entry := o.iter.next()
if entry == nil {
o.iter = nil
return o.val.runtime.createIterResultObject(_undefined, true)
}
var result Value
switch o.kind {
case iterationKindKey:
result = entry.key
case iterationKindValue:
result = entry.value
default:
result = o.val.runtime.newArrayValues([]Value{entry.key, entry.value})
}
return o.val.runtime.createIterResultObject(result, false)
}
func (mo *mapObject) init() {
mo.baseObject.init()
mo.m = newOrderedMap(mo.val.runtime.getHash())
}
func (mo *mapObject) exportType() reflect.Type {
return mapExportType
}
func (mo *mapObject) export(ctx *objectExportCtx) interface{} {
m := make([][2]interface{}, mo.m.size)
ctx.put(mo.val, m)
iter := mo.m.newIter()
for i := 0; i < len(m); i++ {
entry := iter.next()
if entry == nil {
break
}
m[i][0] = exportValue(entry.key, ctx)
m[i][1] = exportValue(entry.value, ctx)
}
return m
}
func (mo *mapObject) exportToMap(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error {
dst.Set(reflect.MakeMap(typ))
ctx.putTyped(mo.val, typ, dst.Interface())
keyTyp := typ.Key()
elemTyp := typ.Elem()
iter := mo.m.newIter()
r := mo.val.runtime
for {
entry := iter.next()
if entry == nil {
break
}
keyVal := reflect.New(keyTyp).Elem()
err := r.toReflectValue(entry.key, keyVal, ctx)
if err != nil {
return err
}
elemVal := reflect.New(elemTyp).Elem()
err = r.toReflectValue(entry.value, elemVal, ctx)
if err != nil {
return err
}
dst.SetMapIndex(keyVal, elemVal)
}
return nil
}
func (r *Runtime) mapProto_clear(call FunctionCall) Value {
thisObj := r.toObject(call.This)
mo, ok := thisObj.self.(*mapObject)
if !ok {
panic(r.NewTypeError("Method Map.prototype.clear called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
}
mo.m.clear()
return _undefined
}
func (r *Runtime) mapProto_delete(call FunctionCall) Value {
thisObj := r.toObject(call.This)
mo, ok := thisObj.self.(*mapObject)
if !ok {
panic(r.NewTypeError("Method Map.prototype.delete called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
}
return r.toBoolean(mo.m.remove(call.Argument(0)))
}
func (r *Runtime) mapProto_get(call FunctionCall) Value {
thisObj := r.toObject(call.This)
mo, ok := thisObj.self.(*mapObject)
if !ok {
panic(r.NewTypeError("Method Map.prototype.get called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
}
return nilSafe(mo.m.get(call.Argument(0)))
}
func (r *Runtime) mapProto_has(call FunctionCall) Value {
thisObj := r.toObject(call.This)
mo, ok := thisObj.self.(*mapObject)
if !ok {
panic(r.NewTypeError("Method Map.prototype.has called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
}
if mo.m.has(call.Argument(0)) {
return valueTrue
}
return valueFalse
}
func (r *Runtime) mapProto_set(call FunctionCall) Value {
thisObj := r.toObject(call.This)
mo, ok := thisObj.self.(*mapObject)
if !ok {
panic(r.NewTypeError("Method Map.prototype.set called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
}
mo.m.set(call.Argument(0), call.Argument(1))
return call.This
}
func (r *Runtime) mapProto_entries(call FunctionCall) Value {
return r.createMapIterator(call.This, iterationKindKeyValue)
}
func (r *Runtime) mapProto_forEach(call FunctionCall) Value {
thisObj := r.toObject(call.This)
mo, ok := thisObj.self.(*mapObject)
if !ok {
panic(r.NewTypeError("Method Map.prototype.forEach called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
}
callbackFn, ok := r.toObject(call.Argument(0)).self.assertCallable()
if !ok {
panic(r.NewTypeError("object is not a function %s"))
}
t := call.Argument(1)
iter := mo.m.newIter()
for {
entry := iter.next()
if entry == nil {
break
}
callbackFn(FunctionCall{This: t, Arguments: []Value{entry.value, entry.key, thisObj}})
}
return _undefined
}
func (r *Runtime) mapProto_keys(call FunctionCall) Value {
return r.createMapIterator(call.This, iterationKindKey)
}
func (r *Runtime) mapProto_values(call FunctionCall) Value {
return r.createMapIterator(call.This, iterationKindValue)
}
func (r *Runtime) mapProto_getSize(call FunctionCall) Value {
thisObj := r.toObject(call.This)
mo, ok := thisObj.self.(*mapObject)
if !ok {
panic(r.NewTypeError("Method get Map.prototype.size called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
}
return intToValue(int64(mo.m.size))
}
func (r *Runtime) builtin_newMap(args []Value, newTarget *Object) *Object {
if newTarget == nil {
panic(r.needNew("Map"))
}
proto := r.getPrototypeFromCtor(newTarget, r.global.Map, r.global.MapPrototype)
o := &Object{runtime: r}
mo := &mapObject{}
mo.class = classObject
mo.val = o
mo.extensible = true
o.self = mo
mo.prototype = proto
mo.init()
if len(args) > 0 {
if arg := args[0]; arg != nil && arg != _undefined && arg != _null {
adder := mo.getStr("set", nil)
adderFn := toMethod(adder)
if adderFn == nil {
panic(r.NewTypeError("Map.set in missing"))
}
iter := r.getIterator(arg, nil)
i0 := valueInt(0)
i1 := valueInt(1)
if adder == r.global.mapAdder {
iter.iterate(func(item Value) {
itemObj := r.toObject(item)
k := nilSafe(itemObj.self.getIdx(i0, nil))
v := nilSafe(itemObj.self.getIdx(i1, nil))
mo.m.set(k, v)
})
} else {
iter.iterate(func(item Value) {
itemObj := r.toObject(item)
k := itemObj.self.getIdx(i0, nil)
v := itemObj.self.getIdx(i1, nil)
adderFn(FunctionCall{This: o, Arguments: []Value{k, v}})
})
}
}
}
return o
}
func (r *Runtime) createMapIterator(mapValue Value, kind iterationKind) Value {
obj := r.toObject(mapValue)
mapObj, ok := obj.self.(*mapObject)
if !ok {
panic(r.NewTypeError("Object is not a Map"))
}
o := &Object{runtime: r}
mi := &mapIterObject{
iter: mapObj.m.newIter(),
kind: kind,
}
mi.class = classObject
mi.val = o
mi.extensible = true
o.self = mi
mi.prototype = r.getMapIteratorPrototype()
mi.init()
return o
}
func (r *Runtime) mapIterProto_next(call FunctionCall) Value {
thisObj := r.toObject(call.This)
if iter, ok := thisObj.self.(*mapIterObject); ok {
return iter.next()
}
panic(r.NewTypeError("Method Map Iterator.prototype.next called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
}
func (r *Runtime) createMapProto(val *Object) objectImpl {
o := newBaseObjectObj(val, r.global.ObjectPrototype, classObject)
o._putProp("constructor", r.getMap(), true, false, true)
o._putProp("clear", r.newNativeFunc(r.mapProto_clear, "clear", 0), true, false, true)
r.global.mapAdder = r.newNativeFunc(r.mapProto_set, "set", 2)
o._putProp("set", r.global.mapAdder, true, false, true)
o._putProp("delete", r.newNativeFunc(r.mapProto_delete, "delete", 1), true, false, true)
o._putProp("forEach", r.newNativeFunc(r.mapProto_forEach, "forEach", 1), true, false, true)
o._putProp("has", r.newNativeFunc(r.mapProto_has, "has", 1), true, false, true)
o._putProp("get", r.newNativeFunc(r.mapProto_get, "get", 1), true, false, true)
o.setOwnStr("size", &valueProperty{
getterFunc: r.newNativeFunc(r.mapProto_getSize, "get size", 0),
accessor: true,
writable: true,
configurable: true,
}, true)
o._putProp("keys", r.newNativeFunc(r.mapProto_keys, "keys", 0), true, false, true)
o._putProp("values", r.newNativeFunc(r.mapProto_values, "values", 0), true, false, true)
entriesFunc := r.newNativeFunc(r.mapProto_entries, "entries", 0)
o._putProp("entries", entriesFunc, true, false, true)
o._putSym(SymIterator, valueProp(entriesFunc, true, false, true))
o._putSym(SymToStringTag, valueProp(asciiString(classMap), false, false, true))
return o
}
func (r *Runtime) createMap(val *Object) objectImpl {
o := r.newNativeConstructOnly(val, r.builtin_newMap, r.getMapPrototype(), "Map", 0)
r.putSpeciesReturnThis(o)
return o
}
func (r *Runtime) createMapIterProto(val *Object) objectImpl {
o := newBaseObjectObj(val, r.getIteratorPrototype(), classObject)
o._putProp("next", r.newNativeFunc(r.mapIterProto_next, "next", 0), true, false, true)
o._putSym(SymToStringTag, valueProp(asciiString(classMapIterator), false, false, true))
return o
}
func (r *Runtime) getMapIteratorPrototype() *Object {
var o *Object
if o = r.global.MapIteratorPrototype; o == nil {
o = &Object{runtime: r}
r.global.MapIteratorPrototype = o
o.self = r.createMapIterProto(o)
}
return o
}
func (r *Runtime) getMapPrototype() *Object {
ret := r.global.MapPrototype
if ret == nil {
ret = &Object{runtime: r}
r.global.MapPrototype = ret
ret.self = r.createMapProto(ret)
}
return ret
}
func (r *Runtime) getMap() *Object {
ret := r.global.Map
if ret == nil {
ret = &Object{runtime: r}
r.global.Map = ret
ret.self = r.createMap(ret)
}
return ret
}

358
dop251/goja/builtin_math.go Normal file
View File

@ -0,0 +1,358 @@
package goja
import (
"math"
"math/bits"
"sync"
)
func (r *Runtime) math_abs(call FunctionCall) Value {
return floatToValue(math.Abs(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_acos(call FunctionCall) Value {
return floatToValue(math.Acos(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_acosh(call FunctionCall) Value {
return floatToValue(math.Acosh(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_asin(call FunctionCall) Value {
return floatToValue(math.Asin(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_asinh(call FunctionCall) Value {
return floatToValue(math.Asinh(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_atan(call FunctionCall) Value {
return floatToValue(math.Atan(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_atanh(call FunctionCall) Value {
return floatToValue(math.Atanh(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_atan2(call FunctionCall) Value {
y := call.Argument(0).ToFloat()
x := call.Argument(1).ToFloat()
return floatToValue(math.Atan2(y, x))
}
func (r *Runtime) math_cbrt(call FunctionCall) Value {
return floatToValue(math.Cbrt(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_ceil(call FunctionCall) Value {
return floatToValue(math.Ceil(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_clz32(call FunctionCall) Value {
return intToValue(int64(bits.LeadingZeros32(toUint32(call.Argument(0)))))
}
func (r *Runtime) math_cos(call FunctionCall) Value {
return floatToValue(math.Cos(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_cosh(call FunctionCall) Value {
return floatToValue(math.Cosh(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_exp(call FunctionCall) Value {
return floatToValue(math.Exp(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_expm1(call FunctionCall) Value {
return floatToValue(math.Expm1(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_floor(call FunctionCall) Value {
return floatToValue(math.Floor(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_fround(call FunctionCall) Value {
return floatToValue(float64(float32(call.Argument(0).ToFloat())))
}
func (r *Runtime) math_hypot(call FunctionCall) Value {
var max float64
var hasNaN bool
absValues := make([]float64, 0, len(call.Arguments))
for _, v := range call.Arguments {
arg := nilSafe(v).ToFloat()
if math.IsNaN(arg) {
hasNaN = true
} else {
abs := math.Abs(arg)
if abs > max {
max = abs
}
absValues = append(absValues, abs)
}
}
if math.IsInf(max, 1) {
return _positiveInf
}
if hasNaN {
return _NaN
}
if max == 0 {
return _positiveZero
}
// Kahan summation to avoid rounding errors.
// Normalize the numbers to the largest one to avoid overflow.
var sum, compensation float64
for _, n := range absValues {
n /= max
summand := n*n - compensation
preliminary := sum + summand
compensation = (preliminary - sum) - summand
sum = preliminary
}
return floatToValue(math.Sqrt(sum) * max)
}
func (r *Runtime) math_imul(call FunctionCall) Value {
x := toUint32(call.Argument(0))
y := toUint32(call.Argument(1))
return intToValue(int64(int32(x * y)))
}
func (r *Runtime) math_log(call FunctionCall) Value {
return floatToValue(math.Log(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_log1p(call FunctionCall) Value {
return floatToValue(math.Log1p(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_log10(call FunctionCall) Value {
return floatToValue(math.Log10(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_log2(call FunctionCall) Value {
return floatToValue(math.Log2(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_max(call FunctionCall) Value {
result := math.Inf(-1)
args := call.Arguments
for i, arg := range args {
n := nilSafe(arg).ToFloat()
if math.IsNaN(n) {
args = args[i+1:]
goto NaNLoop
}
result = math.Max(result, n)
}
return floatToValue(result)
NaNLoop:
// All arguments still need to be coerced to number according to the specs.
for _, arg := range args {
nilSafe(arg).ToFloat()
}
return _NaN
}
func (r *Runtime) math_min(call FunctionCall) Value {
result := math.Inf(1)
args := call.Arguments
for i, arg := range args {
n := nilSafe(arg).ToFloat()
if math.IsNaN(n) {
args = args[i+1:]
goto NaNLoop
}
result = math.Min(result, n)
}
return floatToValue(result)
NaNLoop:
// All arguments still need to be coerced to number according to the specs.
for _, arg := range args {
nilSafe(arg).ToFloat()
}
return _NaN
}
func pow(x, y Value) Value {
if x, ok := x.(valueInt); ok {
if y, ok := y.(valueInt); ok && y >= 0 {
if y == 0 {
return intToValue(1)
}
if x == 0 {
return intToValue(0)
}
ip := ipow(int64(x), int64(y))
if ip != 0 {
return intToValue(ip)
}
}
}
xf := x.ToFloat()
yf := y.ToFloat()
if math.Abs(xf) == 1 && math.IsInf(yf, 0) {
return _NaN
}
if xf == 1 && math.IsNaN(yf) {
return _NaN
}
return floatToValue(math.Pow(xf, yf))
}
func (r *Runtime) math_pow(call FunctionCall) Value {
return pow(call.Argument(0), call.Argument(1))
}
func (r *Runtime) math_random(call FunctionCall) Value {
return floatToValue(r.rand())
}
func (r *Runtime) math_round(call FunctionCall) Value {
f := call.Argument(0).ToFloat()
if math.IsNaN(f) {
return _NaN
}
if f == 0 && math.Signbit(f) {
return _negativeZero
}
t := math.Trunc(f)
if f >= 0 {
if f-t >= 0.5 {
return floatToValue(t + 1)
}
} else {
if t-f > 0.5 {
return floatToValue(t - 1)
}
}
return floatToValue(t)
}
func (r *Runtime) math_sign(call FunctionCall) Value {
arg := call.Argument(0)
num := arg.ToFloat()
if math.IsNaN(num) || num == 0 { // this will match -0 too
return arg
}
if num > 0 {
return intToValue(1)
}
return intToValue(-1)
}
func (r *Runtime) math_sin(call FunctionCall) Value {
return floatToValue(math.Sin(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_sinh(call FunctionCall) Value {
return floatToValue(math.Sinh(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_sqrt(call FunctionCall) Value {
return floatToValue(math.Sqrt(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_tan(call FunctionCall) Value {
return floatToValue(math.Tan(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_tanh(call FunctionCall) Value {
return floatToValue(math.Tanh(call.Argument(0).ToFloat()))
}
func (r *Runtime) math_trunc(call FunctionCall) Value {
arg := call.Argument(0)
if i, ok := arg.(valueInt); ok {
return i
}
return floatToValue(math.Trunc(arg.ToFloat()))
}
func createMathTemplate() *objectTemplate {
t := newObjectTemplate()
t.protoFactory = func(r *Runtime) *Object {
return r.global.ObjectPrototype
}
t.putStr("E", func(r *Runtime) Value { return valueProp(valueFloat(math.E), false, false, false) })
t.putStr("LN10", func(r *Runtime) Value { return valueProp(valueFloat(math.Ln10), false, false, false) })
t.putStr("LN2", func(r *Runtime) Value { return valueProp(valueFloat(math.Ln2), false, false, false) })
t.putStr("LOG10E", func(r *Runtime) Value { return valueProp(valueFloat(math.Log10E), false, false, false) })
t.putStr("LOG2E", func(r *Runtime) Value { return valueProp(valueFloat(math.Log2E), false, false, false) })
t.putStr("PI", func(r *Runtime) Value { return valueProp(valueFloat(math.Pi), false, false, false) })
t.putStr("SQRT1_2", func(r *Runtime) Value { return valueProp(valueFloat(sqrt1_2), false, false, false) })
t.putStr("SQRT2", func(r *Runtime) Value { return valueProp(valueFloat(math.Sqrt2), false, false, false) })
t.putSym(SymToStringTag, func(r *Runtime) Value { return valueProp(asciiString(classMath), false, false, true) })
t.putStr("abs", func(r *Runtime) Value { return r.methodProp(r.math_abs, "abs", 1) })
t.putStr("acos", func(r *Runtime) Value { return r.methodProp(r.math_acos, "acos", 1) })
t.putStr("acosh", func(r *Runtime) Value { return r.methodProp(r.math_acosh, "acosh", 1) })
t.putStr("asin", func(r *Runtime) Value { return r.methodProp(r.math_asin, "asin", 1) })
t.putStr("asinh", func(r *Runtime) Value { return r.methodProp(r.math_asinh, "asinh", 1) })
t.putStr("atan", func(r *Runtime) Value { return r.methodProp(r.math_atan, "atan", 1) })
t.putStr("atanh", func(r *Runtime) Value { return r.methodProp(r.math_atanh, "atanh", 1) })
t.putStr("atan2", func(r *Runtime) Value { return r.methodProp(r.math_atan2, "atan2", 2) })
t.putStr("cbrt", func(r *Runtime) Value { return r.methodProp(r.math_cbrt, "cbrt", 1) })
t.putStr("ceil", func(r *Runtime) Value { return r.methodProp(r.math_ceil, "ceil", 1) })
t.putStr("clz32", func(r *Runtime) Value { return r.methodProp(r.math_clz32, "clz32", 1) })
t.putStr("cos", func(r *Runtime) Value { return r.methodProp(r.math_cos, "cos", 1) })
t.putStr("cosh", func(r *Runtime) Value { return r.methodProp(r.math_cosh, "cosh", 1) })
t.putStr("exp", func(r *Runtime) Value { return r.methodProp(r.math_exp, "exp", 1) })
t.putStr("expm1", func(r *Runtime) Value { return r.methodProp(r.math_expm1, "expm1", 1) })
t.putStr("floor", func(r *Runtime) Value { return r.methodProp(r.math_floor, "floor", 1) })
t.putStr("fround", func(r *Runtime) Value { return r.methodProp(r.math_fround, "fround", 1) })
t.putStr("hypot", func(r *Runtime) Value { return r.methodProp(r.math_hypot, "hypot", 2) })
t.putStr("imul", func(r *Runtime) Value { return r.methodProp(r.math_imul, "imul", 2) })
t.putStr("log", func(r *Runtime) Value { return r.methodProp(r.math_log, "log", 1) })
t.putStr("log1p", func(r *Runtime) Value { return r.methodProp(r.math_log1p, "log1p", 1) })
t.putStr("log10", func(r *Runtime) Value { return r.methodProp(r.math_log10, "log10", 1) })
t.putStr("log2", func(r *Runtime) Value { return r.methodProp(r.math_log2, "log2", 1) })
t.putStr("max", func(r *Runtime) Value { return r.methodProp(r.math_max, "max", 2) })
t.putStr("min", func(r *Runtime) Value { return r.methodProp(r.math_min, "min", 2) })
t.putStr("pow", func(r *Runtime) Value { return r.methodProp(r.math_pow, "pow", 2) })
t.putStr("random", func(r *Runtime) Value { return r.methodProp(r.math_random, "random", 0) })
t.putStr("round", func(r *Runtime) Value { return r.methodProp(r.math_round, "round", 1) })
t.putStr("sign", func(r *Runtime) Value { return r.methodProp(r.math_sign, "sign", 1) })
t.putStr("sin", func(r *Runtime) Value { return r.methodProp(r.math_sin, "sin", 1) })
t.putStr("sinh", func(r *Runtime) Value { return r.methodProp(r.math_sinh, "sinh", 1) })
t.putStr("sqrt", func(r *Runtime) Value { return r.methodProp(r.math_sqrt, "sqrt", 1) })
t.putStr("tan", func(r *Runtime) Value { return r.methodProp(r.math_tan, "tan", 1) })
t.putStr("tanh", func(r *Runtime) Value { return r.methodProp(r.math_tanh, "tanh", 1) })
t.putStr("trunc", func(r *Runtime) Value { return r.methodProp(r.math_trunc, "trunc", 1) })
return t
}
var mathTemplate *objectTemplate
var mathTemplateOnce sync.Once
func getMathTemplate() *objectTemplate {
mathTemplateOnce.Do(func() {
mathTemplate = createMathTemplate()
})
return mathTemplate
}
func (r *Runtime) getMath() *Object {
ret := r.global.Math
if ret == nil {
ret = &Object{runtime: r}
r.global.Math = ret
r.newTemplatedObject(getMathTemplate(), ret)
}
return ret
}

View File

@ -0,0 +1,303 @@
package goja
import (
"math"
"sync"
"apigo.cc/gojs/dop251/goja/ftoa"
)
func (r *Runtime) toNumber(v Value) Value {
switch t := v.(type) {
case valueFloat, valueInt:
return v
case *Object:
switch t := t.self.(type) {
case *primitiveValueObject:
return r.toNumber(t.pValue)
case *objectGoReflect:
if t.class == classNumber && t.valueOf != nil {
return t.valueOf()
}
}
if t == r.global.NumberPrototype {
return _positiveZero
}
}
panic(r.NewTypeError("Value is not a number: %s", v))
}
func (r *Runtime) numberproto_valueOf(call FunctionCall) Value {
return r.toNumber(call.This)
}
func (r *Runtime) numberproto_toString(call FunctionCall) Value {
var numVal Value
switch t := call.This.(type) {
case valueFloat, valueInt:
numVal = t
case *Object:
switch t := t.self.(type) {
case *primitiveValueObject:
numVal = r.toNumber(t.pValue)
case *objectGoReflect:
if t.class == classNumber {
if t.toString != nil {
return t.toString()
}
if t.valueOf != nil {
numVal = t.valueOf()
}
}
}
if t == r.global.NumberPrototype {
return asciiString("0")
}
}
if numVal == nil {
panic(r.NewTypeError("Value is not a number"))
}
var radix int
if arg := call.Argument(0); arg != _undefined {
radix = int(arg.ToInteger())
} else {
radix = 10
}
if radix < 2 || radix > 36 {
panic(r.newError(r.getRangeError(), "toString() radix argument must be between 2 and 36"))
}
num := numVal.ToFloat()
if math.IsNaN(num) {
return stringNaN
}
if math.IsInf(num, 1) {
return stringInfinity
}
if math.IsInf(num, -1) {
return stringNegInfinity
}
if radix == 10 {
return asciiString(fToStr(num, ftoa.ModeStandard, 0))
}
return asciiString(ftoa.FToBaseStr(num, radix))
}
func (r *Runtime) numberproto_toFixed(call FunctionCall) Value {
num := r.toNumber(call.This).ToFloat()
prec := call.Argument(0).ToInteger()
if prec < 0 || prec > 100 {
panic(r.newError(r.getRangeError(), "toFixed() precision must be between 0 and 100"))
}
if math.IsNaN(num) {
return stringNaN
}
return asciiString(fToStr(num, ftoa.ModeFixed, int(prec)))
}
func (r *Runtime) numberproto_toExponential(call FunctionCall) Value {
num := r.toNumber(call.This).ToFloat()
precVal := call.Argument(0)
var prec int64
if precVal == _undefined {
return asciiString(fToStr(num, ftoa.ModeStandardExponential, 0))
} else {
prec = precVal.ToInteger()
}
if math.IsNaN(num) {
return stringNaN
}
if math.IsInf(num, 1) {
return stringInfinity
}
if math.IsInf(num, -1) {
return stringNegInfinity
}
if prec < 0 || prec > 100 {
panic(r.newError(r.getRangeError(), "toExponential() precision must be between 0 and 100"))
}
return asciiString(fToStr(num, ftoa.ModeExponential, int(prec+1)))
}
func (r *Runtime) numberproto_toPrecision(call FunctionCall) Value {
numVal := r.toNumber(call.This)
precVal := call.Argument(0)
if precVal == _undefined {
return numVal.toString()
}
num := numVal.ToFloat()
prec := precVal.ToInteger()
if math.IsNaN(num) {
return stringNaN
}
if math.IsInf(num, 1) {
return stringInfinity
}
if math.IsInf(num, -1) {
return stringNegInfinity
}
if prec < 1 || prec > 100 {
panic(r.newError(r.getRangeError(), "toPrecision() precision must be between 1 and 100"))
}
return asciiString(fToStr(num, ftoa.ModePrecision, int(prec)))
}
func (r *Runtime) number_isFinite(call FunctionCall) Value {
switch arg := call.Argument(0).(type) {
case valueInt:
return valueTrue
case valueFloat:
f := float64(arg)
return r.toBoolean(!math.IsInf(f, 0) && !math.IsNaN(f))
default:
return valueFalse
}
}
func (r *Runtime) number_isInteger(call FunctionCall) Value {
switch arg := call.Argument(0).(type) {
case valueInt:
return valueTrue
case valueFloat:
f := float64(arg)
return r.toBoolean(!math.IsNaN(f) && !math.IsInf(f, 0) && math.Floor(f) == f)
default:
return valueFalse
}
}
func (r *Runtime) number_isNaN(call FunctionCall) Value {
if f, ok := call.Argument(0).(valueFloat); ok && math.IsNaN(float64(f)) {
return valueTrue
}
return valueFalse
}
func (r *Runtime) number_isSafeInteger(call FunctionCall) Value {
arg := call.Argument(0)
if i, ok := arg.(valueInt); ok && i >= -(maxInt-1) && i <= maxInt-1 {
return valueTrue
}
if arg == _negativeZero {
return valueTrue
}
return valueFalse
}
func createNumberProtoTemplate() *objectTemplate {
t := newObjectTemplate()
t.protoFactory = func(r *Runtime) *Object {
return r.global.ObjectPrototype
}
t.putStr("constructor", func(r *Runtime) Value { return valueProp(r.getNumber(), true, false, true) })
t.putStr("toExponential", func(r *Runtime) Value { return r.methodProp(r.numberproto_toExponential, "toExponential", 1) })
t.putStr("toFixed", func(r *Runtime) Value { return r.methodProp(r.numberproto_toFixed, "toFixed", 1) })
t.putStr("toLocaleString", func(r *Runtime) Value { return r.methodProp(r.numberproto_toString, "toLocaleString", 0) })
t.putStr("toPrecision", func(r *Runtime) Value { return r.methodProp(r.numberproto_toPrecision, "toPrecision", 1) })
t.putStr("toString", func(r *Runtime) Value { return r.methodProp(r.numberproto_toString, "toString", 1) })
t.putStr("valueOf", func(r *Runtime) Value { return r.methodProp(r.numberproto_valueOf, "valueOf", 0) })
return t
}
var numberProtoTemplate *objectTemplate
var numberProtoTemplateOnce sync.Once
func getNumberProtoTemplate() *objectTemplate {
numberProtoTemplateOnce.Do(func() {
numberProtoTemplate = createNumberProtoTemplate()
})
return numberProtoTemplate
}
func (r *Runtime) getNumberPrototype() *Object {
ret := r.global.NumberPrototype
if ret == nil {
ret = &Object{runtime: r}
r.global.NumberPrototype = ret
o := r.newTemplatedObject(getNumberProtoTemplate(), ret)
o.class = classNumber
}
return ret
}
func (r *Runtime) getParseFloat() *Object {
ret := r.global.parseFloat
if ret == nil {
ret = r.newNativeFunc(r.builtin_parseFloat, "parseFloat", 1)
r.global.parseFloat = ret
}
return ret
}
func (r *Runtime) getParseInt() *Object {
ret := r.global.parseInt
if ret == nil {
ret = r.newNativeFunc(r.builtin_parseInt, "parseInt", 2)
r.global.parseInt = ret
}
return ret
}
func createNumberTemplate() *objectTemplate {
t := newObjectTemplate()
t.protoFactory = func(r *Runtime) *Object {
return r.getFunctionPrototype()
}
t.putStr("length", func(r *Runtime) Value { return valueProp(intToValue(1), false, false, true) })
t.putStr("name", func(r *Runtime) Value { return valueProp(asciiString("Number"), false, false, true) })
t.putStr("prototype", func(r *Runtime) Value { return valueProp(r.getNumberPrototype(), false, false, false) })
t.putStr("EPSILON", func(r *Runtime) Value { return valueProp(_epsilon, false, false, false) })
t.putStr("isFinite", func(r *Runtime) Value { return r.methodProp(r.number_isFinite, "isFinite", 1) })
t.putStr("isInteger", func(r *Runtime) Value { return r.methodProp(r.number_isInteger, "isInteger", 1) })
t.putStr("isNaN", func(r *Runtime) Value { return r.methodProp(r.number_isNaN, "isNaN", 1) })
t.putStr("isSafeInteger", func(r *Runtime) Value { return r.methodProp(r.number_isSafeInteger, "isSafeInteger", 1) })
t.putStr("MAX_SAFE_INTEGER", func(r *Runtime) Value { return valueProp(valueInt(maxInt-1), false, false, false) })
t.putStr("MIN_SAFE_INTEGER", func(r *Runtime) Value { return valueProp(valueInt(-(maxInt - 1)), false, false, false) })
t.putStr("MIN_VALUE", func(r *Runtime) Value { return valueProp(valueFloat(math.SmallestNonzeroFloat64), false, false, false) })
t.putStr("MAX_VALUE", func(r *Runtime) Value { return valueProp(valueFloat(math.MaxFloat64), false, false, false) })
t.putStr("NaN", func(r *Runtime) Value { return valueProp(_NaN, false, false, false) })
t.putStr("NEGATIVE_INFINITY", func(r *Runtime) Value { return valueProp(_negativeInf, false, false, false) })
t.putStr("parseFloat", func(r *Runtime) Value { return valueProp(r.getParseFloat(), true, false, true) })
t.putStr("parseInt", func(r *Runtime) Value { return valueProp(r.getParseInt(), true, false, true) })
t.putStr("POSITIVE_INFINITY", func(r *Runtime) Value { return valueProp(_positiveInf, false, false, false) })
return t
}
var numberTemplate *objectTemplate
var numberTemplateOnce sync.Once
func getNumberTemplate() *objectTemplate {
numberTemplateOnce.Do(func() {
numberTemplate = createNumberTemplate()
})
return numberTemplate
}
func (r *Runtime) getNumber() *Object {
ret := r.global.Number
if ret == nil {
ret = &Object{runtime: r}
r.global.Number = ret
r.newTemplatedFuncObject(getNumberTemplate(), ret, r.builtin_Number,
r.wrapNativeConstruct(r.builtin_newNumber, ret, r.getNumberPrototype()))
}
return ret
}

View File

@ -0,0 +1,711 @@
package goja
import (
"fmt"
"sync"
)
func (r *Runtime) builtin_Object(args []Value, newTarget *Object) *Object {
if newTarget != nil && newTarget != r.getObject() {
proto := r.getPrototypeFromCtor(newTarget, nil, r.global.ObjectPrototype)
return r.newBaseObject(proto, classObject).val
}
if len(args) > 0 {
arg := args[0]
if arg != _undefined && arg != _null {
return arg.ToObject(r)
}
}
return r.NewObject()
}
func (r *Runtime) object_getPrototypeOf(call FunctionCall) Value {
o := call.Argument(0).ToObject(r)
p := o.self.proto()
if p == nil {
return _null
}
return p
}
func (r *Runtime) valuePropToDescriptorObject(desc Value) Value {
if desc == nil {
return _undefined
}
var writable, configurable, enumerable, accessor bool
var get, set *Object
var value Value
if v, ok := desc.(*valueProperty); ok {
writable = v.writable
configurable = v.configurable
enumerable = v.enumerable
accessor = v.accessor
value = v.value
get = v.getterFunc
set = v.setterFunc
} else {
writable = true
configurable = true
enumerable = true
value = desc
}
ret := r.NewObject()
obj := ret.self
if !accessor {
obj.setOwnStr("value", value, false)
obj.setOwnStr("writable", r.toBoolean(writable), false)
} else {
if get != nil {
obj.setOwnStr("get", get, false)
} else {
obj.setOwnStr("get", _undefined, false)
}
if set != nil {
obj.setOwnStr("set", set, false)
} else {
obj.setOwnStr("set", _undefined, false)
}
}
obj.setOwnStr("enumerable", r.toBoolean(enumerable), false)
obj.setOwnStr("configurable", r.toBoolean(configurable), false)
return ret
}
func (r *Runtime) object_getOwnPropertyDescriptor(call FunctionCall) Value {
o := call.Argument(0).ToObject(r)
propName := toPropertyKey(call.Argument(1))
return r.valuePropToDescriptorObject(o.getOwnProp(propName))
}
func (r *Runtime) object_getOwnPropertyDescriptors(call FunctionCall) Value {
o := call.Argument(0).ToObject(r)
result := r.newBaseObject(r.global.ObjectPrototype, classObject).val
for item, next := o.self.iterateKeys()(); next != nil; item, next = next() {
var prop Value
if item.value == nil {
prop = o.getOwnProp(item.name)
if prop == nil {
continue
}
} else {
prop = item.value
}
descriptor := r.valuePropToDescriptorObject(prop)
if descriptor != _undefined {
createDataPropertyOrThrow(result, item.name, descriptor)
}
}
return result
}
func (r *Runtime) object_getOwnPropertyNames(call FunctionCall) Value {
obj := call.Argument(0).ToObject(r)
return r.newArrayValues(obj.self.stringKeys(true, nil))
}
func (r *Runtime) object_getOwnPropertySymbols(call FunctionCall) Value {
obj := call.Argument(0).ToObject(r)
return r.newArrayValues(obj.self.symbols(true, nil))
}
func (r *Runtime) toValueProp(v Value) *valueProperty {
if v == nil || v == _undefined {
return nil
}
obj := r.toObject(v)
getter := obj.self.getStr("get", nil)
setter := obj.self.getStr("set", nil)
writable := obj.self.getStr("writable", nil)
value := obj.self.getStr("value", nil)
if (getter != nil || setter != nil) && (value != nil || writable != nil) {
r.typeErrorResult(true, "Invalid property descriptor. Cannot both specify accessors and a value or writable attribute")
}
ret := &valueProperty{}
if writable != nil && writable.ToBoolean() {
ret.writable = true
}
if e := obj.self.getStr("enumerable", nil); e != nil && e.ToBoolean() {
ret.enumerable = true
}
if c := obj.self.getStr("configurable", nil); c != nil && c.ToBoolean() {
ret.configurable = true
}
ret.value = value
if getter != nil && getter != _undefined {
o := r.toObject(getter)
if _, ok := o.self.assertCallable(); !ok {
r.typeErrorResult(true, "getter must be a function")
}
ret.getterFunc = o
}
if setter != nil && setter != _undefined {
o := r.toObject(setter)
if _, ok := o.self.assertCallable(); !ok {
r.typeErrorResult(true, "setter must be a function")
}
ret.setterFunc = o
}
if ret.getterFunc != nil || ret.setterFunc != nil {
ret.accessor = true
}
return ret
}
func (r *Runtime) toPropertyDescriptor(v Value) (ret PropertyDescriptor) {
if o, ok := v.(*Object); ok {
descr := o.self
// Save the original descriptor for reference
ret.jsDescriptor = o
ret.Value = descr.getStr("value", nil)
if p := descr.getStr("writable", nil); p != nil {
ret.Writable = ToFlag(p.ToBoolean())
}
if p := descr.getStr("enumerable", nil); p != nil {
ret.Enumerable = ToFlag(p.ToBoolean())
}
if p := descr.getStr("configurable", nil); p != nil {
ret.Configurable = ToFlag(p.ToBoolean())
}
ret.Getter = descr.getStr("get", nil)
ret.Setter = descr.getStr("set", nil)
if ret.Getter != nil && ret.Getter != _undefined {
if _, ok := r.toObject(ret.Getter).self.assertCallable(); !ok {
r.typeErrorResult(true, "getter must be a function")
}
}
if ret.Setter != nil && ret.Setter != _undefined {
if _, ok := r.toObject(ret.Setter).self.assertCallable(); !ok {
r.typeErrorResult(true, "setter must be a function")
}
}
if (ret.Getter != nil || ret.Setter != nil) && (ret.Value != nil || ret.Writable != FLAG_NOT_SET) {
r.typeErrorResult(true, "Invalid property descriptor. Cannot both specify accessors and a value or writable attribute")
}
} else {
r.typeErrorResult(true, "Property description must be an object: %s", v.String())
}
return
}
func (r *Runtime) _defineProperties(o *Object, p Value) {
type propItem struct {
name Value
prop PropertyDescriptor
}
props := p.ToObject(r)
var list []propItem
for item, next := iterateEnumerableProperties(props)(); next != nil; item, next = next() {
list = append(list, propItem{
name: item.name,
prop: r.toPropertyDescriptor(item.value),
})
}
for _, prop := range list {
o.defineOwnProperty(prop.name, prop.prop, true)
}
}
func (r *Runtime) object_create(call FunctionCall) Value {
var proto *Object
if arg := call.Argument(0); arg != _null {
if o, ok := arg.(*Object); ok {
proto = o
} else {
r.typeErrorResult(true, "Object prototype may only be an Object or null: %s", arg.String())
}
}
o := r.newBaseObject(proto, classObject).val
if props := call.Argument(1); props != _undefined {
r._defineProperties(o, props)
}
return o
}
func (r *Runtime) object_defineProperty(call FunctionCall) (ret Value) {
if obj, ok := call.Argument(0).(*Object); ok {
descr := r.toPropertyDescriptor(call.Argument(2))
obj.defineOwnProperty(toPropertyKey(call.Argument(1)), descr, true)
ret = call.Argument(0)
} else {
r.typeErrorResult(true, "Object.defineProperty called on non-object")
}
return
}
func (r *Runtime) object_defineProperties(call FunctionCall) Value {
obj := r.toObject(call.Argument(0))
r._defineProperties(obj, call.Argument(1))
return obj
}
func (r *Runtime) object_seal(call FunctionCall) Value {
// ES6
arg := call.Argument(0)
if obj, ok := arg.(*Object); ok {
obj.self.preventExtensions(true)
descr := PropertyDescriptor{
Configurable: FLAG_FALSE,
}
for item, next := obj.self.iterateKeys()(); next != nil; item, next = next() {
if prop, ok := item.value.(*valueProperty); ok {
prop.configurable = false
} else {
obj.defineOwnProperty(item.name, descr, true)
}
}
return obj
}
return arg
}
func (r *Runtime) object_freeze(call FunctionCall) Value {
arg := call.Argument(0)
if obj, ok := arg.(*Object); ok {
obj.self.preventExtensions(true)
for item, next := obj.self.iterateKeys()(); next != nil; item, next = next() {
if prop, ok := item.value.(*valueProperty); ok {
prop.configurable = false
if !prop.accessor {
prop.writable = false
}
} else {
prop := obj.getOwnProp(item.name)
descr := PropertyDescriptor{
Configurable: FLAG_FALSE,
}
if prop, ok := prop.(*valueProperty); ok && prop.accessor {
// no-op
} else {
descr.Writable = FLAG_FALSE
}
obj.defineOwnProperty(item.name, descr, true)
}
}
return obj
} else {
// ES6 behavior
return arg
}
}
func (r *Runtime) object_preventExtensions(call FunctionCall) (ret Value) {
arg := call.Argument(0)
if obj, ok := arg.(*Object); ok {
obj.self.preventExtensions(true)
}
return arg
}
func (r *Runtime) object_isSealed(call FunctionCall) Value {
if obj, ok := call.Argument(0).(*Object); ok {
if obj.self.isExtensible() {
return valueFalse
}
for item, next := obj.self.iterateKeys()(); next != nil; item, next = next() {
var prop Value
if item.value == nil {
prop = obj.getOwnProp(item.name)
if prop == nil {
continue
}
} else {
prop = item.value
}
if prop, ok := prop.(*valueProperty); ok {
if prop.configurable {
return valueFalse
}
} else {
return valueFalse
}
}
}
return valueTrue
}
func (r *Runtime) object_isFrozen(call FunctionCall) Value {
if obj, ok := call.Argument(0).(*Object); ok {
if obj.self.isExtensible() {
return valueFalse
}
for item, next := obj.self.iterateKeys()(); next != nil; item, next = next() {
var prop Value
if item.value == nil {
prop = obj.getOwnProp(item.name)
if prop == nil {
continue
}
} else {
prop = item.value
}
if prop, ok := prop.(*valueProperty); ok {
if prop.configurable || prop.value != nil && prop.writable {
return valueFalse
}
} else {
return valueFalse
}
}
}
return valueTrue
}
func (r *Runtime) object_isExtensible(call FunctionCall) Value {
if obj, ok := call.Argument(0).(*Object); ok {
if obj.self.isExtensible() {
return valueTrue
}
return valueFalse
} else {
// ES6
//r.typeErrorResult(true, "Object.isExtensible called on non-object")
return valueFalse
}
}
func (r *Runtime) object_keys(call FunctionCall) Value {
obj := call.Argument(0).ToObject(r)
return r.newArrayValues(obj.self.stringKeys(false, nil))
}
func (r *Runtime) object_entries(call FunctionCall) Value {
obj := call.Argument(0).ToObject(r)
var values []Value
for item, next := iterateEnumerableStringProperties(obj)(); next != nil; item, next = next() {
values = append(values, r.newArrayValues([]Value{item.name, item.value}))
}
return r.newArrayValues(values)
}
func (r *Runtime) object_values(call FunctionCall) Value {
obj := call.Argument(0).ToObject(r)
var values []Value
for item, next := iterateEnumerableStringProperties(obj)(); next != nil; item, next = next() {
values = append(values, item.value)
}
return r.newArrayValues(values)
}
func (r *Runtime) objectproto_hasOwnProperty(call FunctionCall) Value {
p := toPropertyKey(call.Argument(0))
o := call.This.ToObject(r)
if o.hasOwnProperty(p) {
return valueTrue
} else {
return valueFalse
}
}
func (r *Runtime) objectproto_isPrototypeOf(call FunctionCall) Value {
if v, ok := call.Argument(0).(*Object); ok {
o := call.This.ToObject(r)
for {
v = v.self.proto()
if v == nil {
break
}
if v == o {
return valueTrue
}
}
}
return valueFalse
}
func (r *Runtime) objectproto_propertyIsEnumerable(call FunctionCall) Value {
p := toPropertyKey(call.Argument(0))
o := call.This.ToObject(r)
pv := o.getOwnProp(p)
if pv == nil {
return valueFalse
}
if prop, ok := pv.(*valueProperty); ok {
if !prop.enumerable {
return valueFalse
}
}
return valueTrue
}
func (r *Runtime) objectproto_toString(call FunctionCall) Value {
switch o := call.This.(type) {
case valueNull:
return stringObjectNull
case valueUndefined:
return stringObjectUndefined
default:
obj := o.ToObject(r)
if o, ok := obj.self.(*objectGoReflect); ok {
if toString := o.toString; toString != nil {
return toString()
}
}
var clsName string
if isArray(obj) {
clsName = classArray
} else {
clsName = obj.self.className()
}
if tag := obj.self.getSym(SymToStringTag, nil); tag != nil {
if str, ok := tag.(String); ok {
clsName = str.String()
}
}
return newStringValue(fmt.Sprintf("[object %s]", clsName))
}
}
func (r *Runtime) objectproto_toLocaleString(call FunctionCall) Value {
toString := toMethod(r.getVStr(call.This, "toString"))
return toString(FunctionCall{This: call.This})
}
func (r *Runtime) objectproto_getProto(call FunctionCall) Value {
proto := call.This.ToObject(r).self.proto()
if proto != nil {
return proto
}
return _null
}
func (r *Runtime) setObjectProto(o, arg Value) {
r.checkObjectCoercible(o)
var proto *Object
if arg != _null {
if obj, ok := arg.(*Object); ok {
proto = obj
} else {
return
}
}
if o, ok := o.(*Object); ok {
o.self.setProto(proto, true)
}
}
func (r *Runtime) objectproto_setProto(call FunctionCall) Value {
r.setObjectProto(call.This, call.Argument(0))
return _undefined
}
func (r *Runtime) objectproto_valueOf(call FunctionCall) Value {
return call.This.ToObject(r)
}
func (r *Runtime) object_assign(call FunctionCall) Value {
to := call.Argument(0).ToObject(r)
if len(call.Arguments) > 1 {
for _, arg := range call.Arguments[1:] {
if arg != _undefined && arg != _null {
source := arg.ToObject(r)
for item, next := iterateEnumerableProperties(source)(); next != nil; item, next = next() {
to.setOwn(item.name, item.value, true)
}
}
}
}
return to
}
func (r *Runtime) object_is(call FunctionCall) Value {
return r.toBoolean(call.Argument(0).SameAs(call.Argument(1)))
}
func (r *Runtime) toProto(proto Value) *Object {
if proto != _null {
if obj, ok := proto.(*Object); ok {
return obj
} else {
panic(r.NewTypeError("Object prototype may only be an Object or null: %s", proto))
}
}
return nil
}
func (r *Runtime) object_setPrototypeOf(call FunctionCall) Value {
o := call.Argument(0)
r.checkObjectCoercible(o)
proto := r.toProto(call.Argument(1))
if o, ok := o.(*Object); ok {
o.self.setProto(proto, true)
}
return o
}
func (r *Runtime) object_fromEntries(call FunctionCall) Value {
o := call.Argument(0)
r.checkObjectCoercible(o)
result := r.newBaseObject(r.global.ObjectPrototype, classObject).val
iter := r.getIterator(o, nil)
iter.iterate(func(nextValue Value) {
i0 := valueInt(0)
i1 := valueInt(1)
itemObj := r.toObject(nextValue)
k := itemObj.self.getIdx(i0, nil)
v := itemObj.self.getIdx(i1, nil)
key := toPropertyKey(k)
createDataPropertyOrThrow(result, key, v)
})
return result
}
func (r *Runtime) object_hasOwn(call FunctionCall) Value {
o := call.Argument(0)
obj := o.ToObject(r)
p := toPropertyKey(call.Argument(1))
if obj.hasOwnProperty(p) {
return valueTrue
} else {
return valueFalse
}
}
func createObjectTemplate() *objectTemplate {
t := newObjectTemplate()
t.protoFactory = func(r *Runtime) *Object {
return r.getFunctionPrototype()
}
t.putStr("length", func(r *Runtime) Value { return valueProp(intToValue(1), false, false, true) })
t.putStr("name", func(r *Runtime) Value { return valueProp(asciiString("Object"), false, false, true) })
t.putStr("prototype", func(r *Runtime) Value { return valueProp(r.global.ObjectPrototype, false, false, false) })
t.putStr("assign", func(r *Runtime) Value { return r.methodProp(r.object_assign, "assign", 2) })
t.putStr("defineProperty", func(r *Runtime) Value { return r.methodProp(r.object_defineProperty, "defineProperty", 3) })
t.putStr("defineProperties", func(r *Runtime) Value { return r.methodProp(r.object_defineProperties, "defineProperties", 2) })
t.putStr("entries", func(r *Runtime) Value { return r.methodProp(r.object_entries, "entries", 1) })
t.putStr("getOwnPropertyDescriptor", func(r *Runtime) Value {
return r.methodProp(r.object_getOwnPropertyDescriptor, "getOwnPropertyDescriptor", 2)
})
t.putStr("getOwnPropertyDescriptors", func(r *Runtime) Value {
return r.methodProp(r.object_getOwnPropertyDescriptors, "getOwnPropertyDescriptors", 1)
})
t.putStr("getPrototypeOf", func(r *Runtime) Value { return r.methodProp(r.object_getPrototypeOf, "getPrototypeOf", 1) })
t.putStr("is", func(r *Runtime) Value { return r.methodProp(r.object_is, "is", 2) })
t.putStr("getOwnPropertyNames", func(r *Runtime) Value { return r.methodProp(r.object_getOwnPropertyNames, "getOwnPropertyNames", 1) })
t.putStr("getOwnPropertySymbols", func(r *Runtime) Value {
return r.methodProp(r.object_getOwnPropertySymbols, "getOwnPropertySymbols", 1)
})
t.putStr("create", func(r *Runtime) Value { return r.methodProp(r.object_create, "create", 2) })
t.putStr("seal", func(r *Runtime) Value { return r.methodProp(r.object_seal, "seal", 1) })
t.putStr("freeze", func(r *Runtime) Value { return r.methodProp(r.object_freeze, "freeze", 1) })
t.putStr("preventExtensions", func(r *Runtime) Value { return r.methodProp(r.object_preventExtensions, "preventExtensions", 1) })
t.putStr("isSealed", func(r *Runtime) Value { return r.methodProp(r.object_isSealed, "isSealed", 1) })
t.putStr("isFrozen", func(r *Runtime) Value { return r.methodProp(r.object_isFrozen, "isFrozen", 1) })
t.putStr("isExtensible", func(r *Runtime) Value { return r.methodProp(r.object_isExtensible, "isExtensible", 1) })
t.putStr("keys", func(r *Runtime) Value { return r.methodProp(r.object_keys, "keys", 1) })
t.putStr("setPrototypeOf", func(r *Runtime) Value { return r.methodProp(r.object_setPrototypeOf, "setPrototypeOf", 2) })
t.putStr("values", func(r *Runtime) Value { return r.methodProp(r.object_values, "values", 1) })
t.putStr("fromEntries", func(r *Runtime) Value { return r.methodProp(r.object_fromEntries, "fromEntries", 1) })
t.putStr("hasOwn", func(r *Runtime) Value { return r.methodProp(r.object_hasOwn, "hasOwn", 2) })
return t
}
var _objectTemplate *objectTemplate
var objectTemplateOnce sync.Once
func getObjectTemplate() *objectTemplate {
objectTemplateOnce.Do(func() {
_objectTemplate = createObjectTemplate()
})
return _objectTemplate
}
func (r *Runtime) getObject() *Object {
ret := r.global.Object
if ret == nil {
ret = &Object{runtime: r}
r.global.Object = ret
r.newTemplatedFuncObject(getObjectTemplate(), ret, func(call FunctionCall) Value {
return r.builtin_Object(call.Arguments, nil)
}, r.builtin_Object)
}
return ret
}
/*
func (r *Runtime) getObjectPrototype() *Object {
ret := r.global.ObjectPrototype
if ret == nil {
ret = &Object{runtime: r}
r.global.ObjectPrototype = ret
r.newTemplatedObject(getObjectProtoTemplate(), ret)
}
return ret
}
*/
var objectProtoTemplate *objectTemplate
var objectProtoTemplateOnce sync.Once
func getObjectProtoTemplate() *objectTemplate {
objectProtoTemplateOnce.Do(func() {
objectProtoTemplate = createObjectProtoTemplate()
})
return objectProtoTemplate
}
func createObjectProtoTemplate() *objectTemplate {
t := newObjectTemplate()
// null prototype
t.putStr("constructor", func(r *Runtime) Value { return valueProp(r.getObject(), true, false, true) })
t.putStr("toString", func(r *Runtime) Value { return r.methodProp(r.objectproto_toString, "toString", 0) })
t.putStr("toLocaleString", func(r *Runtime) Value { return r.methodProp(r.objectproto_toLocaleString, "toLocaleString", 0) })
t.putStr("valueOf", func(r *Runtime) Value { return r.methodProp(r.objectproto_valueOf, "valueOf", 0) })
t.putStr("hasOwnProperty", func(r *Runtime) Value { return r.methodProp(r.objectproto_hasOwnProperty, "hasOwnProperty", 1) })
t.putStr("isPrototypeOf", func(r *Runtime) Value { return r.methodProp(r.objectproto_isPrototypeOf, "isPrototypeOf", 1) })
t.putStr("propertyIsEnumerable", func(r *Runtime) Value {
return r.methodProp(r.objectproto_propertyIsEnumerable, "propertyIsEnumerable", 1)
})
t.putStr(__proto__, func(r *Runtime) Value {
return &valueProperty{
accessor: true,
getterFunc: r.newNativeFunc(r.objectproto_getProto, "get __proto__", 0),
setterFunc: r.newNativeFunc(r.objectproto_setProto, "set __proto__", 1),
configurable: true,
}
})
return t
}

View File

@ -0,0 +1,647 @@
package goja
import (
"reflect"
"apigo.cc/gojs/dop251/goja/unistring"
)
type PromiseState int
type PromiseRejectionOperation int
type promiseReactionType int
const (
PromiseStatePending PromiseState = iota
PromiseStateFulfilled
PromiseStateRejected
)
const (
PromiseRejectionReject PromiseRejectionOperation = iota
PromiseRejectionHandle
)
const (
promiseReactionFulfill promiseReactionType = iota
promiseReactionReject
)
type PromiseRejectionTracker func(p *Promise, operation PromiseRejectionOperation)
type jobCallback struct {
callback func(FunctionCall) Value
}
type promiseCapability struct {
promise *Object
resolveObj, rejectObj *Object
}
type promiseReaction struct {
capability *promiseCapability
typ promiseReactionType
handler *jobCallback
asyncRunner *asyncRunner
asyncCtx interface{}
}
var typePromise = reflect.TypeOf((*Promise)(nil))
// Promise is a Go wrapper around ECMAScript Promise. Calling Runtime.ToValue() on it
// returns the underlying Object. Calling Export() on a Promise Object returns a Promise.
//
// Use Runtime.NewPromise() to create one. Calling Runtime.ToValue() on a zero object or nil returns null Value.
//
// WARNING: Instances of Promise are not goroutine-safe. See Runtime.NewPromise() for more details.
type Promise struct {
baseObject
state PromiseState
result Value
fulfillReactions []*promiseReaction
rejectReactions []*promiseReaction
handled bool
}
func (p *Promise) State() PromiseState {
return p.state
}
func (p *Promise) Result() Value {
return p.result
}
func (p *Promise) toValue(r *Runtime) Value {
if p == nil || p.val == nil {
return _null
}
promise := p.val
if promise.runtime != r {
panic(r.NewTypeError("Illegal runtime transition of a Promise"))
}
return promise
}
func (p *Promise) createResolvingFunctions() (resolve, reject *Object) {
r := p.val.runtime
alreadyResolved := false
return p.val.runtime.newNativeFunc(func(call FunctionCall) Value {
if alreadyResolved {
return _undefined
}
alreadyResolved = true
resolution := call.Argument(0)
if resolution.SameAs(p.val) {
return p.reject(r.NewTypeError("Promise self-resolution"))
}
if obj, ok := resolution.(*Object); ok {
var thenAction Value
ex := r.vm.try(func() {
thenAction = obj.self.getStr("then", nil)
})
if ex != nil {
return p.reject(ex.val)
}
if call, ok := assertCallable(thenAction); ok {
job := r.newPromiseResolveThenableJob(p, resolution, &jobCallback{callback: call})
r.enqueuePromiseJob(job)
return _undefined
}
}
return p.fulfill(resolution)
}, "", 1),
p.val.runtime.newNativeFunc(func(call FunctionCall) Value {
if alreadyResolved {
return _undefined
}
alreadyResolved = true
reason := call.Argument(0)
return p.reject(reason)
}, "", 1)
}
func (p *Promise) reject(reason Value) Value {
reactions := p.rejectReactions
p.result = reason
p.fulfillReactions, p.rejectReactions = nil, nil
p.state = PromiseStateRejected
r := p.val.runtime
if !p.handled {
r.trackPromiseRejection(p, PromiseRejectionReject)
}
r.triggerPromiseReactions(reactions, reason)
return _undefined
}
func (p *Promise) fulfill(value Value) Value {
reactions := p.fulfillReactions
p.result = value
p.fulfillReactions, p.rejectReactions = nil, nil
p.state = PromiseStateFulfilled
p.val.runtime.triggerPromiseReactions(reactions, value)
return _undefined
}
func (p *Promise) exportType() reflect.Type {
return typePromise
}
func (p *Promise) export(*objectExportCtx) interface{} {
return p
}
func (p *Promise) addReactions(fulfillReaction *promiseReaction, rejectReaction *promiseReaction) {
r := p.val.runtime
if tracker := r.asyncContextTracker; tracker != nil {
ctx := tracker.Grab()
fulfillReaction.asyncCtx = ctx
rejectReaction.asyncCtx = ctx
}
switch p.state {
case PromiseStatePending:
p.fulfillReactions = append(p.fulfillReactions, fulfillReaction)
p.rejectReactions = append(p.rejectReactions, rejectReaction)
case PromiseStateFulfilled:
r.enqueuePromiseJob(r.newPromiseReactionJob(fulfillReaction, p.result))
default:
reason := p.result
if !p.handled {
r.trackPromiseRejection(p, PromiseRejectionHandle)
}
r.enqueuePromiseJob(r.newPromiseReactionJob(rejectReaction, reason))
}
p.handled = true
}
func (r *Runtime) newPromiseResolveThenableJob(p *Promise, thenable Value, then *jobCallback) func() {
return func() {
resolve, reject := p.createResolvingFunctions()
ex := r.vm.try(func() {
r.callJobCallback(then, thenable, resolve, reject)
})
if ex != nil {
if fn, ok := reject.self.assertCallable(); ok {
fn(FunctionCall{Arguments: []Value{ex.val}})
}
}
}
}
func (r *Runtime) enqueuePromiseJob(job func()) {
r.jobQueue = append(r.jobQueue, job)
}
func (r *Runtime) triggerPromiseReactions(reactions []*promiseReaction, argument Value) {
for _, reaction := range reactions {
r.enqueuePromiseJob(r.newPromiseReactionJob(reaction, argument))
}
}
func (r *Runtime) newPromiseReactionJob(reaction *promiseReaction, argument Value) func() {
return func() {
var handlerResult Value
fulfill := false
if reaction.handler == nil {
handlerResult = argument
if reaction.typ == promiseReactionFulfill {
fulfill = true
}
} else {
if tracker := r.asyncContextTracker; tracker != nil {
tracker.Resumed(reaction.asyncCtx)
}
ex := r.vm.try(func() {
handlerResult = r.callJobCallback(reaction.handler, _undefined, argument)
fulfill = true
})
if ex != nil {
handlerResult = ex.val
}
if tracker := r.asyncContextTracker; tracker != nil {
tracker.Exited()
}
}
if reaction.capability != nil {
if fulfill {
reaction.capability.resolve(handlerResult)
} else {
reaction.capability.reject(handlerResult)
}
}
}
}
func (r *Runtime) newPromise(proto *Object) *Promise {
o := &Object{runtime: r}
po := &Promise{}
po.class = classObject
po.val = o
po.extensible = true
o.self = po
po.prototype = proto
po.init()
return po
}
func (r *Runtime) builtin_newPromise(args []Value, newTarget *Object) *Object {
if newTarget == nil {
panic(r.needNew("Promise"))
}
var arg0 Value
if len(args) > 0 {
arg0 = args[0]
}
executor := r.toCallable(arg0)
proto := r.getPrototypeFromCtor(newTarget, r.global.Promise, r.getPromisePrototype())
po := r.newPromise(proto)
resolve, reject := po.createResolvingFunctions()
ex := r.vm.try(func() {
executor(FunctionCall{Arguments: []Value{resolve, reject}})
})
if ex != nil {
if fn, ok := reject.self.assertCallable(); ok {
fn(FunctionCall{Arguments: []Value{ex.val}})
}
}
return po.val
}
func (r *Runtime) promiseProto_then(call FunctionCall) Value {
thisObj := r.toObject(call.This)
if p, ok := thisObj.self.(*Promise); ok {
c := r.speciesConstructorObj(thisObj, r.getPromise())
resultCapability := r.newPromiseCapability(c)
return r.performPromiseThen(p, call.Argument(0), call.Argument(1), resultCapability)
}
panic(r.NewTypeError("Method Promise.prototype.then called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
}
func (r *Runtime) newPromiseCapability(c *Object) *promiseCapability {
pcap := new(promiseCapability)
if c == r.getPromise() {
p := r.newPromise(r.getPromisePrototype())
pcap.resolveObj, pcap.rejectObj = p.createResolvingFunctions()
pcap.promise = p.val
} else {
var resolve, reject Value
executor := r.newNativeFunc(func(call FunctionCall) Value {
if resolve != nil {
panic(r.NewTypeError("resolve is already set"))
}
if reject != nil {
panic(r.NewTypeError("reject is already set"))
}
if arg := call.Argument(0); arg != _undefined {
resolve = arg
}
if arg := call.Argument(1); arg != _undefined {
reject = arg
}
return nil
}, "", 2)
pcap.promise = r.toConstructor(c)([]Value{executor}, c)
pcap.resolveObj = r.toObject(resolve)
r.toCallable(pcap.resolveObj) // make sure it's callable
pcap.rejectObj = r.toObject(reject)
r.toCallable(pcap.rejectObj)
}
return pcap
}
func (r *Runtime) performPromiseThen(p *Promise, onFulfilled, onRejected Value, resultCapability *promiseCapability) Value {
var onFulfilledJobCallback, onRejectedJobCallback *jobCallback
if f, ok := assertCallable(onFulfilled); ok {
onFulfilledJobCallback = &jobCallback{callback: f}
}
if f, ok := assertCallable(onRejected); ok {
onRejectedJobCallback = &jobCallback{callback: f}
}
fulfillReaction := &promiseReaction{
capability: resultCapability,
typ: promiseReactionFulfill,
handler: onFulfilledJobCallback,
}
rejectReaction := &promiseReaction{
capability: resultCapability,
typ: promiseReactionReject,
handler: onRejectedJobCallback,
}
p.addReactions(fulfillReaction, rejectReaction)
if resultCapability == nil {
return _undefined
}
return resultCapability.promise
}
func (r *Runtime) promiseProto_catch(call FunctionCall) Value {
return r.invoke(call.This, "then", _undefined, call.Argument(0))
}
func (r *Runtime) promiseResolve(c *Object, x Value) *Object {
if obj, ok := x.(*Object); ok {
xConstructor := nilSafe(obj.self.getStr("constructor", nil))
if xConstructor.SameAs(c) {
return obj
}
}
pcap := r.newPromiseCapability(c)
pcap.resolve(x)
return pcap.promise
}
func (r *Runtime) promiseProto_finally(call FunctionCall) Value {
promise := r.toObject(call.This)
c := r.speciesConstructorObj(promise, r.getPromise())
onFinally := call.Argument(0)
var thenFinally, catchFinally Value
if onFinallyFn, ok := assertCallable(onFinally); !ok {
thenFinally, catchFinally = onFinally, onFinally
} else {
thenFinally = r.newNativeFunc(func(call FunctionCall) Value {
value := call.Argument(0)
result := onFinallyFn(FunctionCall{})
promise := r.promiseResolve(c, result)
valueThunk := r.newNativeFunc(func(call FunctionCall) Value {
return value
}, "", 0)
return r.invoke(promise, "then", valueThunk)
}, "", 1)
catchFinally = r.newNativeFunc(func(call FunctionCall) Value {
reason := call.Argument(0)
result := onFinallyFn(FunctionCall{})
promise := r.promiseResolve(c, result)
thrower := r.newNativeFunc(func(call FunctionCall) Value {
panic(reason)
}, "", 0)
return r.invoke(promise, "then", thrower)
}, "", 1)
}
return r.invoke(promise, "then", thenFinally, catchFinally)
}
func (pcap *promiseCapability) resolve(result Value) {
pcap.promise.runtime.toCallable(pcap.resolveObj)(FunctionCall{Arguments: []Value{result}})
}
func (pcap *promiseCapability) reject(reason Value) {
pcap.promise.runtime.toCallable(pcap.rejectObj)(FunctionCall{Arguments: []Value{reason}})
}
func (pcap *promiseCapability) try(f func()) bool {
ex := pcap.promise.runtime.vm.try(f)
if ex != nil {
pcap.reject(ex.val)
return false
}
return true
}
func (r *Runtime) promise_all(call FunctionCall) Value {
c := r.toObject(call.This)
pcap := r.newPromiseCapability(c)
pcap.try(func() {
promiseResolve := r.toCallable(c.self.getStr("resolve", nil))
iter := r.getIterator(call.Argument(0), nil)
var values []Value
remainingElementsCount := 1
iter.iterate(func(nextValue Value) {
index := len(values)
values = append(values, _undefined)
nextPromise := promiseResolve(FunctionCall{This: c, Arguments: []Value{nextValue}})
alreadyCalled := false
onFulfilled := r.newNativeFunc(func(call FunctionCall) Value {
if alreadyCalled {
return _undefined
}
alreadyCalled = true
values[index] = call.Argument(0)
remainingElementsCount--
if remainingElementsCount == 0 {
pcap.resolve(r.newArrayValues(values))
}
return _undefined
}, "", 1)
remainingElementsCount++
r.invoke(nextPromise, "then", onFulfilled, pcap.rejectObj)
})
remainingElementsCount--
if remainingElementsCount == 0 {
pcap.resolve(r.newArrayValues(values))
}
})
return pcap.promise
}
func (r *Runtime) promise_allSettled(call FunctionCall) Value {
c := r.toObject(call.This)
pcap := r.newPromiseCapability(c)
pcap.try(func() {
promiseResolve := r.toCallable(c.self.getStr("resolve", nil))
iter := r.getIterator(call.Argument(0), nil)
var values []Value
remainingElementsCount := 1
iter.iterate(func(nextValue Value) {
index := len(values)
values = append(values, _undefined)
nextPromise := promiseResolve(FunctionCall{This: c, Arguments: []Value{nextValue}})
alreadyCalled := false
reaction := func(status Value, valueKey unistring.String) *Object {
return r.newNativeFunc(func(call FunctionCall) Value {
if alreadyCalled {
return _undefined
}
alreadyCalled = true
obj := r.NewObject()
obj.self._putProp("status", status, true, true, true)
obj.self._putProp(valueKey, call.Argument(0), true, true, true)
values[index] = obj
remainingElementsCount--
if remainingElementsCount == 0 {
pcap.resolve(r.newArrayValues(values))
}
return _undefined
}, "", 1)
}
onFulfilled := reaction(asciiString("fulfilled"), "value")
onRejected := reaction(asciiString("rejected"), "reason")
remainingElementsCount++
r.invoke(nextPromise, "then", onFulfilled, onRejected)
})
remainingElementsCount--
if remainingElementsCount == 0 {
pcap.resolve(r.newArrayValues(values))
}
})
return pcap.promise
}
func (r *Runtime) promise_any(call FunctionCall) Value {
c := r.toObject(call.This)
pcap := r.newPromiseCapability(c)
pcap.try(func() {
promiseResolve := r.toCallable(c.self.getStr("resolve", nil))
iter := r.getIterator(call.Argument(0), nil)
var errors []Value
remainingElementsCount := 1
iter.iterate(func(nextValue Value) {
index := len(errors)
errors = append(errors, _undefined)
nextPromise := promiseResolve(FunctionCall{This: c, Arguments: []Value{nextValue}})
alreadyCalled := false
onRejected := r.newNativeFunc(func(call FunctionCall) Value {
if alreadyCalled {
return _undefined
}
alreadyCalled = true
errors[index] = call.Argument(0)
remainingElementsCount--
if remainingElementsCount == 0 {
_error := r.builtin_new(r.getAggregateError(), nil)
_error.self._putProp("errors", r.newArrayValues(errors), true, false, true)
pcap.reject(_error)
}
return _undefined
}, "", 1)
remainingElementsCount++
r.invoke(nextPromise, "then", pcap.resolveObj, onRejected)
})
remainingElementsCount--
if remainingElementsCount == 0 {
_error := r.builtin_new(r.getAggregateError(), nil)
_error.self._putProp("errors", r.newArrayValues(errors), true, false, true)
pcap.reject(_error)
}
})
return pcap.promise
}
func (r *Runtime) promise_race(call FunctionCall) Value {
c := r.toObject(call.This)
pcap := r.newPromiseCapability(c)
pcap.try(func() {
promiseResolve := r.toCallable(c.self.getStr("resolve", nil))
iter := r.getIterator(call.Argument(0), nil)
iter.iterate(func(nextValue Value) {
nextPromise := promiseResolve(FunctionCall{This: c, Arguments: []Value{nextValue}})
r.invoke(nextPromise, "then", pcap.resolveObj, pcap.rejectObj)
})
})
return pcap.promise
}
func (r *Runtime) promise_reject(call FunctionCall) Value {
pcap := r.newPromiseCapability(r.toObject(call.This))
pcap.reject(call.Argument(0))
return pcap.promise
}
func (r *Runtime) promise_resolve(call FunctionCall) Value {
return r.promiseResolve(r.toObject(call.This), call.Argument(0))
}
func (r *Runtime) createPromiseProto(val *Object) objectImpl {
o := newBaseObjectObj(val, r.global.ObjectPrototype, classObject)
o._putProp("constructor", r.getPromise(), true, false, true)
o._putProp("catch", r.newNativeFunc(r.promiseProto_catch, "catch", 1), true, false, true)
o._putProp("finally", r.newNativeFunc(r.promiseProto_finally, "finally", 1), true, false, true)
o._putProp("then", r.newNativeFunc(r.promiseProto_then, "then", 2), true, false, true)
o._putSym(SymToStringTag, valueProp(asciiString(classPromise), false, false, true))
return o
}
func (r *Runtime) createPromise(val *Object) objectImpl {
o := r.newNativeConstructOnly(val, r.builtin_newPromise, r.getPromisePrototype(), "Promise", 1)
o._putProp("all", r.newNativeFunc(r.promise_all, "all", 1), true, false, true)
o._putProp("allSettled", r.newNativeFunc(r.promise_allSettled, "allSettled", 1), true, false, true)
o._putProp("any", r.newNativeFunc(r.promise_any, "any", 1), true, false, true)
o._putProp("race", r.newNativeFunc(r.promise_race, "race", 1), true, false, true)
o._putProp("reject", r.newNativeFunc(r.promise_reject, "reject", 1), true, false, true)
o._putProp("resolve", r.newNativeFunc(r.promise_resolve, "resolve", 1), true, false, true)
r.putSpeciesReturnThis(o)
return o
}
func (r *Runtime) getPromisePrototype() *Object {
ret := r.global.PromisePrototype
if ret == nil {
ret = &Object{runtime: r}
r.global.PromisePrototype = ret
ret.self = r.createPromiseProto(ret)
}
return ret
}
func (r *Runtime) getPromise() *Object {
ret := r.global.Promise
if ret == nil {
ret = &Object{runtime: r}
r.global.Promise = ret
ret.self = r.createPromise(ret)
}
return ret
}
func (r *Runtime) wrapPromiseReaction(fObj *Object) func(interface{}) {
f, _ := AssertFunction(fObj)
return func(x interface{}) {
_, _ = f(nil, r.ToValue(x))
}
}
// NewPromise creates and returns a Promise and resolving functions for it.
//
// WARNING: The returned values are not goroutine-safe and must not be called in parallel with VM running.
// In order to make use of this method you need an event loop such as the one in goja_nodejs (https://apigo.cc/gojs/dop251/goja_nodejs)
// where it can be used like this:
//
// loop := NewEventLoop()
// loop.Start()
// defer loop.Stop()
// loop.RunOnLoop(func(vm *goja.Runtime) {
// p, resolve, _ := vm.NewPromise()
// vm.Set("p", p)
// go func() {
// time.Sleep(500 * time.Millisecond) // or perform any other blocking operation
// loop.RunOnLoop(func(*goja.Runtime) { // resolve() must be called on the loop, cannot call it here
// resolve(result)
// })
// }()
// }
func (r *Runtime) NewPromise() (promise *Promise, resolve func(result interface{}), reject func(reason interface{})) {
p := r.newPromise(r.getPromisePrototype())
resolveF, rejectF := p.createResolvingFunctions()
return p, r.wrapPromiseReaction(resolveF), r.wrapPromiseReaction(rejectF)
}
// SetPromiseRejectionTracker registers a function that will be called in two scenarios: when a promise is rejected
// without any handlers (with operation argument set to PromiseRejectionReject), and when a handler is added to a
// rejected promise for the first time (with operation argument set to PromiseRejectionHandle).
//
// Setting a tracker replaces any existing one. Setting it to nil disables the functionality.
//
// See https://tc39.es/ecma262/#sec-host-promise-rejection-tracker for more details.
func (r *Runtime) SetPromiseRejectionTracker(tracker PromiseRejectionTracker) {
r.promiseRejectionTracker = tracker
}
// SetAsyncContextTracker registers a handler that allows to track async execution contexts. See AsyncContextTracker
// documentation for more details. Setting it to nil disables the functionality.
// This method (as Runtime in general) is not goroutine-safe.
func (r *Runtime) SetAsyncContextTracker(tracker AsyncContextTracker) {
r.asyncContextTracker = tracker
}

View File

@ -0,0 +1,396 @@
package goja
import (
"apigo.cc/gojs/dop251/goja/unistring"
)
type nativeProxyHandler struct {
handler *ProxyTrapConfig
}
func (h *nativeProxyHandler) getPrototypeOf(target *Object) (Value, bool) {
if trap := h.handler.GetPrototypeOf; trap != nil {
return trap(target), true
}
return nil, false
}
func (h *nativeProxyHandler) setPrototypeOf(target *Object, proto *Object) (bool, bool) {
if trap := h.handler.SetPrototypeOf; trap != nil {
return trap(target, proto), true
}
return false, false<