first
This commit is contained in:
commit
702ea0fbe9
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.*
|
||||
!.gitignore
|
||||
go.sum
|
||||
9
LICENSE
Normal file
9
LICENSE
Normal 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
82
README.md
Normal 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
344
args.go
Normal 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
51
common.go
Normal 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
|
||||
}
|
||||
22
dop251/base64dec/decode_map.go
Normal file
22
dop251/base64dec/decode_map.go
Normal 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
223
dop251/base64dec/decoder.go
Normal 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
|
||||
}
|
||||
58
dop251/base64dec/generate_decode_map.go
Normal file
58
dop251/base64dec/generate_decode_map.go
Normal 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
565
dop251/goja/array.go
Normal 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
500
dop251/goja/array_sparse.go
Normal 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
876
dop251/goja/ast/node.go
Normal 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
1794
dop251/goja/builtin_array.go
Normal file
File diff suppressed because it is too large
Load Diff
369
dop251/goja/builtin_bigint.go
Normal file
369
dop251/goja/builtin_bigint.go
Normal 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
|
||||
}
|
||||
75
dop251/goja/builtin_boolean.go
Normal file
75
dop251/goja/builtin_boolean.go
Normal 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
1058
dop251/goja/builtin_date.go
Normal file
File diff suppressed because it is too large
Load Diff
314
dop251/goja/builtin_error.go
Normal file
314
dop251/goja/builtin_error.go
Normal 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
|
||||
}
|
||||
416
dop251/goja/builtin_function.go
Normal file
416
dop251/goja/builtin_function.go
Normal 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
|
||||
}
|
||||
576
dop251/goja/builtin_global.go
Normal file
576
dop251/goja/builtin_global.go
Normal 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
542
dop251/goja/builtin_json.go
Normal 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
342
dop251/goja/builtin_map.go
Normal 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
358
dop251/goja/builtin_math.go
Normal 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
|
||||
}
|
||||
303
dop251/goja/builtin_number.go
Normal file
303
dop251/goja/builtin_number.go
Normal 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
|
||||
}
|
||||
711
dop251/goja/builtin_object.go
Normal file
711
dop251/goja/builtin_object.go
Normal 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
|
||||
}
|
||||
647
dop251/goja/builtin_promise.go
Normal file
647
dop251/goja/builtin_promise.go
Normal 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
|
||||
}
|
||||
396
dop251/goja/builtin_proxy.go
Normal file
396
dop251/goja/builtin_proxy.go
Normal 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< | ||||