This commit is contained in:
Star 2024-07-03 15:38:51 +08:00
commit 531529a909
36 changed files with 11079 additions and 0 deletions

17
.gitignore vendored Normal file
View File

@ -0,0 +1,17 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
.vscode/

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "deps/quickjs"]
path = deps/quickjs
url = https://github.com/bellard/quickjs.git

24
LICENSE Normal file
View File

@ -0,0 +1,24 @@
MIT License
Copyright (c) 2022 Brian Wang <wangbuke@gmail.com>
Copyright (c) 2020 Kenta Iwasaki <kenta@lithdew.net>
Copyright (c) 2017-2020 Fabrice Bellard
Copyright (c) 2017-2020 Charlie Gordon
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.

349
README.md Normal file
View File

@ -0,0 +1,349 @@
# quickjs-go
English | [简体中文](README_zh-cn.md)
[![Test](https://github.com/buke/quickjs-go/workflows/Test/badge.svg)](https://github.com/buke/quickjs-go/actions?query=workflow%3ATest)
[![codecov](https://codecov.io/gh/buke/quickjs-go/branch/main/graph/badge.svg?token=DW5RGD01AG)](https://codecov.io/gh/buke/quickjs-go)
[![Go Report Card](https://goreportcard.com/badge/github.com/buke/quickjs-go)](https://goreportcard.com/report/github.com/buke/quickjs-go)
[![GoDoc](https://pkg.go.dev/badge/github.com/buke/quickjs-go?status.svg)](https://pkg.go.dev/github.com/buke/quickjs-go?tab=doc)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fbuke%2Fquickjs-go.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fbuke%2Fquickjs-go?ref=badge_shield)
Go bindings to QuickJS: a fast, small, and embeddable ES2020 JavaScript interpreter.
## Platform Support
we prebuilt quickjs static library for the following platforms:
| Platform | Arch | Static Library |
| --------- | ---- | -------------- |
| Linux | x64 | [libquickjs.a](deps/libs/linux_amd64/libquickjs.a) |
| Linux | arm64| [libquickjs.a](deps/libs/linux_arm64/libquickjs.a) |
| Windows | x64 | [libquickjs.a](deps/libs/windows_amd64/libquickjs.a) |
| Windows | x86 | [libquickjs.a](deps/libs/windows_386/libquickjs.a) |
| MacOS | x64 | [libquickjs.a](deps/libs/darwin_amd64/libquickjs.a) |
| MacOS | arm64| [libquickjs.a](deps/libs/darwin_arm64/libquickjs.a) |
\* The windows static library is compiled based on mingw32 12.2.0. Please confirm go version > 1.20.0
## Version Notes
| quickjs-go | QuickJS |
| ---------- | ------- |
| v0.1.x | v2021-03-27 |
| v0.2.x | v2023-12-09 |
| v0.3.x | v2024-01-13 |
| v0.4.x | v2024-02-14 |
## Features
* Evaluate script
* Compile script into bytecode and Eval from bytecode
* Operate JavaScript values and objects in Go
* Bind Go function to JavaScript async/sync function
* Simple exception throwing and catching
## Guidelines
1. Free `quickjs.Runtime` and `quickjs.Context` once you are done using them.
2. Free `quickjs.Value`'s returned by `Eval()` and `EvalFile()`. All other values do not need to be freed, as they get garbage-collected.
3. Use `ctx.Loop()` wait for promise/job result after you using promise/job
4. You may access the stacktrace of an error returned by `Eval()` or `EvalFile()` by casting it to a `*quickjs.Error`.
5. Make new copies of arguments should you want to return them in functions you created.
## Usage
```go
import "github.com/buke/quickjs-go"
```
### Run a script
```go
package main
import (
"fmt"
"github.com/buke/quickjs-go"
)
func main() {
// Create a new runtime
rt := quickjs.NewRuntime(
quickjs.WithExecuteTimeout(30),
quickjs.WithMemoryLimit(128*1024),
quickjs.WithGCThreshold(256*1024),
quickjs.WithMaxStackSize(65534),
quickjs.WithCanBlock(true),
)
defer rt.Close()
// Create a new context
ctx := rt.NewContext()
defer ctx.Close()
ret, err := ctx.Eval("'Hello ' + 'QuickJS!'")
if err != nil {
println(err.Error())
}
fmt.Println(ret.String())
}
```
### Get/Set Javascript Object
```go
package main
import (
"fmt"
"github.com/buke/quickjs-go"
)
func main() {
// Create a new runtime
rt := quickjs.NewRuntime()
defer rt.Close()
// Create a new context
ctx := rt.NewContext()
defer ctx.Close()
test := ctx.Object()
test.Set("A", ctx.String("String A"))
test.Set("B", ctx.String("String B"))
test.Set("C", ctx.String("String C"))
ctx.Globals().Set("test", test)
ret, _ := ctx.Eval(`Object.keys(test).map(key => test[key]).join(" ")`)
defer ret.Free()
fmt.Println(ret.String())
}
```
### Bind Go Funtion to Javascript async/sync function
```go
package main
import "github.com/buke/quickjs-go"
func main() {
// Create a new runtime
rt := quickjs.NewRuntime()
defer rt.Close()
// Create a new context
ctx := rt.NewContext()
defer ctx.Close()
// Create a new object
test := ctx.Object()
defer test.Free()
// bind properties to the object
test.Set("A", test.Context().String("String A"))
test.Set("B", ctx.Int32(0))
test.Set("C", ctx.Bool(false))
// bind go function to js object
test.Set("hello", ctx.Function(func(ctx *quickjs.Context, this quickjs.Value, args []quickjs.Value) quickjs.Value {
return ctx.String("Hello " + args[0].String())
}))
// bind "test" object to global object
ctx.Globals().Set("test", test)
// call js function by js
js_ret, _ := ctx.Eval(`test.hello("Javascript!")`)
fmt.Println(js_ret.String())
// call js function by go
go_ret := ctx.Globals().Get("test").Call("hello", ctx.String("Golang!"))
fmt.Println(go_ret.String())
//bind go function to Javascript async function
ctx.Globals().Set("testAsync", ctx.AsyncFunction(func(ctx *quickjs.Context, this quickjs.Value, promise quickjs.Value, args []quickjs.Value) {
promise.Call("resolve", ctx.String("Hello Async Function!"))
}))
ret, _ := ctx.Eval(`
var ret;
testAsync().then(v => ret = v)
`)
defer ret.Free()
// wait for promise resolve
ctx.Loop()
//get promise result
asyncRet, _ := ctx.Eval("ret")
defer asyncRet.Free()
fmt.Println(asyncRet.String())
// Output:
// Hello Javascript!
// Hello Golang!
// Hello Async Function!
}
```
### Error Handling
```go
package main
import (
"fmt"
"github.com/buke/quickjs-go"
)
func main() {
// Create a new runtime
rt := quickjs.NewRuntime()
defer rt.Close()
// Create a new context
ctx := rt.NewContext()
defer ctx.Close()
ctx.Globals().SetFunction("A", func(ctx *Context, this Value, args []Value) Value {
// raise error
return ctx.ThrowError(expected)
})
_, actual := ctx.Eval("A()")
fmt.Println(actual.Error())
}
```
### Bytecode Compiler
```go
package main
import (
"fmt"
"github.com/buke/quickjs-go"
)
func main() {
// Create a new runtime
rt := quickjs.NewRuntime()
defer rt.Close()
// Create a new context
ctx := rt.NewContext()
defer ctx.Close()
jsStr := `
function fib(n)
{
if (n <= 0)
return 0;
else if (n == 1)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
fib(10)
`
// Compile the script to bytecode
buf, _ := ctx.Compile(jsStr)
// Create a new runtime
rt2 := quickjs.NewRuntime()
defer rt2.Close()
// Create a new context
ctx2 := rt2.NewContext()
defer ctx2.Close()
//Eval bytecode
result, _ := ctx2.EvalBytecode(buf)
fmt.Println(result.Int32())
}
```
### Runtime Options: memory, stack, GC, ...
```go
package main
import (
"fmt"
"github.com/buke/quickjs-go"
)
func main() {
// Create a new runtime
rt := quickjs.NewRuntime()
defer rt.Close()
// set runtime options
rt.SetExecuteTimeout(30) // Set execute timeout to 30 seconds
rt.SetMemoryLimit(256 * 1024) // Set memory limit to 256KB
rt.SetMaxStackSize(65534) // Set max stack size to 65534
rt.SetGCThreshold(256 * 1024) // Set GC threshold to 256KB
rt.SetCanBlock(true) // Set can block to true
// Create a new context
ctx := rt.NewContext()
defer ctx.Close()
result, err := ctx.Eval(`var array = []; while (true) { array.push(null) }`)
defer result.Free()
}
```
### ES6 Module Support
```go
package main
import (
"fmt"
"github.com/buke/quickjs-go"
)
func main() {
// enable module import
rt := quickjs.NewRuntime(quickjs.WithModuleImport(true))
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
// eval module
r1, err := ctx.EvalFile("./test/hello_module.js")
defer r1.Free()
require.NoError(t, err)
require.EqualValues(t, 55, ctx.Globals().Get("result").Int32())
// load module
r2, err := ctx.LoadModuleFile("./test/fib_module.js", "fib_foo")
defer r2.Free()
require.NoError(t, err)
// call module
r3, err := ctx.Eval(`
import {fib} from 'fib_foo';
globalThis.result = fib(9);
`)
defer r3.Free()
require.NoError(t, err)
require.EqualValues(t, 34, ctx.Globals().Get("result").Int32())
}
```
## Documentation
Go Reference & more examples: https://pkg.go.dev/github.com/buke/quickjs-go
## License
[MIT](./LICENSE)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fbuke%2Fquickjs-go.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fbuke%2Fquickjs-go?ref=badge_large)
## Related Projects
* https://github.com/buke/quickjs-go-polyfill

350
README_zh-cn.md Normal file
View File

@ -0,0 +1,350 @@
# quickjs-go
[English](README.md) | 简体中文
[![Test](https://github.com/buke/quickjs-go/workflows/Test/badge.svg)](https://github.com/buke/quickjs-go/actions?query=workflow%3ATest)
[![codecov](https://codecov.io/gh/buke/quickjs-go/branch/main/graph/badge.svg?token=DW5RGD01AG)](https://codecov.io/gh/buke/quickjs-go)
[![Go Report Card](https://goreportcard.com/badge/github.com/buke/quickjs-go)](https://goreportcard.com/report/github.com/buke/quickjs-go)
[![GoDoc](https://pkg.go.dev/badge/github.com/buke/quickjs-go?status.svg)](https://pkg.go.dev/github.com/buke/quickjs-go?tab=doc)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fbuke%2Fquickjs-go.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fbuke%2Fquickjs-go?ref=badge_shield)
Go 语言的QuickJS绑定库快速、小型、可嵌入的ES2020 JavaScript解释器。
## 平台支持
使用预编译的quickjs静态库支持以下平台
| 平台 | 架构 | 静态库 |
| -------- | ------------ | --------------------------------------------------- |
| Linux | x64 | [libquickjs.a](deps/libs/linux_amd64/libquickjs.a) |
| Linux | arm64 | [libquickjs.a](deps/libs/linux_arm64/libquickjs.a) |
| Windows | x64 | [libquickjs.a](deps/libs/windows_amd64/libquickjs.a) |
| Windows | x86 | [libquickjs.a](deps/libs/windows_386/libquickjs.a) |
| MacOS | x64 | [libquickjs.a](deps/libs/darwin_amd64/libquickjs.a) |
| MacOS | arm64 | [libquickjs.a](deps/libs/darwin_arm64/libquickjs.a) |
\* windows 静态库基于 mingw 12.2.0 编译, 请确认 go version > 1.20.0
## 版本说明
| quickjs-go | QuickJS |
| ---------- | ------- |
| v0.1.x | v2021-03-27 |
| v0.2.x | v2023-12-09 |
| v0.3.x | v2024-01-13 |
| v0.4.x | v2024-02-14 |
## 功能
* 执行javascript脚本
* 编译javascript脚本到字节码并执行字节码
* 在 Go 中操作 JavaScript 值和对象
* 绑定 Go 函数到 JavaScript 同步函数和异步函数
* 简单的异常抛出和捕获
## 指南
1. 在使用完毕后,请记得关闭 `quickjs.Runtime``quickjs.Context`
2. 请记得关闭由 `Eval()``EvalFile()` 返回的 `quickjs.Value`。其他值不需要关闭,因为它们会被垃圾回收。
3. 如果你使用了promise 或 async function请使用 `ctx.Loop()` 等待所有的promise/job结果。
4. 如果`Eval()` 或 `EvalFile()`返回了错误,可强制转换为`*quickjs.Error`以读取错误的堆栈信息。
5. 如果你想在函数中返回参数,请在函数中复制参数。
## 用法
```go
import "github.com/buke/quickjs-go"
```
### 执行javascript脚本
```go
package main
import (
"fmt"
"github.com/buke/quickjs-go"
)
func main() {
// Create a new runtime
rt := quickjs.NewRuntime(
quickjs.WithExecuteTimeout(30),
quickjs.WithMemoryLimit(128*1024),
quickjs.WithGCThreshold(256*1024),
quickjs.WithMaxStackSize(65534),
quickjs.WithCanBlock(true),
)
defer rt.Close()
// Create a new context
ctx := rt.NewContext()
defer ctx.Close()
ret, err := ctx.Eval("'Hello ' + 'QuickJS!'")
if err != nil {
println(err.Error())
}
fmt.Println(ret.String())
}
```
### 读取/设置 JavaScript 对象
```go
package main
import (
"fmt"
"github.com/buke/quickjs-go"
)
func main() {
// Create a new runtime
rt := quickjs.NewRuntime()
defer rt.Close()
// Create a new context
ctx := rt.NewContext()
defer ctx.Close()
test := ctx.Object()
test.Set("A", ctx.String("String A"))
test.Set("B", ctx.String("String B"))
test.Set("C", ctx.String("String C"))
ctx.Globals().Set("test", test)
ret, _ := ctx.Eval(`Object.keys(test).map(key => test[key]).join(" ")`)
defer ret.Free()
fmt.Println(ret.String())
}
```
### 函数绑定
```go
package main
import "github.com/buke/quickjs-go"
func main() {
// Create a new runtime
rt := quickjs.NewRuntime()
defer rt.Close()
// Create a new context
ctx := rt.NewContext()
defer ctx.Close()
// Create a new object
test := ctx.Object()
defer test.Free()
// bind properties to the object
test.Set("A", test.Context().String("String A"))
test.Set("B", ctx.Int32(0))
test.Set("C", ctx.Bool(false))
// bind go function to js object
test.Set("hello", ctx.Function(func(ctx *quickjs.Context, this quickjs.Value, args []quickjs.Value) quickjs.Value {
return ctx.String("Hello " + args[0].String())
}))
// bind "test" object to global object
ctx.Globals().Set("test", test)
// call js function by js
js_ret, _ := ctx.Eval(`test.hello("Javascript!")`)
fmt.Println(js_ret.String())
// call js function by go
go_ret := ctx.Globals().Get("test").Call("hello", ctx.String("Golang!"))
fmt.Println(go_ret.String())
//bind go function to Javascript async function
ctx.Globals().Set("testAsync", ctx.AsyncFunction(func(ctx *quickjs.Context, this quickjs.Value, promise quickjs.Value, args []quickjs.Value) {
promise.Call("resolve", ctx.String("Hello Async Function!"))
}))
ret, _ := ctx.Eval(`
var ret;
testAsync().then(v => ret = v)
`)
defer ret.Free()
// wait for promise resolve
ctx.Loop()
//get promise result
asyncRet, _ := ctx.Eval("ret")
defer asyncRet.Free()
fmt.Println(asyncRet.String())
// Output:
// Hello Javascript!
// Hello Golang!
// Hello Async Function!
}
```
### 异常抛出和捕获
```go
package main
import (
"fmt"
"github.com/buke/quickjs-go"
)
func main() {
// Create a new runtime
rt := quickjs.NewRuntime()
defer rt.Close()
// Create a new context
ctx := rt.NewContext()
defer ctx.Close()
ctx.Globals().SetFunction("A", func(ctx *Context, this Value, args []Value) Value {
// raise error
return ctx.ThrowError(expected)
})
_, actual := ctx.Eval("A()")
fmt.Println(actual.Error())
}
```
### Bytecode编译和执行
```go
package main
import (
"fmt"
"github.com/buke/quickjs-go"
)
func main() {
// Create a new runtime
rt := quickjs.NewRuntime()
defer rt.Close()
// Create a new context
ctx := rt.NewContext()
defer ctx.Close()
jsStr := `
function fib(n)
{
if (n <= 0)
return 0;
else if (n == 1)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
fib(10)
`
// Compile the script to bytecode
buf, _ := ctx.Compile(jsStr)
// Create a new runtime
rt2 := quickjs.NewRuntime()
defer rt2.Close()
// Create a new context
ctx2 := rt2.NewContext()
defer ctx2.Close()
//Eval bytecode
result, _ := ctx2.EvalBytecode(buf)
fmt.Println(result.Int32())
}
```
### 设置内存、栈、GC等等
```go
package main
import (
"fmt"
"github.com/buke/quickjs-go"
)
func main() {
// Create a new runtime
rt := quickjs.NewRuntime()
defer rt.Close()
// set runtime options
rt.SetExecuteTimeout(30) // Set execute timeout to 30 seconds
rt.SetMemoryLimit(256 * 1024) // Set memory limit to 256KB
rt.SetMaxStackSize(65534) // Set max stack size to 65534
rt.SetGCThreshold(256 * 1024) // Set GC threshold to 256KB
rt.SetCanBlock(true) // Set can block to true
// Create a new context
ctx := rt.NewContext()
defer ctx.Close()
result, err := ctx.Eval(`var array = []; while (true) { array.push(null) }`)
defer result.Free()
}
```
### ES6 模块支持
```go
package main
import (
"fmt"
"github.com/buke/quickjs-go"
)
func main() {
// enable module import
rt := quickjs.NewRuntime(quickjs.WithModuleImport(true))
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
// eval module
r1, err := ctx.EvalFile("./test/hello_module.js")
defer r1.Free()
require.NoError(t, err)
require.EqualValues(t, 55, ctx.Globals().Get("result").Int32())
// load module
r2, err := ctx.LoadModuleFile("./test/fib_module.js", "fib_foo")
defer r2.Free()
require.NoError(t, err)
// call module
r3, err := ctx.Eval(`
import {fib} from 'fib_foo';
globalThis.result = fib(9);
`)
defer r3.Free()
require.NoError(t, err)
require.EqualValues(t, 34, ctx.Globals().Get("result").Int32())
}
```
## 文档
Go 语言文档和示例: https://pkg.go.dev/github.com/buke/quickjs-go
## 协议
[MIT](./LICENSE)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fbuke%2Fquickjs-go.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fbuke%2Fquickjs-go?ref=badge_large)
## 相关项目
* https://github.com/buke/quickjs-go-polyfill

63
bridge.c Normal file
View File

@ -0,0 +1,63 @@
#include "_cgo_export.h"
#include "quickjs.h"
#include <time.h>
JSValue JS_NewNull() { return JS_NULL; }
JSValue JS_NewUndefined() { return JS_UNDEFINED; }
JSValue JS_NewUninitialized() { return JS_UNINITIALIZED; }
JSValue ThrowSyntaxError(JSContext *ctx, const char *fmt) { return JS_ThrowSyntaxError(ctx, "%s", fmt); }
JSValue ThrowTypeError(JSContext *ctx, const char *fmt) { return JS_ThrowTypeError(ctx, "%s", fmt); }
JSValue ThrowReferenceError(JSContext *ctx, const char *fmt) { return JS_ThrowReferenceError(ctx, "%s", fmt); }
JSValue ThrowRangeError(JSContext *ctx, const char *fmt) { return JS_ThrowRangeError(ctx, "%s", fmt); }
JSValue ThrowInternalError(JSContext *ctx, const char *fmt) { return JS_ThrowInternalError(ctx, "%s", fmt); }
int ValueGetTag(JSValueConst v) {
return JS_VALUE_GET_TAG(v);
}
JSValue InvokeProxy(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
return goProxy(ctx, this_val, argc, argv);
}
JSValue InvokeAsyncProxy(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
return goAsyncProxy(ctx, this_val, argc, argv);
}
int interruptHandler(JSRuntime *rt, void *handlerArgs) {
return goInterruptHandler(rt, handlerArgs);
}
void SetInterruptHandler(JSRuntime *rt, void *handlerArgs){
JS_SetInterruptHandler(rt, &interruptHandler, handlerArgs);
}
typedef struct {
time_t start;
time_t timeout;
} TimeoutStruct;
int timeoutHandler(JSRuntime *rt, void *opaque) {
TimeoutStruct* ts = (TimeoutStruct*)opaque;
time_t timeout = ts->timeout;
time_t start = ts->start;
if (timeout <= 0) {
return 0;
}
time_t now = time(NULL);
if (now - start > timeout) {
free(ts);
return 1;
}
return 0;
}
void SetExecuteTimeout(JSRuntime *rt, time_t timeout){
TimeoutStruct* ts = malloc(sizeof(TimeoutStruct));
ts->start = time(NULL);
ts->timeout = timeout;
JS_SetInterruptHandler(rt, &timeoutHandler, ts);
}

75
bridge.go Normal file
View File

@ -0,0 +1,75 @@
package quickjs
import (
"runtime/cgo"
"unsafe"
)
/*
#include <stdint.h>
#include "bridge.h"
*/
import "C"
//export goProxy
func goProxy(ctx *C.JSContext, thisVal C.JSValueConst, argc C.int, argv *C.JSValueConst) C.JSValue {
refs := unsafe.Slice(argv, argc) // Go 1.17 and later
// get the function
fnHandler := C.int64_t(0)
C.JS_ToInt64(ctx, &fnHandler, refs[0])
fn := cgo.Handle(fnHandler).Value().(func(ctx *Context, this Value, args []Value) Value)
// get ctx
ctxHandler := C.int64_t(0)
C.JS_ToInt64(ctx, &ctxHandler, refs[1])
ctxOrigin := cgo.Handle(ctxHandler).Value().(*Context)
// refs[0] is the id, refs[1] is the ctx
args := make([]Value, len(refs)-2)
for i := 0; i < len(args); i++ {
args[i].ctx = ctxOrigin
args[i].ref = refs[2+i]
}
result := fn(ctxOrigin, Value{ctx: ctxOrigin, ref: thisVal}, args)
return result.ref
}
//export goAsyncProxy
func goAsyncProxy(ctx *C.JSContext, thisVal C.JSValueConst, argc C.int, argv *C.JSValueConst) C.JSValue {
refs := unsafe.Slice(argv, argc) // Go 1.17 and later
// get the function
fnHandler := C.int64_t(0)
C.JS_ToInt64(ctx, &fnHandler, refs[0])
asyncFn := cgo.Handle(fnHandler).Value().(func(ctx *Context, this Value, promise Value, args []Value) Value)
// get ctx
ctxHandler := C.int64_t(0)
C.JS_ToInt64(ctx, &ctxHandler, refs[1])
ctxOrigin := cgo.Handle(ctxHandler).Value().(*Context)
args := make([]Value, len(refs)-2)
for i := 0; i < len(args); i++ {
args[i].ctx = ctxOrigin
args[i].ref = refs[2+i]
}
promise := args[0]
result := asyncFn(ctxOrigin, Value{ctx: ctxOrigin, ref: thisVal}, promise, args[1:])
return result.ref
}
//export goInterruptHandler
func goInterruptHandler(rt *C.JSRuntime, handlerArgs unsafe.Pointer) C.int {
handlerArgsStruct := (*C.handlerArgs)(handlerArgs)
hFn := cgo.Handle(handlerArgsStruct.fn)
hFnValue := hFn.Value().(InterruptHandler)
// defer hFn.Delete()
return C.int(hFnValue())
}

29
bridge.h Normal file
View File

@ -0,0 +1,29 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "quickjs.h"
#include "quickjs-libc.h"
extern JSValue JS_NewNull();
extern JSValue JS_NewUndefined();
extern JSValue JS_NewUninitialized();
extern JSValue ThrowSyntaxError(JSContext *ctx, const char *fmt) ;
extern JSValue ThrowTypeError(JSContext *ctx, const char *fmt) ;
extern JSValue ThrowReferenceError(JSContext *ctx, const char *fmt) ;
extern JSValue ThrowRangeError(JSContext *ctx, const char *fmt) ;
extern JSValue ThrowInternalError(JSContext *ctx, const char *fmt) ;
int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int flags);
extern JSValue InvokeProxy(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
extern JSValue InvokeAsyncProxy(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
extern int ValueGetTag(JSValueConst v);
typedef struct {
uintptr_t fn;
} handlerArgs;
extern void SetInterruptHandler(JSRuntime *rt, void *handlerArgs);
extern void SetExecuteTimeout(JSRuntime *rt, time_t timeout);

265
collection.go Normal file
View File

@ -0,0 +1,265 @@
package quickjs
import (
"errors"
)
//
// Array
// @Description: simply implement the array structure of js
type Array struct {
arrayValue Value
ctx *Context
}
func NewQjsArray(value Value, ctx *Context) *Array {
return &Array{
arrayValue: value,
ctx: ctx,
}
}
// Push
//
// @Description: add one or more elements after the array,returns the new array length
// @receiver a :
// @param elements :
// @return int64
func (a Array) Push(elements ...Value) int64 {
ret := a.arrayValue.Call("push", elements...)
//defer ret.Free()
return ret.Int64()
}
// Get
//
// @Description: get the specific value by subscript
// @receiver a :
// @param index :
// @return Value
func (a Array) Get(index int64) (Value, error) {
if index < 0 {
return Value{}, errors.New("the input index value is a negative number")
}
if index >= a.arrayValue.Len() {
return Value{}, errors.New("index subscript out of range")
}
return a.arrayValue.GetIdx(index), nil
}
// Set
//
// @Description:
// @receiver a :
// @param index :
// @param value :
// @return error
func (a Array) Set(index int64, value Value) error {
if index < 0 {
return errors.New("the input index value is a negative number")
}
if index >= a.arrayValue.Len() {
return errors.New("index subscript out of range")
}
a.arrayValue.SetIdx(index, value)
return nil
}
func (a Array) Delete(index int64) (bool, error) {
if index < 0 {
return false, errors.New("the input index value is a negative number")
}
if index >= a.arrayValue.Len() {
return false, errors.New("index subscript out of range")
}
removeList := a.arrayValue.Call("splice", a.ctx.Int64(index), a.ctx.Int64(1))
defer removeList.Free()
return removeList.IsArray(), nil
}
// Len
//
// @Description: get the length of the array
// @receiver a :
// @return int64
func (a Array) Len() int64 {
return a.arrayValue.Len()
}
// HasIdx
//
// @Description: Determine whether there is data at the current subscript position
// @receiver a :
// @param i :
// @return bool
func (a Array) HasIdx(i int64) bool {
return a.arrayValue.HasIdx(i)
}
// ToValue
//
// @Description: get the value object of qjs
// @receiver a :
// @return Value
func (a Array) ToValue() Value {
return a.arrayValue
}
func (a Array) Free() {
a.arrayValue.Free()
}
//
// Map
// @Description: simply implement the map structure of js
type Map struct {
mapValue Value
ctx *Context
}
func NewQjsMap(value Value, ctx *Context) *Map {
return &Map{
mapValue: value,
ctx: ctx,
}
}
// Get
//
// @Description: get the value by key
// @receiver m :
// @param key :
// @return Value
func (m Map) Get(key Value) Value {
return m.mapValue.Call("get", key)
}
// Put
//
// @Description:
// @receiver m :
// @param key :
// @param value :
func (m Map) Put(key Value, value Value) {
m.mapValue.Call("set", key, value).Free()
}
// Delete
//
// @Description:delete the value of an element by key
// @receiver m :
// @param key :
func (m Map) Delete(key Value) {
m.mapValue.Call("delete", key).Free()
}
// Has
//
// @Description:determine whether an element exists
// @receiver m :
// @param key :
func (m Map) Has(key Value) bool {
boolValue := m.mapValue.Call("has", key)
defer boolValue.Free()
return boolValue.Bool()
}
// ForEach
//
// @Description: iterate map
// @receiver m :
func (m Map) ForEach(forFn func(key Value, value Value)) {
forEachFn := m.ctx.Function(func(ctx *Context, this Value, args []Value) Value {
forFn(args[1], args[0])
return ctx.Null()
})
value := m.mapValue.Call("forEach", forEachFn)
forEachFn.Free()
defer value.Free()
}
func (m Map) Free() {
m.mapValue.Free()
}
func (m Map) ToValue() Value {
return m.mapValue
}
// Call
//
// @Description: call some internal methods of js
// @receiver a :
// @param funcName :
// @param values :
// @return Value
func (m Map) Call(funcName string, values []Value) Value {
return m.mapValue.Call(funcName, values...)
}
type Set struct {
setValue Value
ctx *Context
}
func NewQjsSet(value Value, ctx *Context) *Set {
return &Set{
setValue: value,
ctx: ctx,
}
}
// Add
//
// @Description: add element
// @receiver s :
// @param value :
func (s Set) Add(value Value) {
v := s.setValue.Call("add", value)
defer v.Free()
}
// Delete
//
// @Description: add element
// @receiver s :
// @param value :
func (s Set) Delete(value Value) {
v := s.setValue.Call("delete", value)
defer v.Free()
}
// Has
//
// @Description: determine whether an element exists in the set
// @receiver s :
// @param value :
// @return bool
func (s Set) Has(value Value) bool {
v := s.setValue.Call("has", value)
return v.Bool()
}
// ForEach
//
// @Description: iterate set
// @receiver m :
func (s Set) ForEach(forFn func(value Value)) {
forEachFn := s.ctx.Function(func(ctx *Context, this Value, args []Value) Value {
forFn(args[0])
return ctx.Null()
})
value := s.setValue.Call("forEach", forEachFn)
forEachFn.Free()
defer value.Free()
}
func (s Set) Free() {
s.setValue.Free()
}
func (s Set) ToValue() Value {
return s.setValue
}

556
context.go Normal file
View File

@ -0,0 +1,556 @@
package quickjs
import (
"fmt"
"os"
"runtime/cgo"
"unsafe"
)
/*
#include <stdint.h> // for uintptr_t
#include "bridge.h"
*/
import "C"
// Context represents a Javascript context (or Realm). Each JSContext has its own global objects and system objects. There can be several JSContexts per JSRuntime and they can share objects, similar to frames of the same origin sharing Javascript objects in a web browser.
type Context struct {
runtime *Runtime
ref *C.JSContext
globals *Value
proxy *Value
asyncProxy *Value
}
// Runtime returns the runtime of the context.
func (ctx *Context) Runtime() *Runtime {
return ctx.runtime
}
// Free will free context and all associated objects.
func (ctx *Context) Close() {
if ctx.proxy != nil {
ctx.proxy.Free()
}
if ctx.asyncProxy != nil {
ctx.asyncProxy.Free()
}
if ctx.globals != nil {
ctx.globals.Free()
}
C.JS_FreeContext(ctx.ref)
}
// Null return a null value.
func (ctx *Context) Null() Value {
return Value{ctx: ctx, ref: C.JS_NewNull()}
}
// Undefined return a undefined value.
func (ctx *Context) Undefined() Value {
return Value{ctx: ctx, ref: C.JS_NewUndefined()}
}
// Uninitialized returns a uninitialized value.
func (ctx *Context) Uninitialized() Value {
return Value{ctx: ctx, ref: C.JS_NewUninitialized()}
}
// Error returns a new exception value with given message.
func (ctx *Context) Error(err error) Value {
val := Value{ctx: ctx, ref: C.JS_NewError(ctx.ref)}
val.Set("message", ctx.String(err.Error()))
return val
}
// Bool returns a bool value with given bool.
func (ctx *Context) Bool(b bool) Value {
bv := 0
if b {
bv = 1
}
return Value{ctx: ctx, ref: C.JS_NewBool(ctx.ref, C.int(bv))}
}
// Int32 returns a int32 value with given int32.
func (ctx *Context) Int32(v int32) Value {
return Value{ctx: ctx, ref: C.JS_NewInt32(ctx.ref, C.int32_t(v))}
}
// Int64 returns a int64 value with given int64.
func (ctx *Context) Int64(v int64) Value {
return Value{ctx: ctx, ref: C.JS_NewInt64(ctx.ref, C.int64_t(v))}
}
// Uint32 returns a uint32 value with given uint32.
func (ctx *Context) Uint32(v uint32) Value {
return Value{ctx: ctx, ref: C.JS_NewUint32(ctx.ref, C.uint32_t(v))}
}
// BigInt64 returns a int64 value with given uint64.
func (ctx *Context) BigInt64(v int64) Value {
return Value{ctx: ctx, ref: C.JS_NewBigInt64(ctx.ref, C.int64_t(v))}
}
// BigUint64 returns a uint64 value with given uint64.
func (ctx *Context) BigUint64(v uint64) Value {
return Value{ctx: ctx, ref: C.JS_NewBigUint64(ctx.ref, C.uint64_t(v))}
}
// Float64 returns a float64 value with given float64.
func (ctx *Context) Float64(v float64) Value {
return Value{ctx: ctx, ref: C.JS_NewFloat64(ctx.ref, C.double(v))}
}
// String returns a string value with given string.
func (ctx *Context) String(v string) Value {
ptr := C.CString(v)
defer C.free(unsafe.Pointer(ptr))
return Value{ctx: ctx, ref: C.JS_NewString(ctx.ref, ptr)}
}
// ArrayBuffer returns a string value with given binary data.
func (ctx *Context) ArrayBuffer(binaryData []byte) Value {
return Value{ctx: ctx, ref: C.JS_NewArrayBufferCopy(ctx.ref, (*C.uchar)(&binaryData[0]), C.size_t(len(binaryData)))}
}
// Object returns a new object value.
func (ctx *Context) Object() Value {
return Value{ctx: ctx, ref: C.JS_NewObject(ctx.ref)}
}
// ParseJson parses given json string and returns a object value.
func (ctx *Context) ParseJSON(v string) Value {
ptr := C.CString(v)
defer C.free(unsafe.Pointer(ptr))
filenamePtr := C.CString("")
defer C.free(unsafe.Pointer(filenamePtr))
return Value{ctx: ctx, ref: C.JS_ParseJSON(ctx.ref, ptr, C.size_t(len(v)), filenamePtr)}
}
// Array returns a new array value.
func (ctx *Context) Array() *Array {
val := Value{ctx: ctx, ref: C.JS_NewArray(ctx.ref)}
return NewQjsArray(val, ctx)
}
func (ctx *Context) Map() *Map {
ctor := ctx.Globals().Get("Map")
defer ctor.Free()
val := Value{ctx: ctx, ref: C.JS_CallConstructor(ctx.ref, ctor.ref, 0, nil)}
return NewQjsMap(val, ctx)
}
func (ctx *Context) Set() *Set {
ctor := ctx.Globals().Get("Set")
defer ctor.Free()
val := Value{ctx: ctx, ref: C.JS_CallConstructor(ctx.ref, ctor.ref, 0, nil)}
return NewQjsSet(val, ctx)
}
// Function returns a js function value with given function template.
func (ctx *Context) Function(fn func(ctx *Context, this Value, args []Value) Value) Value {
if ctx.proxy == nil {
ctx.proxy = &Value{
ctx: ctx,
ref: C.JS_NewCFunction(ctx.ref, (*C.JSCFunction)(unsafe.Pointer(C.InvokeProxy)), nil, C.int(0)),
}
}
fnHandler := ctx.Int64(int64(cgo.NewHandle(fn)))
ctxHandler := ctx.Int64(int64(cgo.NewHandle(ctx)))
args := []C.JSValue{ctx.proxy.ref, fnHandler.ref, ctxHandler.ref}
val, err := ctx.Eval(`(proxy, fnHandler, ctx) => function() { return proxy.call(this, fnHandler, ctx, ...arguments); }`)
defer val.Free()
if err != nil {
panic(err)
}
return Value{ctx: ctx, ref: C.JS_Call(ctx.ref, val.ref, ctx.Null().ref, C.int(len(args)), &args[0])}
}
// AsyncFunction returns a js async function value with given function template.
func (ctx *Context) AsyncFunction(asyncFn func(ctx *Context, this Value, promise Value, args []Value) Value) Value {
if ctx.asyncProxy == nil {
ctx.asyncProxy = &Value{
ctx: ctx,
ref: C.JS_NewCFunction(ctx.ref, (*C.JSCFunction)(unsafe.Pointer(C.InvokeAsyncProxy)), nil, C.int(0)),
}
}
fnHandler := ctx.Int64(int64(cgo.NewHandle(asyncFn)))
ctxHandler := ctx.Int64(int64(cgo.NewHandle(ctx)))
args := []C.JSValue{ctx.asyncProxy.ref, fnHandler.ref, ctxHandler.ref}
val, err := ctx.Eval(`(proxy, fnHandler, ctx) => async function(...arguments) {
let resolve, reject;
const promise = new Promise((resolve_, reject_) => {
resolve = resolve_;
reject = reject_;
});
promise.resolve = resolve;
promise.reject = reject;
proxy.call(this, fnHandler, ctx, promise, ...arguments);
return await promise;
}`)
defer val.Free()
if err != nil {
panic(err)
}
return Value{ctx: ctx, ref: C.JS_Call(ctx.ref, val.ref, ctx.Null().ref, C.int(len(args)), &args[0])}
}
// InterruptHandler is a function type for interrupt handler.
/* return != 0 if the JS code needs to be interrupted */
type InterruptHandler func() int
// SetInterruptHandler sets a interrupt handler.
func (ctx *Context) SetInterruptHandler(handler InterruptHandler) {
handlerArgs := C.handlerArgs{
fn: (C.uintptr_t)(cgo.NewHandle(handler)),
}
C.SetInterruptHandler(ctx.runtime.ref, unsafe.Pointer(&handlerArgs))
}
// Atom returns a new Atom value with given string.
func (ctx *Context) Atom(v string) Atom {
ptr := C.CString(v)
defer C.free(unsafe.Pointer(ptr))
return Atom{ctx: ctx, ref: C.JS_NewAtom(ctx.ref, ptr)}
}
// Atom returns a new Atom value with given idx.
func (ctx *Context) AtomIdx(idx int64) Atom {
return Atom{ctx: ctx, ref: C.JS_NewAtomUInt32(ctx.ref, C.uint32_t(idx))}
}
// Invoke invokes a function with given this value and arguments.
func (ctx *Context) Invoke(fn Value, this Value, args ...Value) Value {
cargs := []C.JSValue{}
for _, x := range args {
cargs = append(cargs, x.ref)
}
if len(cargs) == 0 {
return Value{ctx: ctx, ref: C.JS_Call(ctx.ref, fn.ref, this.ref, 0, nil)}
}
return Value{ctx: ctx, ref: C.JS_Call(ctx.ref, fn.ref, this.ref, C.int(len(cargs)), &cargs[0])}
}
type EvalOptions struct {
js_eval_type_global bool
js_eval_type_module bool
js_eval_flag_strict bool
js_eval_flag_strip bool
js_eval_flag_compile_only bool
filename string
await bool
}
type EvalOption func(*EvalOptions)
func EvalFlagGlobal(global bool) EvalOption {
return func(flags *EvalOptions) {
flags.js_eval_type_global = global
}
}
func EvalFlagModule(module bool) EvalOption {
return func(flags *EvalOptions) {
flags.js_eval_type_module = module
}
}
func EvalFlagStrict(strict bool) EvalOption {
return func(flags *EvalOptions) {
flags.js_eval_flag_strict = strict
}
}
func EvalFlagStrip(strip bool) EvalOption {
return func(flags *EvalOptions) {
flags.js_eval_flag_strip = strip
}
}
func EvalFlagCompileOnly(compileOnly bool) EvalOption {
return func(flags *EvalOptions) {
flags.js_eval_flag_compile_only = compileOnly
}
}
func EvalFileName(filename string) EvalOption {
return func(flags *EvalOptions) {
flags.filename = filename
}
}
func EvalAwait(await bool) EvalOption {
return func(flags *EvalOptions) {
flags.await = await
}
}
// Eval returns a js value with given code.
// Need call Free() `quickjs.Value`'s returned by `Eval()` and `EvalFile()` and `EvalBytecode()`.
// func (ctx *Context) Eval(code string) (Value, error) { return ctx.EvalFile(code, "code") }
func (ctx *Context) Eval(code string, opts ...EvalOption) (Value, error) {
options := EvalOptions{
js_eval_type_global: true,
filename: "<input>",
await: false,
}
for _, fn := range opts {
fn(&options)
}
cFlag := C.int(0)
if options.js_eval_type_global {
cFlag |= C.JS_EVAL_TYPE_GLOBAL
}
if options.js_eval_type_module {
cFlag |= C.JS_EVAL_TYPE_MODULE
}
if options.js_eval_flag_strict {
cFlag |= C.JS_EVAL_FLAG_STRICT
}
if options.js_eval_flag_strip {
cFlag |= C.JS_EVAL_FLAG_STRIP
}
if options.js_eval_flag_compile_only {
cFlag |= C.JS_EVAL_FLAG_COMPILE_ONLY
}
codePtr := C.CString(code)
defer C.free(unsafe.Pointer(codePtr))
filenamePtr := C.CString(options.filename)
defer C.free(unsafe.Pointer(filenamePtr))
if C.JS_DetectModule(codePtr, C.size_t(len(code))) != 0 {
cFlag |= C.JS_EVAL_TYPE_MODULE
}
var val Value
if options.await {
val = Value{ctx: ctx, ref: C.js_std_await(ctx.ref, C.JS_Eval(ctx.ref, codePtr, C.size_t(len(code)), filenamePtr, cFlag))}
} else {
val = Value{ctx: ctx, ref: C.JS_Eval(ctx.ref, codePtr, C.size_t(len(code)), filenamePtr, cFlag)}
}
if val.IsException() {
return val, ctx.Exception()
}
return val, nil
}
// EvalFile returns a js value with given code and filename.
// Need call Free() `quickjs.Value`'s returned by `Eval()` and `EvalFile()` and `EvalBytecode()`.
func (ctx *Context) EvalFile(filePath string, opts ...EvalOption) (Value, error) {
b, err := os.ReadFile(filePath)
if err != nil {
return ctx.Null(), err
}
opts = append(opts, EvalFileName(filePath))
return ctx.Eval(string(b), opts...)
}
// LoadModule returns a js value with given code and module name.
func (ctx *Context) LoadModule(code string, moduleName string) (Value, error) {
codePtr := C.CString(code)
defer C.free(unsafe.Pointer(codePtr))
filenamePtr := C.CString(moduleName)
defer C.free(unsafe.Pointer(filenamePtr))
cFlag := C.JS_EVAL_TYPE_MODULE | C.JS_EVAL_FLAG_COMPILE_ONLY
cVal := C.JS_Eval(ctx.ref, codePtr, C.size_t(len(code)), filenamePtr, C.int(cFlag))
if C.ValueGetTag(cVal) != C.JS_TAG_MODULE {
return ctx.Null(), fmt.Errorf("not a module")
}
if C.JS_ResolveModule(ctx.ref, cVal) != 0 {
C.JS_FreeValue(ctx.ref, cVal)
return ctx.Null(), fmt.Errorf("resolve module failed")
}
C.js_module_set_import_meta(ctx.ref, cVal, 0, 1)
cVal = C.js_std_await(ctx.ref, cVal)
return Value{ctx: ctx, ref: cVal}, nil
}
// LoadModuleFile returns a js value with given file path and module name.
func (ctx *Context) LoadModuleFile(filePath string, moduleName string) (Value, error) {
b, err := os.ReadFile(filePath)
if err != nil {
return ctx.Null(), err
}
return ctx.LoadModule(string(b), moduleName)
}
// LoadModuleByteCode returns a js value with given bytecode and module name.
func (ctx *Context) LoadModuleBytecode(buf []byte, moduleName string) (Value, error) {
cbuf := C.CBytes(buf)
cVal := C.JS_ReadObject(ctx.ref, (*C.uint8_t)(cbuf), C.size_t(len(buf)), C.JS_READ_OBJ_BYTECODE)
defer C.js_free(ctx.ref, unsafe.Pointer(cbuf))
if C.JS_IsException(cVal) == 1 {
return ctx.Null(), ctx.Exception()
}
if C.ValueGetTag(cVal) != C.JS_TAG_MODULE {
return ctx.Null(), fmt.Errorf("not a module")
}
if C.JS_ResolveModule(ctx.ref, cVal) != 0 {
C.JS_FreeValue(ctx.ref, cVal)
return ctx.Null(), fmt.Errorf("resolve module failed")
}
C.js_module_set_import_meta(ctx.ref, cVal, 0, 1)
cVal = C.js_std_await(ctx.ref, cVal)
return Value{ctx: ctx, ref: cVal}, nil
}
// EvalBytecode returns a js value with given bytecode.
// Need call Free() `quickjs.Value`'s returned by `Eval()` and `EvalFile()` and `EvalBytecode()`.
func (ctx *Context) EvalBytecode(buf []byte) (Value, error) {
cbuf := C.CBytes(buf)
obj := Value{ctx: ctx, ref: C.JS_ReadObject(ctx.ref, (*C.uint8_t)(cbuf), C.size_t(len(buf)), C.JS_READ_OBJ_BYTECODE)}
defer C.js_free(ctx.ref, unsafe.Pointer(cbuf))
if obj.IsException() {
return obj, ctx.Exception()
}
val := Value{ctx: ctx, ref: C.JS_EvalFunction(ctx.ref, obj.ref)}
if val.IsException() {
return val, ctx.Exception()
}
return val, nil
}
// Compile returns a compiled bytecode with given code.
func (ctx *Context) Compile(code string, opts ...EvalOption) ([]byte, error) {
opts = append(opts, EvalFlagCompileOnly(true))
val, err := ctx.Eval(code, opts...)
if err != nil {
return nil, err
}
defer val.Free()
var kSize C.size_t
ptr := C.JS_WriteObject(ctx.ref, &kSize, val.ref, C.JS_WRITE_OBJ_BYTECODE)
defer C.js_free(ctx.ref, unsafe.Pointer(ptr))
if C.int(kSize) <= 0 {
return nil, ctx.Exception()
}
ret := make([]byte, C.int(kSize))
copy(ret, C.GoBytes(unsafe.Pointer(ptr), C.int(kSize)))
return ret, nil
}
// Compile returns a compiled bytecode with given filename.
func (ctx *Context) CompileFile(filePath string, opts ...EvalOption) ([]byte, error) {
b, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
options := EvalOptions{}
for _, fn := range opts {
fn(&options)
}
if options.filename == "" {
opts = append(opts, EvalFileName(filePath))
}
return ctx.Compile(string(b), opts...)
}
// Global returns a context's global object.
func (ctx *Context) Globals() Value {
if ctx.globals == nil {
ctx.globals = &Value{
ctx: ctx,
ref: C.JS_GetGlobalObject(ctx.ref),
}
}
return *ctx.globals
}
// Throw returns a context's exception value.
func (ctx *Context) Throw(v Value) Value {
return Value{ctx: ctx, ref: C.JS_Throw(ctx.ref, v.ref)}
}
// ThrowError returns a context's exception value with given error message.
func (ctx *Context) ThrowError(err error) Value {
return ctx.Throw(ctx.Error(err))
}
// ThrowSyntaxError returns a context's exception value with given error message.
func (ctx *Context) ThrowSyntaxError(format string, args ...interface{}) Value {
cause := fmt.Sprintf(format, args...)
causePtr := C.CString(cause)
defer C.free(unsafe.Pointer(causePtr))
return Value{ctx: ctx, ref: C.ThrowSyntaxError(ctx.ref, causePtr)}
}
// ThrowTypeError returns a context's exception value with given error message.
func (ctx *Context) ThrowTypeError(format string, args ...interface{}) Value {
cause := fmt.Sprintf(format, args...)
causePtr := C.CString(cause)
defer C.free(unsafe.Pointer(causePtr))
return Value{ctx: ctx, ref: C.ThrowTypeError(ctx.ref, causePtr)}
}
// ThrowReferenceError returns a context's exception value with given error message.
func (ctx *Context) ThrowReferenceError(format string, args ...interface{}) Value {
cause := fmt.Sprintf(format, args...)
causePtr := C.CString(cause)
defer C.free(unsafe.Pointer(causePtr))
return Value{ctx: ctx, ref: C.ThrowReferenceError(ctx.ref, causePtr)}
}
// ThrowRangeError returns a context's exception value with given error message.
func (ctx *Context) ThrowRangeError(format string, args ...interface{}) Value {
cause := fmt.Sprintf(format, args...)
causePtr := C.CString(cause)
defer C.free(unsafe.Pointer(causePtr))
return Value{ctx: ctx, ref: C.ThrowRangeError(ctx.ref, causePtr)}
}
// ThrowInternalError returns a context's exception value with given error message.
func (ctx *Context) ThrowInternalError(format string, args ...interface{}) Value {
cause := fmt.Sprintf(format, args...)
causePtr := C.CString(cause)
defer C.free(unsafe.Pointer(causePtr))
return Value{ctx: ctx, ref: C.ThrowInternalError(ctx.ref, causePtr)}
}
// Exception returns a context's exception value.
func (ctx *Context) Exception() error {
val := Value{ctx: ctx, ref: C.JS_GetException(ctx.ref)}
defer val.Free()
return val.Error()
}
// Loop runs the context's event loop.
func (ctx *Context) Loop() {
C.js_std_loop(ctx.ref)
}
// Wait for a promise and execute pending jobs while waiting for it. Return the promise result or JS_EXCEPTION in case of promise rejection.
func (ctx *Context) Await(v Value) (Value, error) {
val := Value{ctx: ctx, ref: C.js_std_await(ctx.ref, v.ref)}
if val.IsException() {
return val, ctx.Exception()
}
return val, nil
}

347
deps/include/cutils.h vendored Normal file
View File

@ -0,0 +1,347 @@
/*
* C utilities
*
* Copyright (c) 2017 Fabrice Bellard
* Copyright (c) 2018 Charlie Gordon
*
* 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.
*/
#ifndef CUTILS_H
#define CUTILS_H
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define force_inline inline __attribute__((always_inline))
#define no_inline __attribute__((noinline))
#define __maybe_unused __attribute__((unused))
#define xglue(x, y) x ## y
#define glue(x, y) xglue(x, y)
#define stringify(s) tostring(s)
#define tostring(s) #s
#ifndef offsetof
#define offsetof(type, field) ((size_t) &((type *)0)->field)
#endif
#ifndef countof
#define countof(x) (sizeof(x) / sizeof((x)[0]))
#endif
#ifndef container_of
/* return the pointer of type 'type *' containing 'ptr' as field 'member' */
#define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member)))
#endif
#if !defined(_MSC_VER) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define minimum_length(n) static n
#else
#define minimum_length(n) n
#endif
typedef int BOOL;
#ifndef FALSE
enum {
FALSE = 0,
TRUE = 1,
};
#endif
void pstrcpy(char *buf, int buf_size, const char *str);
char *pstrcat(char *buf, int buf_size, const char *s);
int strstart(const char *str, const char *val, const char **ptr);
int has_suffix(const char *str, const char *suffix);
/* Prevent UB when n == 0 and (src == NULL or dest == NULL) */
static inline void memcpy_no_ub(void *dest, const void *src, size_t n) {
if (n)
memcpy(dest, src, n);
}
static inline int max_int(int a, int b)
{
if (a > b)
return a;
else
return b;
}
static inline int min_int(int a, int b)
{
if (a < b)
return a;
else
return b;
}
static inline uint32_t max_uint32(uint32_t a, uint32_t b)
{
if (a > b)
return a;
else
return b;
}
static inline uint32_t min_uint32(uint32_t a, uint32_t b)
{
if (a < b)
return a;
else
return b;
}
static inline int64_t max_int64(int64_t a, int64_t b)
{
if (a > b)
return a;
else
return b;
}
static inline int64_t min_int64(int64_t a, int64_t b)
{
if (a < b)
return a;
else
return b;
}
/* WARNING: undefined if a = 0 */
static inline int clz32(unsigned int a)
{
return __builtin_clz(a);
}
/* WARNING: undefined if a = 0 */
static inline int clz64(uint64_t a)
{
return __builtin_clzll(a);
}
/* WARNING: undefined if a = 0 */
static inline int ctz32(unsigned int a)
{
return __builtin_ctz(a);
}
/* WARNING: undefined if a = 0 */
static inline int ctz64(uint64_t a)
{
return __builtin_ctzll(a);
}
struct __attribute__((packed)) packed_u64 {
uint64_t v;
};
struct __attribute__((packed)) packed_u32 {
uint32_t v;
};
struct __attribute__((packed)) packed_u16 {
uint16_t v;
};
static inline uint64_t get_u64(const uint8_t *tab)
{
return ((const struct packed_u64 *)tab)->v;
}
static inline int64_t get_i64(const uint8_t *tab)
{
return (int64_t)((const struct packed_u64 *)tab)->v;
}
static inline void put_u64(uint8_t *tab, uint64_t val)
{
((struct packed_u64 *)tab)->v = val;
}
static inline uint32_t get_u32(const uint8_t *tab)
{
return ((const struct packed_u32 *)tab)->v;
}
static inline int32_t get_i32(const uint8_t *tab)
{
return (int32_t)((const struct packed_u32 *)tab)->v;
}
static inline void put_u32(uint8_t *tab, uint32_t val)
{
((struct packed_u32 *)tab)->v = val;
}
static inline uint32_t get_u16(const uint8_t *tab)
{
return ((const struct packed_u16 *)tab)->v;
}
static inline int32_t get_i16(const uint8_t *tab)
{
return (int16_t)((const struct packed_u16 *)tab)->v;
}
static inline void put_u16(uint8_t *tab, uint16_t val)
{
((struct packed_u16 *)tab)->v = val;
}
static inline uint32_t get_u8(const uint8_t *tab)
{
return *tab;
}
static inline int32_t get_i8(const uint8_t *tab)
{
return (int8_t)*tab;
}
static inline void put_u8(uint8_t *tab, uint8_t val)
{
*tab = val;
}
#ifndef bswap16
static inline uint16_t bswap16(uint16_t x)
{
return (x >> 8) | (x << 8);
}
#endif
#ifndef bswap32
static inline uint32_t bswap32(uint32_t v)
{
return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) |
((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24);
}
#endif
#ifndef bswap64
static inline uint64_t bswap64(uint64_t v)
{
return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) |
((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) |
((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) |
((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) |
((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) |
((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) |
((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) |
((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8));
}
#endif
/* XXX: should take an extra argument to pass slack information to the caller */
typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size);
typedef struct DynBuf {
uint8_t *buf;
size_t size;
size_t allocated_size;
BOOL error; /* true if a memory allocation error occurred */
DynBufReallocFunc *realloc_func;
void *opaque; /* for realloc_func */
} DynBuf;
void dbuf_init(DynBuf *s);
void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func);
int dbuf_realloc(DynBuf *s, size_t new_size);
int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len);
int dbuf_put(DynBuf *s, const uint8_t *data, size_t len);
int dbuf_put_self(DynBuf *s, size_t offset, size_t len);
int dbuf_putc(DynBuf *s, uint8_t c);
int dbuf_putstr(DynBuf *s, const char *str);
static inline int dbuf_put_u16(DynBuf *s, uint16_t val)
{
return dbuf_put(s, (uint8_t *)&val, 2);
}
static inline int dbuf_put_u32(DynBuf *s, uint32_t val)
{
return dbuf_put(s, (uint8_t *)&val, 4);
}
static inline int dbuf_put_u64(DynBuf *s, uint64_t val)
{
return dbuf_put(s, (uint8_t *)&val, 8);
}
int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s,
const char *fmt, ...);
void dbuf_free(DynBuf *s);
static inline BOOL dbuf_error(DynBuf *s) {
return s->error;
}
static inline void dbuf_set_error(DynBuf *s)
{
s->error = TRUE;
}
#define UTF8_CHAR_LEN_MAX 6
int unicode_to_utf8(uint8_t *buf, unsigned int c);
int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp);
static inline BOOL is_surrogate(uint32_t c)
{
return (c >> 11) == (0xD800 >> 11); // 0xD800-0xDFFF
}
static inline BOOL is_hi_surrogate(uint32_t c)
{
return (c >> 10) == (0xD800 >> 10); // 0xD800-0xDBFF
}
static inline BOOL is_lo_surrogate(uint32_t c)
{
return (c >> 10) == (0xDC00 >> 10); // 0xDC00-0xDFFF
}
static inline uint32_t get_hi_surrogate(uint32_t c)
{
return (c >> 10) - (0x10000 >> 10) + 0xD800;
}
static inline uint32_t get_lo_surrogate(uint32_t c)
{
return (c & 0x3FF) | 0xDC00;
}
static inline uint32_t from_surrogate(uint32_t hi, uint32_t lo)
{
return 0x10000 + 0x400 * (hi - 0xD800) + (lo - 0xDC00);
}
static inline int from_hex(int c)
{
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
else if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
else
return -1;
}
void rqsort(void *base, size_t nmemb, size_t size,
int (*cmp)(const void *, const void *, void *),
void *arg);
#endif /* CUTILS_H */

535
deps/include/libbf.h vendored Normal file
View File

@ -0,0 +1,535 @@
/*
* Tiny arbitrary precision floating point library
*
* Copyright (c) 2017-2021 Fabrice Bellard
*
* 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.
*/
#ifndef LIBBF_H
#define LIBBF_H
#include <stddef.h>
#include <stdint.h>
#if defined(__SIZEOF_INT128__) && (INTPTR_MAX >= INT64_MAX)
#define LIMB_LOG2_BITS 6
#else
#define LIMB_LOG2_BITS 5
#endif
#define LIMB_BITS (1 << LIMB_LOG2_BITS)
#if LIMB_BITS == 64
typedef __int128 int128_t;
typedef unsigned __int128 uint128_t;
typedef int64_t slimb_t;
typedef uint64_t limb_t;
typedef uint128_t dlimb_t;
#define BF_RAW_EXP_MIN INT64_MIN
#define BF_RAW_EXP_MAX INT64_MAX
#define LIMB_DIGITS 19
#define BF_DEC_BASE UINT64_C(10000000000000000000)
#else
typedef int32_t slimb_t;
typedef uint32_t limb_t;
typedef uint64_t dlimb_t;
#define BF_RAW_EXP_MIN INT32_MIN
#define BF_RAW_EXP_MAX INT32_MAX
#define LIMB_DIGITS 9
#define BF_DEC_BASE 1000000000U
#endif
/* in bits */
/* minimum number of bits for the exponent */
#define BF_EXP_BITS_MIN 3
/* maximum number of bits for the exponent */
#define BF_EXP_BITS_MAX (LIMB_BITS - 3)
/* extended range for exponent, used internally */
#define BF_EXT_EXP_BITS_MAX (BF_EXP_BITS_MAX + 1)
/* minimum possible precision */
#define BF_PREC_MIN 2
/* minimum possible precision */
#define BF_PREC_MAX (((limb_t)1 << (LIMB_BITS - 2)) - 2)
/* some operations support infinite precision */
#define BF_PREC_INF (BF_PREC_MAX + 1) /* infinite precision */
#if LIMB_BITS == 64
#define BF_CHKSUM_MOD (UINT64_C(975620677) * UINT64_C(9795002197))
#else
#define BF_CHKSUM_MOD 975620677U
#endif
#define BF_EXP_ZERO BF_RAW_EXP_MIN
#define BF_EXP_INF (BF_RAW_EXP_MAX - 1)
#define BF_EXP_NAN BF_RAW_EXP_MAX
/* +/-zero is represented with expn = BF_EXP_ZERO and len = 0,
+/-infinity is represented with expn = BF_EXP_INF and len = 0,
NaN is represented with expn = BF_EXP_NAN and len = 0 (sign is ignored)
*/
typedef struct {
struct bf_context_t *ctx;
int sign;
slimb_t expn;
limb_t len;
limb_t *tab;
} bf_t;
typedef struct {
/* must be kept identical to bf_t */
struct bf_context_t *ctx;
int sign;
slimb_t expn;
limb_t len;
limb_t *tab;
} bfdec_t;
typedef enum {
BF_RNDN, /* round to nearest, ties to even */
BF_RNDZ, /* round to zero */
BF_RNDD, /* round to -inf (the code relies on (BF_RNDD xor BF_RNDU) = 1) */
BF_RNDU, /* round to +inf */
BF_RNDNA, /* round to nearest, ties away from zero */
BF_RNDA, /* round away from zero */
BF_RNDF, /* faithful rounding (nondeterministic, either RNDD or RNDU,
inexact flag is always set) */
} bf_rnd_t;
/* allow subnormal numbers. Only available if the number of exponent
bits is <= BF_EXP_BITS_USER_MAX and prec != BF_PREC_INF. */
#define BF_FLAG_SUBNORMAL (1 << 3)
/* 'prec' is the precision after the radix point instead of the whole
mantissa. Can only be used with bf_round() and
bfdec_[add|sub|mul|div|sqrt|round](). */
#define BF_FLAG_RADPNT_PREC (1 << 4)
#define BF_RND_MASK 0x7
#define BF_EXP_BITS_SHIFT 5
#define BF_EXP_BITS_MASK 0x3f
/* shortcut for bf_set_exp_bits(BF_EXT_EXP_BITS_MAX) */
#define BF_FLAG_EXT_EXP (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)
/* contains the rounding mode and number of exponents bits */
typedef uint32_t bf_flags_t;
typedef void *bf_realloc_func_t(void *opaque, void *ptr, size_t size);
typedef struct {
bf_t val;
limb_t prec;
} BFConstCache;
typedef struct bf_context_t {
void *realloc_opaque;
bf_realloc_func_t *realloc_func;
BFConstCache log2_cache;
BFConstCache pi_cache;
struct BFNTTState *ntt_state;
} bf_context_t;
static inline int bf_get_exp_bits(bf_flags_t flags)
{
int e;
e = (flags >> BF_EXP_BITS_SHIFT) & BF_EXP_BITS_MASK;
if (e == BF_EXP_BITS_MASK)
return BF_EXP_BITS_MAX + 1;
else
return BF_EXP_BITS_MAX - e;
}
static inline bf_flags_t bf_set_exp_bits(int n)
{
return ((BF_EXP_BITS_MAX - n) & BF_EXP_BITS_MASK) << BF_EXP_BITS_SHIFT;
}
/* returned status */
#define BF_ST_INVALID_OP (1 << 0)
#define BF_ST_DIVIDE_ZERO (1 << 1)
#define BF_ST_OVERFLOW (1 << 2)
#define BF_ST_UNDERFLOW (1 << 3)
#define BF_ST_INEXACT (1 << 4)
/* indicate that a memory allocation error occured. NaN is returned */
#define BF_ST_MEM_ERROR (1 << 5)
#define BF_RADIX_MAX 36 /* maximum radix for bf_atof() and bf_ftoa() */
static inline slimb_t bf_max(slimb_t a, slimb_t b)
{
if (a > b)
return a;
else
return b;
}
static inline slimb_t bf_min(slimb_t a, slimb_t b)
{
if (a < b)
return a;
else
return b;
}
void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func,
void *realloc_opaque);
void bf_context_end(bf_context_t *s);
/* free memory allocated for the bf cache data */
void bf_clear_cache(bf_context_t *s);
static inline void *bf_realloc(bf_context_t *s, void *ptr, size_t size)
{
return s->realloc_func(s->realloc_opaque, ptr, size);
}
/* 'size' must be != 0 */
static inline void *bf_malloc(bf_context_t *s, size_t size)
{
return bf_realloc(s, NULL, size);
}
static inline void bf_free(bf_context_t *s, void *ptr)
{
/* must test ptr otherwise equivalent to malloc(0) */
if (ptr)
bf_realloc(s, ptr, 0);
}
void bf_init(bf_context_t *s, bf_t *r);
static inline void bf_delete(bf_t *r)
{
bf_context_t *s = r->ctx;
/* we accept to delete a zeroed bf_t structure */
if (s && r->tab) {
bf_realloc(s, r->tab, 0);
}
}
static inline void bf_neg(bf_t *r)
{
r->sign ^= 1;
}
static inline int bf_is_finite(const bf_t *a)
{
return (a->expn < BF_EXP_INF);
}
static inline int bf_is_nan(const bf_t *a)
{
return (a->expn == BF_EXP_NAN);
}
static inline int bf_is_zero(const bf_t *a)
{
return (a->expn == BF_EXP_ZERO);
}
static inline void bf_memcpy(bf_t *r, const bf_t *a)
{
*r = *a;
}
int bf_set_ui(bf_t *r, uint64_t a);
int bf_set_si(bf_t *r, int64_t a);
void bf_set_nan(bf_t *r);
void bf_set_zero(bf_t *r, int is_neg);
void bf_set_inf(bf_t *r, int is_neg);
int bf_set(bf_t *r, const bf_t *a);
void bf_move(bf_t *r, bf_t *a);
int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode);
int bf_set_float64(bf_t *a, double d);
int bf_cmpu(const bf_t *a, const bf_t *b);
int bf_cmp_full(const bf_t *a, const bf_t *b);
int bf_cmp(const bf_t *a, const bf_t *b);
static inline int bf_cmp_eq(const bf_t *a, const bf_t *b)
{
return bf_cmp(a, b) == 0;
}
static inline int bf_cmp_le(const bf_t *a, const bf_t *b)
{
return bf_cmp(a, b) <= 0;
}
static inline int bf_cmp_lt(const bf_t *a, const bf_t *b)
{
return bf_cmp(a, b) < 0;
}
int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, bf_flags_t flags);
int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, bf_flags_t flags);
int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec,
bf_flags_t flags);
int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags);
int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
#define BF_DIVREM_EUCLIDIAN BF_RNDF
int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b,
limb_t prec, bf_flags_t flags, int rnd_mode);
int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
bf_flags_t flags, int rnd_mode);
int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
bf_flags_t flags, int rnd_mode);
/* round to integer with infinite precision */
int bf_rint(bf_t *r, int rnd_mode);
int bf_round(bf_t *r, limb_t prec, bf_flags_t flags);
int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a);
int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
slimb_t bf_get_exp_min(const bf_t *a);
int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b);
int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b);
int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b);
/* additional flags for bf_atof */
/* do not accept hex radix prefix (0x or 0X) if radix = 0 or radix = 16 */
#define BF_ATOF_NO_HEX (1 << 16)
/* accept binary (0b or 0B) or octal (0o or 0O) radix prefix if radix = 0 */
#define BF_ATOF_BIN_OCT (1 << 17)
/* Do not parse NaN or Inf */
#define BF_ATOF_NO_NAN_INF (1 << 18)
/* return the exponent separately */
#define BF_ATOF_EXPONENT (1 << 19)
int bf_atof(bf_t *a, const char *str, const char **pnext, int radix,
limb_t prec, bf_flags_t flags);
/* this version accepts prec = BF_PREC_INF and returns the radix
exponent */
int bf_atof2(bf_t *r, slimb_t *pexponent,
const char *str, const char **pnext, int radix,
limb_t prec, bf_flags_t flags);
int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix,
slimb_t expn, limb_t prec, bf_flags_t flags);
/* Conversion of floating point number to string. Return a null
terminated string or NULL if memory error. *plen contains its
length if plen != NULL. The exponent letter is "e" for base 10,
"p" for bases 2, 8, 16 with a binary exponent and "@" for the other
bases. */
#define BF_FTOA_FORMAT_MASK (3 << 16)
/* fixed format: prec significant digits rounded with (flags &
BF_RND_MASK). Exponential notation is used if too many zeros are
needed.*/
#define BF_FTOA_FORMAT_FIXED (0 << 16)
/* fractional format: prec digits after the decimal point rounded with
(flags & BF_RND_MASK) */
#define BF_FTOA_FORMAT_FRAC (1 << 16)
/* free format:
For binary radices with bf_ftoa() and for bfdec_ftoa(): use the minimum
number of digits to represent 'a'. The precision and the rounding
mode are ignored.
For the non binary radices with bf_ftoa(): use as many digits as
necessary so that bf_atof() return the same number when using
precision 'prec', rounding to nearest and the subnormal
configuration of 'flags'. The result is meaningful only if 'a' is
already rounded to 'prec' bits. If the subnormal flag is set, the
exponent in 'flags' must also be set to the desired exponent range.
*/
#define BF_FTOA_FORMAT_FREE (2 << 16)
/* same as BF_FTOA_FORMAT_FREE but uses the minimum number of digits
(takes more computation time). Identical to BF_FTOA_FORMAT_FREE for
binary radices with bf_ftoa() and for bfdec_ftoa(). */
#define BF_FTOA_FORMAT_FREE_MIN (3 << 16)
/* force exponential notation for fixed or free format */
#define BF_FTOA_FORCE_EXP (1 << 20)
/* add 0x prefix for base 16, 0o prefix for base 8 or 0b prefix for
base 2 if non zero value */
#define BF_FTOA_ADD_PREFIX (1 << 21)
/* return "Infinity" instead of "Inf" and add a "+" for positive
exponents */
#define BF_FTOA_JS_QUIRKS (1 << 22)
char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec,
bf_flags_t flags);
/* modulo 2^n instead of saturation. NaN and infinity return 0 */
#define BF_GET_INT_MOD (1 << 0)
int bf_get_int32(int *pres, const bf_t *a, int flags);
int bf_get_int64(int64_t *pres, const bf_t *a, int flags);
int bf_get_uint64(uint64_t *pres, const bf_t *a);
/* the following functions are exported for testing only. */
void mp_print_str(const char *str, const limb_t *tab, limb_t n);
void bf_print_str(const char *str, const bf_t *a);
int bf_resize(bf_t *r, limb_t len);
int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len);
int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags);
int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k);
slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv,
int is_ceil1);
int mp_mul(bf_context_t *s, limb_t *result,
const limb_t *op1, limb_t op1_size,
const limb_t *op2, limb_t op2_size);
limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2,
limb_t n, limb_t carry);
limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n);
int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n);
int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n);
limb_t bf_isqrt(limb_t a);
/* transcendental functions */
int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags);
int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags);
int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
#define BF_POW_JS_QUIRKS (1 << 16) /* (+/-1)^(+/-Inf) = NaN, 1^NaN = NaN */
int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags);
int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x,
limb_t prec, bf_flags_t flags);
int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
/* decimal floating point */
static inline void bfdec_init(bf_context_t *s, bfdec_t *r)
{
bf_init(s, (bf_t *)r);
}
static inline void bfdec_delete(bfdec_t *r)
{
bf_delete((bf_t *)r);
}
static inline void bfdec_neg(bfdec_t *r)
{
r->sign ^= 1;
}
static inline int bfdec_is_finite(const bfdec_t *a)
{
return (a->expn < BF_EXP_INF);
}
static inline int bfdec_is_nan(const bfdec_t *a)
{
return (a->expn == BF_EXP_NAN);
}
static inline int bfdec_is_zero(const bfdec_t *a)
{
return (a->expn == BF_EXP_ZERO);
}
static inline void bfdec_memcpy(bfdec_t *r, const bfdec_t *a)
{
bf_memcpy((bf_t *)r, (const bf_t *)a);
}
int bfdec_set_ui(bfdec_t *r, uint64_t a);
int bfdec_set_si(bfdec_t *r, int64_t a);
static inline void bfdec_set_nan(bfdec_t *r)
{
bf_set_nan((bf_t *)r);
}
static inline void bfdec_set_zero(bfdec_t *r, int is_neg)
{
bf_set_zero((bf_t *)r, is_neg);
}
static inline void bfdec_set_inf(bfdec_t *r, int is_neg)
{
bf_set_inf((bf_t *)r, is_neg);
}
static inline int bfdec_set(bfdec_t *r, const bfdec_t *a)
{
return bf_set((bf_t *)r, (bf_t *)a);
}
static inline void bfdec_move(bfdec_t *r, bfdec_t *a)
{
bf_move((bf_t *)r, (bf_t *)a);
}
static inline int bfdec_cmpu(const bfdec_t *a, const bfdec_t *b)
{
return bf_cmpu((const bf_t *)a, (const bf_t *)b);
}
static inline int bfdec_cmp_full(const bfdec_t *a, const bfdec_t *b)
{
return bf_cmp_full((const bf_t *)a, (const bf_t *)b);
}
static inline int bfdec_cmp(const bfdec_t *a, const bfdec_t *b)
{
return bf_cmp((const bf_t *)a, (const bf_t *)b);
}
static inline int bfdec_cmp_eq(const bfdec_t *a, const bfdec_t *b)
{
return bfdec_cmp(a, b) == 0;
}
static inline int bfdec_cmp_le(const bfdec_t *a, const bfdec_t *b)
{
return bfdec_cmp(a, b) <= 0;
}
static inline int bfdec_cmp_lt(const bfdec_t *a, const bfdec_t *b)
{
return bfdec_cmp(a, b) < 0;
}
int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
bf_flags_t flags);
int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
bf_flags_t flags);
int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec,
bf_flags_t flags);
int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
bf_flags_t flags);
int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec,
bf_flags_t flags);
int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
bf_flags_t flags);
int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b,
limb_t prec, bf_flags_t flags, int rnd_mode);
int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
bf_flags_t flags, int rnd_mode);
int bfdec_rint(bfdec_t *r, int rnd_mode);
int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags);
int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags);
int bfdec_get_int32(int *pres, const bfdec_t *a);
int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b);
char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags);
int bfdec_atof(bfdec_t *r, const char *str, const char **pnext,
limb_t prec, bf_flags_t flags);
/* the following functions are exported for testing only. */
extern const limb_t mp_pow_dec[LIMB_DIGITS + 1];
void bfdec_print_str(const char *str, const bfdec_t *a);
static inline int bfdec_resize(bfdec_t *r, limb_t len)
{
return bf_resize((bf_t *)r, len);
}
int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags);
#endif /* LIBBF_H */

57
deps/include/libregexp-opcode.h vendored Normal file
View File

@ -0,0 +1,57 @@
/*
* Regular Expression Engine
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* 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.
*/
#ifdef DEF
DEF(invalid, 1) /* never used */
DEF(char, 3)
DEF(char32, 5)
DEF(dot, 1)
DEF(any, 1) /* same as dot but match any character including line terminator */
DEF(line_start, 1)
DEF(line_end, 1)
DEF(goto, 5)
DEF(split_goto_first, 5)
DEF(split_next_first, 5)
DEF(match, 1)
DEF(save_start, 2) /* save start position */
DEF(save_end, 2) /* save end position, must come after saved_start */
DEF(save_reset, 3) /* reset save positions */
DEF(loop, 5) /* decrement the top the stack and goto if != 0 */
DEF(push_i32, 5) /* push integer on the stack */
DEF(drop, 1)
DEF(word_boundary, 1)
DEF(not_word_boundary, 1)
DEF(back_reference, 2)
DEF(backward_back_reference, 2) /* must come after back_reference */
DEF(range, 3) /* variable length */
DEF(range32, 3) /* variable length */
DEF(lookahead, 5)
DEF(negative_lookahead, 5)
DEF(push_char_pos, 1) /* push the character position on the stack */
DEF(check_advance, 1) /* pop one stack element and check that it is different from the character position */
DEF(prev, 1) /* go to the previous char */
DEF(simple_greedy_quant, 17)
#endif /* DEF */

55
deps/include/libregexp.h vendored Normal file
View File

@ -0,0 +1,55 @@
/*
* Regular Expression Engine
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* 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.
*/
#ifndef LIBREGEXP_H
#define LIBREGEXP_H
#include <stddef.h>
#include <stdint.h>
#define LRE_FLAG_GLOBAL (1 << 0)
#define LRE_FLAG_IGNORECASE (1 << 1)
#define LRE_FLAG_MULTILINE (1 << 2)
#define LRE_FLAG_DOTALL (1 << 3)
#define LRE_FLAG_UNICODE (1 << 4)
#define LRE_FLAG_STICKY (1 << 5)
#define LRE_FLAG_INDICES (1 << 6) /* Unused by libregexp, just recorded. */
#define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */
uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
const char *buf, size_t buf_len, int re_flags,
void *opaque);
int lre_get_capture_count(const uint8_t *bc_buf);
int lre_get_flags(const uint8_t *bc_buf);
const char *lre_get_groupnames(const uint8_t *bc_buf);
int lre_exec(uint8_t **capture,
const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen,
int cbuf_type, void *opaque);
int lre_parse_escape(const uint8_t **pp, int allow_utf16);
/* must be provided by the user, return non zero if overflow */
int lre_check_stack_overflow(void *opaque, size_t alloca_size);
void *lre_realloc(void *opaque, void *ptr, size_t size);
#endif /* LIBREGEXP_H */

4557
deps/include/libunicode-table.h vendored Normal file

File diff suppressed because it is too large Load Diff

182
deps/include/libunicode.h vendored Normal file
View File

@ -0,0 +1,182 @@
/*
* Unicode utilities
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* 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.
*/
#ifndef LIBUNICODE_H
#define LIBUNICODE_H
#include <stdint.h>
/* define it to include all the unicode tables (40KB larger) */
#define CONFIG_ALL_UNICODE
#define LRE_CC_RES_LEN_MAX 3
/* char ranges */
typedef struct {
int len; /* in points, always even */
int size;
uint32_t *points; /* points sorted by increasing value */
void *mem_opaque;
void *(*realloc_func)(void *opaque, void *ptr, size_t size);
} CharRange;
typedef enum {
CR_OP_UNION,
CR_OP_INTER,
CR_OP_XOR,
} CharRangeOpEnum;
void cr_init(CharRange *cr, void *mem_opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size));
void cr_free(CharRange *cr);
int cr_realloc(CharRange *cr, int size);
int cr_copy(CharRange *cr, const CharRange *cr1);
static inline int cr_add_point(CharRange *cr, uint32_t v)
{
if (cr->len >= cr->size) {
if (cr_realloc(cr, cr->len + 1))
return -1;
}
cr->points[cr->len++] = v;
return 0;
}
static inline int cr_add_interval(CharRange *cr, uint32_t c1, uint32_t c2)
{
if ((cr->len + 2) > cr->size) {
if (cr_realloc(cr, cr->len + 2))
return -1;
}
cr->points[cr->len++] = c1;
cr->points[cr->len++] = c2;
return 0;
}
int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len);
static inline int cr_union_interval(CharRange *cr, uint32_t c1, uint32_t c2)
{
uint32_t b_pt[2];
b_pt[0] = c1;
b_pt[1] = c2 + 1;
return cr_union1(cr, b_pt, 2);
}
int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len,
const uint32_t *b_pt, int b_len, int op);
int cr_invert(CharRange *cr);
int cr_regexp_canonicalize(CharRange *cr, int is_unicode);
typedef enum {
UNICODE_NFC,
UNICODE_NFD,
UNICODE_NFKC,
UNICODE_NFKD,
} UnicodeNormalizationEnum;
int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len,
UnicodeNormalizationEnum n_type,
void *opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size));
/* Unicode character range functions */
int unicode_script(CharRange *cr, const char *script_name, int is_ext);
int unicode_general_category(CharRange *cr, const char *gc_name);
int unicode_prop(CharRange *cr, const char *prop_name);
int lre_case_conv(uint32_t *res, uint32_t c, int conv_type);
int lre_canonicalize(uint32_t c, int is_unicode);
/* Code point type categories */
enum {
UNICODE_C_SPACE = (1 << 0),
UNICODE_C_DIGIT = (1 << 1),
UNICODE_C_UPPER = (1 << 2),
UNICODE_C_LOWER = (1 << 3),
UNICODE_C_UNDER = (1 << 4),
UNICODE_C_DOLLAR = (1 << 5),
UNICODE_C_XDIGIT = (1 << 6),
};
extern uint8_t const lre_ctype_bits[256];
/* zero or non-zero return value */
int lre_is_cased(uint32_t c);
int lre_is_case_ignorable(uint32_t c);
int lre_is_id_start(uint32_t c);
int lre_is_id_continue(uint32_t c);
static inline int lre_is_space_byte(uint8_t c) {
return lre_ctype_bits[c] & UNICODE_C_SPACE;
}
static inline int lre_is_id_start_byte(uint8_t c) {
return lre_ctype_bits[c] & (UNICODE_C_UPPER | UNICODE_C_LOWER |
UNICODE_C_UNDER | UNICODE_C_DOLLAR);
}
static inline int lre_is_id_continue_byte(uint8_t c) {
return lre_ctype_bits[c] & (UNICODE_C_UPPER | UNICODE_C_LOWER |
UNICODE_C_UNDER | UNICODE_C_DOLLAR |
UNICODE_C_DIGIT);
}
int lre_is_space_non_ascii(uint32_t c);
static inline int lre_is_space(uint32_t c) {
if (c < 256)
return lre_is_space_byte(c);
else
return lre_is_space_non_ascii(c);
}
static inline int lre_js_is_ident_first(uint32_t c) {
if (c < 128) {
return lre_is_id_start_byte(c);
} else {
#ifdef CONFIG_ALL_UNICODE
return lre_is_id_start(c);
#else
return !lre_is_space_non_ascii(c);
#endif
}
}
static inline int lre_js_is_ident_next(uint32_t c) {
if (c < 128) {
return lre_is_id_continue_byte(c);
} else {
/* ZWNJ and ZWJ are accepted in identifiers */
if (c >= 0x200C && c <= 0x200D)
return TRUE;
#ifdef CONFIG_ALL_UNICODE
return lre_is_id_continue(c);
#else
return !lre_is_space_non_ascii(c);
#endif
}
}
#endif /* LIBUNICODE_H */

99
deps/include/list.h vendored Normal file
View File

@ -0,0 +1,99 @@
/*
* Linux klist like system
*
* Copyright (c) 2016-2017 Fabrice Bellard
*
* 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.
*/
#ifndef LIST_H
#define LIST_H
#ifndef NULL
#include <stddef.h>
#endif
struct list_head {
struct list_head *prev;
struct list_head *next;
};
#define LIST_HEAD_INIT(el) { &(el), &(el) }
/* return the pointer of type 'type *' containing 'el' as field 'member' */
#define list_entry(el, type, member) container_of(el, type, member)
static inline void init_list_head(struct list_head *head)
{
head->prev = head;
head->next = head;
}
/* insert 'el' between 'prev' and 'next' */
static inline void __list_add(struct list_head *el,
struct list_head *prev, struct list_head *next)
{
prev->next = el;
el->prev = prev;
el->next = next;
next->prev = el;
}
/* add 'el' at the head of the list 'head' (= after element head) */
static inline void list_add(struct list_head *el, struct list_head *head)
{
__list_add(el, head, head->next);
}
/* add 'el' at the end of the list 'head' (= before element head) */
static inline void list_add_tail(struct list_head *el, struct list_head *head)
{
__list_add(el, head->prev, head);
}
static inline void list_del(struct list_head *el)
{
struct list_head *prev, *next;
prev = el->prev;
next = el->next;
prev->next = next;
next->prev = prev;
el->prev = NULL; /* fail safe */
el->next = NULL; /* fail safe */
}
static inline int list_empty(struct list_head *el)
{
return el->next == el;
}
#define list_for_each(el, head) \
for(el = (head)->next; el != (head); el = el->next)
#define list_for_each_safe(el, el1, head) \
for(el = (head)->next, el1 = el->next; el != (head); \
el = el1, el1 = el->next)
#define list_for_each_prev(el, head) \
for(el = (head)->prev; el != (head); el = el->prev)
#define list_for_each_prev_safe(el, el1, head) \
for(el = (head)->prev, el1 = el->prev; el != (head); \
el = el1, el1 = el->prev)
#endif /* LIST_H */

273
deps/include/quickjs-atom.h vendored Normal file
View File

@ -0,0 +1,273 @@
/*
* QuickJS atom definitions
*
* Copyright (c) 2017-2018 Fabrice Bellard
* Copyright (c) 2017-2018 Charlie Gordon
*
* 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.
*/
#ifdef DEF
/* Note: first atoms are considered as keywords in the parser */
DEF(null, "null") /* must be first */
DEF(false, "false")
DEF(true, "true")
DEF(if, "if")
DEF(else, "else")
DEF(return, "return")
DEF(var, "var")
DEF(this, "this")
DEF(delete, "delete")
DEF(void, "void")
DEF(typeof, "typeof")
DEF(new, "new")
DEF(in, "in")
DEF(instanceof, "instanceof")
DEF(do, "do")
DEF(while, "while")
DEF(for, "for")
DEF(break, "break")
DEF(continue, "continue")
DEF(switch, "switch")
DEF(case, "case")
DEF(default, "default")
DEF(throw, "throw")
DEF(try, "try")
DEF(catch, "catch")
DEF(finally, "finally")
DEF(function, "function")
DEF(debugger, "debugger")
DEF(with, "with")
/* FutureReservedWord */
DEF(class, "class")
DEF(const, "const")
DEF(enum, "enum")
DEF(export, "export")
DEF(extends, "extends")
DEF(import, "import")
DEF(super, "super")
/* FutureReservedWords when parsing strict mode code */
DEF(implements, "implements")
DEF(interface, "interface")
DEF(let, "let")
DEF(package, "package")
DEF(private, "private")
DEF(protected, "protected")
DEF(public, "public")
DEF(static, "static")
DEF(yield, "yield")
DEF(await, "await")
/* empty string */
DEF(empty_string, "")
/* identifiers */
DEF(length, "length")
DEF(fileName, "fileName")
DEF(lineNumber, "lineNumber")
DEF(message, "message")
DEF(cause, "cause")
DEF(errors, "errors")
DEF(stack, "stack")
DEF(name, "name")
DEF(toString, "toString")
DEF(toLocaleString, "toLocaleString")
DEF(valueOf, "valueOf")
DEF(eval, "eval")
DEF(prototype, "prototype")
DEF(constructor, "constructor")
DEF(configurable, "configurable")
DEF(writable, "writable")
DEF(enumerable, "enumerable")
DEF(value, "value")
DEF(get, "get")
DEF(set, "set")
DEF(of, "of")
DEF(__proto__, "__proto__")
DEF(undefined, "undefined")
DEF(number, "number")
DEF(boolean, "boolean")
DEF(string, "string")
DEF(object, "object")
DEF(symbol, "symbol")
DEF(integer, "integer")
DEF(unknown, "unknown")
DEF(arguments, "arguments")
DEF(callee, "callee")
DEF(caller, "caller")
DEF(_eval_, "<eval>")
DEF(_ret_, "<ret>")
DEF(_var_, "<var>")
DEF(_arg_var_, "<arg_var>")
DEF(_with_, "<with>")
DEF(lastIndex, "lastIndex")
DEF(target, "target")
DEF(index, "index")
DEF(input, "input")
DEF(defineProperties, "defineProperties")
DEF(apply, "apply")
DEF(join, "join")
DEF(concat, "concat")
DEF(split, "split")
DEF(construct, "construct")
DEF(getPrototypeOf, "getPrototypeOf")
DEF(setPrototypeOf, "setPrototypeOf")
DEF(isExtensible, "isExtensible")
DEF(preventExtensions, "preventExtensions")
DEF(has, "has")
DEF(deleteProperty, "deleteProperty")
DEF(defineProperty, "defineProperty")
DEF(getOwnPropertyDescriptor, "getOwnPropertyDescriptor")
DEF(ownKeys, "ownKeys")
DEF(add, "add")
DEF(done, "done")
DEF(next, "next")
DEF(values, "values")
DEF(source, "source")
DEF(flags, "flags")
DEF(global, "global")
DEF(unicode, "unicode")
DEF(raw, "raw")
DEF(new_target, "new.target")
DEF(this_active_func, "this.active_func")
DEF(home_object, "<home_object>")
DEF(computed_field, "<computed_field>")
DEF(static_computed_field, "<static_computed_field>") /* must come after computed_fields */
DEF(class_fields_init, "<class_fields_init>")
DEF(brand, "<brand>")
DEF(hash_constructor, "#constructor")
DEF(as, "as")
DEF(from, "from")
DEF(meta, "meta")
DEF(_default_, "*default*")
DEF(_star_, "*")
DEF(Module, "Module")
DEF(then, "then")
DEF(resolve, "resolve")
DEF(reject, "reject")
DEF(promise, "promise")
DEF(proxy, "proxy")
DEF(revoke, "revoke")
DEF(async, "async")
DEF(exec, "exec")
DEF(groups, "groups")
DEF(indices, "indices")
DEF(status, "status")
DEF(reason, "reason")
DEF(globalThis, "globalThis")
DEF(bigint, "bigint")
#ifdef CONFIG_BIGNUM
DEF(bigfloat, "bigfloat")
DEF(bigdecimal, "bigdecimal")
DEF(roundingMode, "roundingMode")
DEF(maximumSignificantDigits, "maximumSignificantDigits")
DEF(maximumFractionDigits, "maximumFractionDigits")
#endif
/* the following 3 atoms are only used with CONFIG_ATOMICS */
DEF(not_equal, "not-equal")
DEF(timed_out, "timed-out")
DEF(ok, "ok")
/* */
DEF(toJSON, "toJSON")
/* class names */
DEF(Object, "Object")
DEF(Array, "Array")
DEF(Error, "Error")
DEF(Number, "Number")
DEF(String, "String")
DEF(Boolean, "Boolean")
DEF(Symbol, "Symbol")
DEF(Arguments, "Arguments")
DEF(Math, "Math")
DEF(JSON, "JSON")
DEF(Date, "Date")
DEF(Function, "Function")
DEF(GeneratorFunction, "GeneratorFunction")
DEF(ForInIterator, "ForInIterator")
DEF(RegExp, "RegExp")
DEF(ArrayBuffer, "ArrayBuffer")
DEF(SharedArrayBuffer, "SharedArrayBuffer")
/* must keep same order as class IDs for typed arrays */
DEF(Uint8ClampedArray, "Uint8ClampedArray")
DEF(Int8Array, "Int8Array")
DEF(Uint8Array, "Uint8Array")
DEF(Int16Array, "Int16Array")
DEF(Uint16Array, "Uint16Array")
DEF(Int32Array, "Int32Array")
DEF(Uint32Array, "Uint32Array")
DEF(BigInt64Array, "BigInt64Array")
DEF(BigUint64Array, "BigUint64Array")
DEF(Float32Array, "Float32Array")
DEF(Float64Array, "Float64Array")
DEF(DataView, "DataView")
DEF(BigInt, "BigInt")
#ifdef CONFIG_BIGNUM
DEF(BigFloat, "BigFloat")
DEF(BigFloatEnv, "BigFloatEnv")
DEF(BigDecimal, "BigDecimal")
DEF(OperatorSet, "OperatorSet")
DEF(Operators, "Operators")
#endif
DEF(Map, "Map")
DEF(Set, "Set") /* Map + 1 */
DEF(WeakMap, "WeakMap") /* Map + 2 */
DEF(WeakSet, "WeakSet") /* Map + 3 */
DEF(Map_Iterator, "Map Iterator")
DEF(Set_Iterator, "Set Iterator")
DEF(Array_Iterator, "Array Iterator")
DEF(String_Iterator, "String Iterator")
DEF(RegExp_String_Iterator, "RegExp String Iterator")
DEF(Generator, "Generator")
DEF(Proxy, "Proxy")
DEF(Promise, "Promise")
DEF(PromiseResolveFunction, "PromiseResolveFunction")
DEF(PromiseRejectFunction, "PromiseRejectFunction")
DEF(AsyncFunction, "AsyncFunction")
DEF(AsyncFunctionResolve, "AsyncFunctionResolve")
DEF(AsyncFunctionReject, "AsyncFunctionReject")
DEF(AsyncGeneratorFunction, "AsyncGeneratorFunction")
DEF(AsyncGenerator, "AsyncGenerator")
DEF(EvalError, "EvalError")
DEF(RangeError, "RangeError")
DEF(ReferenceError, "ReferenceError")
DEF(SyntaxError, "SyntaxError")
DEF(TypeError, "TypeError")
DEF(URIError, "URIError")
DEF(InternalError, "InternalError")
/* private symbols */
DEF(Private_brand, "<brand>")
/* symbols */
DEF(Symbol_toPrimitive, "Symbol.toPrimitive")
DEF(Symbol_iterator, "Symbol.iterator")
DEF(Symbol_match, "Symbol.match")
DEF(Symbol_matchAll, "Symbol.matchAll")
DEF(Symbol_replace, "Symbol.replace")
DEF(Symbol_search, "Symbol.search")
DEF(Symbol_split, "Symbol.split")
DEF(Symbol_toStringTag, "Symbol.toStringTag")
DEF(Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable")
DEF(Symbol_hasInstance, "Symbol.hasInstance")
DEF(Symbol_species, "Symbol.species")
DEF(Symbol_unscopables, "Symbol.unscopables")
DEF(Symbol_asyncIterator, "Symbol.asyncIterator")
#ifdef CONFIG_BIGNUM
DEF(Symbol_operatorSet, "Symbol.operatorSet")
#endif
#endif /* DEF */

60
deps/include/quickjs-libc.h vendored Normal file
View File

@ -0,0 +1,60 @@
/*
* QuickJS C library
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* 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.
*/
#ifndef QUICKJS_LIBC_H
#define QUICKJS_LIBC_H
#include <stdio.h>
#include <stdlib.h>
#include "quickjs.h"
#ifdef __cplusplus
extern "C" {
#endif
JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name);
JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name);
void js_std_add_helpers(JSContext *ctx, int argc, char **argv);
void js_std_loop(JSContext *ctx);
JSValue js_std_await(JSContext *ctx, JSValue obj);
void js_std_init_handlers(JSRuntime *rt);
void js_std_free_handlers(JSRuntime *rt);
void js_std_dump_error(JSContext *ctx);
uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename);
int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val,
JS_BOOL use_realpath, JS_BOOL is_main);
JSModuleDef *js_module_loader(JSContext *ctx,
const char *module_name, void *opaque);
void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len,
int flags);
void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise,
JSValueConst reason,
JS_BOOL is_handled, void *opaque);
void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt));
#ifdef __cplusplus
} /* extern "C" { */
#endif
#endif /* QUICKJS_LIBC_H */

372
deps/include/quickjs-opcode.h vendored Normal file
View File

@ -0,0 +1,372 @@
/*
* QuickJS opcode definitions
*
* Copyright (c) 2017-2018 Fabrice Bellard
* Copyright (c) 2017-2018 Charlie Gordon
*
* 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.
*/
#ifdef FMT
FMT(none)
FMT(none_int)
FMT(none_loc)
FMT(none_arg)
FMT(none_var_ref)
FMT(u8)
FMT(i8)
FMT(loc8)
FMT(const8)
FMT(label8)
FMT(u16)
FMT(i16)
FMT(label16)
FMT(npop)
FMT(npopx)
FMT(npop_u16)
FMT(loc)
FMT(arg)
FMT(var_ref)
FMT(u32)
FMT(i32)
FMT(const)
FMT(label)
FMT(atom)
FMT(atom_u8)
FMT(atom_u16)
FMT(atom_label_u8)
FMT(atom_label_u16)
FMT(label_u16)
#undef FMT
#endif /* FMT */
#ifdef DEF
#ifndef def
#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f)
#endif
DEF(invalid, 1, 0, 0, none) /* never emitted */
/* push values */
DEF( push_i32, 5, 0, 1, i32)
DEF( push_const, 5, 0, 1, const)
DEF( fclosure, 5, 0, 1, const) /* must follow push_const */
DEF(push_atom_value, 5, 0, 1, atom)
DEF( private_symbol, 5, 0, 1, atom)
DEF( undefined, 1, 0, 1, none)
DEF( null, 1, 0, 1, none)
DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */
DEF( push_false, 1, 0, 1, none)
DEF( push_true, 1, 0, 1, none)
DEF( object, 1, 0, 1, none)
DEF( special_object, 2, 0, 1, u8) /* only used at the start of a function */
DEF( rest, 3, 0, 1, u16) /* only used at the start of a function */
DEF( drop, 1, 1, 0, none) /* a -> */
DEF( nip, 1, 2, 1, none) /* a b -> b */
DEF( nip1, 1, 3, 2, none) /* a b c -> b c */
DEF( dup, 1, 1, 2, none) /* a -> a a */
DEF( dup1, 1, 2, 3, none) /* a b -> a a b */
DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */
DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */
DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */
DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */
DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */
DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */
DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */
DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */
DEF( swap, 1, 2, 2, none) /* a b -> b a */
DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */
DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */
DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */
DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */
DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */
DEF(call_constructor, 3, 2, 1, npop) /* func new.target args -> ret. arguments are not counted in n_pop */
DEF( call, 3, 1, 1, npop) /* arguments are not counted in n_pop */
DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */
DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */
DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */
DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */
DEF( apply, 3, 3, 1, u16)
DEF( return, 1, 1, 0, none)
DEF( return_undef, 1, 0, 0, none)
DEF(check_ctor_return, 1, 1, 2, none)
DEF( check_ctor, 1, 0, 0, none)
DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */
DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */
DEF( return_async, 1, 1, 0, none)
DEF( throw, 1, 1, 0, none)
DEF( throw_error, 6, 0, 0, atom_u8)
DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */
DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */
DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a
bytecode string */
DEF( get_super, 1, 1, 1, none)
DEF( import, 1, 1, 1, none) /* dynamic module import */
DEF( check_var, 5, 0, 1, atom) /* check if a variable exists */
DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */
DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */
DEF( put_var, 5, 1, 0, atom) /* must come after get_var */
DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */
DEF( put_var_strict, 5, 2, 0, atom) /* for strict mode variable write */
DEF( get_ref_value, 1, 2, 3, none)
DEF( put_ref_value, 1, 3, 0, none)
DEF( define_var, 6, 0, 0, atom_u8)
DEF(check_define_var, 6, 0, 0, atom_u8)
DEF( define_func, 6, 1, 0, atom_u8)
DEF( get_field, 5, 1, 1, atom)
DEF( get_field2, 5, 1, 2, atom)
DEF( put_field, 5, 2, 0, atom)
DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */
DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */
DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */
DEF( get_array_el, 1, 2, 1, none)
DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */
DEF( put_array_el, 1, 3, 0, none)
DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */
DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */
DEF( define_field, 5, 2, 1, atom)
DEF( set_name, 5, 1, 1, atom)
DEF(set_name_computed, 1, 2, 2, none)
DEF( set_proto, 1, 2, 1, none)
DEF(set_home_object, 1, 2, 2, none)
DEF(define_array_el, 1, 3, 2, none)
DEF( append, 1, 3, 2, none) /* append enumerated object, update length */
DEF(copy_data_properties, 2, 3, 3, u8)
DEF( define_method, 6, 2, 1, atom_u8)
DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */
DEF( define_class, 6, 2, 2, atom_u8) /* parent ctor -> ctor proto */
DEF( define_class_computed, 6, 3, 3, atom_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */
DEF( get_loc, 3, 0, 1, loc)
DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */
DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */
DEF( get_arg, 3, 0, 1, arg)
DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */
DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */
DEF( get_var_ref, 3, 0, 1, var_ref)
DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */
DEF( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */
DEF(set_loc_uninitialized, 3, 0, 0, loc)
DEF( get_loc_check, 3, 0, 1, loc)
DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */
DEF( put_loc_check_init, 3, 1, 0, loc)
DEF(get_loc_checkthis, 3, 0, 1, loc)
DEF(get_var_ref_check, 3, 0, 1, var_ref)
DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */
DEF(put_var_ref_check_init, 3, 1, 0, var_ref)
DEF( close_loc, 3, 0, 0, loc)
DEF( if_false, 5, 1, 0, label)
DEF( if_true, 5, 1, 0, label) /* must come after if_false */
DEF( goto, 5, 0, 0, label) /* must come after if_true */
DEF( catch, 5, 0, 1, label)
DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */
DEF( ret, 1, 1, 0, none) /* used to return from the finally block */
DEF( nip_catch, 1, 2, 1, none) /* catch ... a -> a */
DEF( to_object, 1, 1, 1, none)
//DEF( to_string, 1, 1, 1, none)
DEF( to_propkey, 1, 1, 1, none)
DEF( to_propkey2, 1, 2, 2, none)
DEF( with_get_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF( with_put_var, 10, 2, 1, atom_label_u8) /* must be in the same order as scope_xxx */
DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF(with_get_ref_undef, 10, 1, 0, atom_label_u8)
DEF( make_loc_ref, 7, 0, 2, atom_u16)
DEF( make_arg_ref, 7, 0, 2, atom_u16)
DEF(make_var_ref_ref, 7, 0, 2, atom_u16)
DEF( make_var_ref, 5, 0, 2, atom)
DEF( for_in_start, 1, 1, 1, none)
DEF( for_of_start, 1, 1, 3, none)
DEF(for_await_of_start, 1, 1, 3, none)
DEF( for_in_next, 1, 1, 3, none)
DEF( for_of_next, 2, 3, 5, u8)
DEF(iterator_check_object, 1, 1, 1, none)
DEF(iterator_get_value_done, 1, 1, 2, none)
DEF( iterator_close, 1, 3, 0, none)
DEF( iterator_next, 1, 4, 4, none)
DEF( iterator_call, 2, 4, 5, u8)
DEF( initial_yield, 1, 0, 0, none)
DEF( yield, 1, 1, 2, none)
DEF( yield_star, 1, 1, 2, none)
DEF(async_yield_star, 1, 1, 2, none)
DEF( await, 1, 1, 1, none)
/* arithmetic/logic operations */
DEF( neg, 1, 1, 1, none)
DEF( plus, 1, 1, 1, none)
DEF( dec, 1, 1, 1, none)
DEF( inc, 1, 1, 1, none)
DEF( post_dec, 1, 1, 2, none)
DEF( post_inc, 1, 1, 2, none)
DEF( dec_loc, 2, 0, 0, loc8)
DEF( inc_loc, 2, 0, 0, loc8)
DEF( add_loc, 2, 1, 0, loc8)
DEF( not, 1, 1, 1, none)
DEF( lnot, 1, 1, 1, none)
DEF( typeof, 1, 1, 1, none)
DEF( delete, 1, 2, 1, none)
DEF( delete_var, 5, 0, 1, atom)
DEF( mul, 1, 2, 1, none)
DEF( div, 1, 2, 1, none)
DEF( mod, 1, 2, 1, none)
DEF( add, 1, 2, 1, none)
DEF( sub, 1, 2, 1, none)
DEF( pow, 1, 2, 1, none)
DEF( shl, 1, 2, 1, none)
DEF( sar, 1, 2, 1, none)
DEF( shr, 1, 2, 1, none)
DEF( lt, 1, 2, 1, none)
DEF( lte, 1, 2, 1, none)
DEF( gt, 1, 2, 1, none)
DEF( gte, 1, 2, 1, none)
DEF( instanceof, 1, 2, 1, none)
DEF( in, 1, 2, 1, none)
DEF( eq, 1, 2, 1, none)
DEF( neq, 1, 2, 1, none)
DEF( strict_eq, 1, 2, 1, none)
DEF( strict_neq, 1, 2, 1, none)
DEF( and, 1, 2, 1, none)
DEF( xor, 1, 2, 1, none)
DEF( or, 1, 2, 1, none)
DEF(is_undefined_or_null, 1, 1, 1, none)
DEF( private_in, 1, 2, 1, none)
#ifdef CONFIG_BIGNUM
DEF( mul_pow10, 1, 2, 1, none)
DEF( math_mod, 1, 2, 1, none)
#endif
/* must be the last non short and non temporary opcode */
DEF( nop, 1, 0, 0, none)
/* temporary opcodes: never emitted in the final bytecode */
def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */
def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */
def( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */
/* the following opcodes must be in the same order as the 'with_x' and
get_var_undef, get_var and put_var opcodes */
def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_get_var_checkthis, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2, only used to return 'this' in derived class constructors */
def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */
def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */
def(scope_put_private_field, 7, 2, 0, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */
def(scope_in_private_field, 7, 1, 1, atom_u16) /* obj -> res emitted in phase 1, removed in phase 2 */
def(get_field_opt_chain, 5, 1, 1, atom) /* emitted in phase 1, removed in phase 2 */
def(get_array_el_opt_chain, 1, 2, 1, none) /* emitted in phase 1, removed in phase 2 */
def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */
def( line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */
#if SHORT_OPCODES
DEF( push_minus1, 1, 0, 1, none_int)
DEF( push_0, 1, 0, 1, none_int)
DEF( push_1, 1, 0, 1, none_int)
DEF( push_2, 1, 0, 1, none_int)
DEF( push_3, 1, 0, 1, none_int)
DEF( push_4, 1, 0, 1, none_int)
DEF( push_5, 1, 0, 1, none_int)
DEF( push_6, 1, 0, 1, none_int)
DEF( push_7, 1, 0, 1, none_int)
DEF( push_i8, 2, 0, 1, i8)
DEF( push_i16, 3, 0, 1, i16)
DEF( push_const8, 2, 0, 1, const8)
DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */
DEF(push_empty_string, 1, 0, 1, none)
DEF( get_loc8, 2, 0, 1, loc8)
DEF( put_loc8, 2, 1, 0, loc8)
DEF( set_loc8, 2, 1, 1, loc8)
DEF( get_loc0, 1, 0, 1, none_loc)
DEF( get_loc1, 1, 0, 1, none_loc)
DEF( get_loc2, 1, 0, 1, none_loc)
DEF( get_loc3, 1, 0, 1, none_loc)
DEF( put_loc0, 1, 1, 0, none_loc)
DEF( put_loc1, 1, 1, 0, none_loc)
DEF( put_loc2, 1, 1, 0, none_loc)
DEF( put_loc3, 1, 1, 0, none_loc)
DEF( set_loc0, 1, 1, 1, none_loc)
DEF( set_loc1, 1, 1, 1, none_loc)
DEF( set_loc2, 1, 1, 1, none_loc)
DEF( set_loc3, 1, 1, 1, none_loc)
DEF( get_arg0, 1, 0, 1, none_arg)
DEF( get_arg1, 1, 0, 1, none_arg)
DEF( get_arg2, 1, 0, 1, none_arg)
DEF( get_arg3, 1, 0, 1, none_arg)
DEF( put_arg0, 1, 1, 0, none_arg)
DEF( put_arg1, 1, 1, 0, none_arg)
DEF( put_arg2, 1, 1, 0, none_arg)
DEF( put_arg3, 1, 1, 0, none_arg)
DEF( set_arg0, 1, 1, 1, none_arg)
DEF( set_arg1, 1, 1, 1, none_arg)
DEF( set_arg2, 1, 1, 1, none_arg)
DEF( set_arg3, 1, 1, 1, none_arg)
DEF( get_var_ref0, 1, 0, 1, none_var_ref)
DEF( get_var_ref1, 1, 0, 1, none_var_ref)
DEF( get_var_ref2, 1, 0, 1, none_var_ref)
DEF( get_var_ref3, 1, 0, 1, none_var_ref)
DEF( put_var_ref0, 1, 1, 0, none_var_ref)
DEF( put_var_ref1, 1, 1, 0, none_var_ref)
DEF( put_var_ref2, 1, 1, 0, none_var_ref)
DEF( put_var_ref3, 1, 1, 0, none_var_ref)
DEF( set_var_ref0, 1, 1, 1, none_var_ref)
DEF( set_var_ref1, 1, 1, 1, none_var_ref)
DEF( set_var_ref2, 1, 1, 1, none_var_ref)
DEF( set_var_ref3, 1, 1, 1, none_var_ref)
DEF( get_length, 1, 1, 1, none)
DEF( if_false8, 2, 1, 0, label8)
DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */
DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */
DEF( goto16, 3, 0, 0, label16)
DEF( call0, 1, 1, 1, npopx)
DEF( call1, 1, 1, 1, npopx)
DEF( call2, 1, 1, 1, npopx)
DEF( call3, 1, 1, 1, npopx)
DEF( is_undefined, 1, 1, 1, none)
DEF( is_null, 1, 1, 1, none)
DEF(typeof_is_undefined, 1, 1, 1, none)
DEF( typeof_is_function, 1, 1, 1, none)
#endif
#undef DEF
#undef def
#endif /* DEF */

1087
deps/include/quickjs.h vendored Normal file

File diff suppressed because it is too large Load Diff

291
deps/include/unicode_gen_def.h vendored Normal file
View File

@ -0,0 +1,291 @@
#ifdef UNICODE_GENERAL_CATEGORY
DEF(Cn, "Unassigned") /* must be zero */
DEF(Lu, "Uppercase_Letter")
DEF(Ll, "Lowercase_Letter")
DEF(Lt, "Titlecase_Letter")
DEF(Lm, "Modifier_Letter")
DEF(Lo, "Other_Letter")
DEF(Mn, "Nonspacing_Mark")
DEF(Mc, "Spacing_Mark")
DEF(Me, "Enclosing_Mark")
DEF(Nd, "Decimal_Number,digit")
DEF(Nl, "Letter_Number")
DEF(No, "Other_Number")
DEF(Sm, "Math_Symbol")
DEF(Sc, "Currency_Symbol")
DEF(Sk, "Modifier_Symbol")
DEF(So, "Other_Symbol")
DEF(Pc, "Connector_Punctuation")
DEF(Pd, "Dash_Punctuation")
DEF(Ps, "Open_Punctuation")
DEF(Pe, "Close_Punctuation")
DEF(Pi, "Initial_Punctuation")
DEF(Pf, "Final_Punctuation")
DEF(Po, "Other_Punctuation")
DEF(Zs, "Space_Separator")
DEF(Zl, "Line_Separator")
DEF(Zp, "Paragraph_Separator")
DEF(Cc, "Control,cntrl")
DEF(Cf, "Format")
DEF(Cs, "Surrogate")
DEF(Co, "Private_Use")
/* synthetic properties */
DEF(LC, "Cased_Letter")
DEF(L, "Letter")
DEF(M, "Mark,Combining_Mark")
DEF(N, "Number")
DEF(S, "Symbol")
DEF(P, "Punctuation,punct")
DEF(Z, "Separator")
DEF(C, "Other")
#endif
#ifdef UNICODE_SCRIPT
/* scripts aliases names in PropertyValueAliases.txt */
DEF(Unknown, "Zzzz")
DEF(Adlam, "Adlm")
DEF(Ahom, "Ahom")
DEF(Anatolian_Hieroglyphs, "Hluw")
DEF(Arabic, "Arab")
DEF(Armenian, "Armn")
DEF(Avestan, "Avst")
DEF(Balinese, "Bali")
DEF(Bamum, "Bamu")
DEF(Bassa_Vah, "Bass")
DEF(Batak, "Batk")
DEF(Bengali, "Beng")
DEF(Bhaiksuki, "Bhks")
DEF(Bopomofo, "Bopo")
DEF(Brahmi, "Brah")
DEF(Braille, "Brai")
DEF(Buginese, "Bugi")
DEF(Buhid, "Buhd")
DEF(Canadian_Aboriginal, "Cans")
DEF(Carian, "Cari")
DEF(Caucasian_Albanian, "Aghb")
DEF(Chakma, "Cakm")
DEF(Cham, "Cham")
DEF(Cherokee, "Cher")
DEF(Chorasmian, "Chrs")
DEF(Common, "Zyyy")
DEF(Coptic, "Copt,Qaac")
DEF(Cuneiform, "Xsux")
DEF(Cypriot, "Cprt")
DEF(Cyrillic, "Cyrl")
DEF(Cypro_Minoan, "Cpmn")
DEF(Deseret, "Dsrt")
DEF(Devanagari, "Deva")
DEF(Dives_Akuru, "Diak")
DEF(Dogra, "Dogr")
DEF(Duployan, "Dupl")
DEF(Egyptian_Hieroglyphs, "Egyp")
DEF(Elbasan, "Elba")
DEF(Elymaic, "Elym")
DEF(Ethiopic, "Ethi")
DEF(Georgian, "Geor")
DEF(Glagolitic, "Glag")
DEF(Gothic, "Goth")
DEF(Grantha, "Gran")
DEF(Greek, "Grek")
DEF(Gujarati, "Gujr")
DEF(Gunjala_Gondi, "Gong")
DEF(Gurmukhi, "Guru")
DEF(Han, "Hani")
DEF(Hangul, "Hang")
DEF(Hanifi_Rohingya, "Rohg")
DEF(Hanunoo, "Hano")
DEF(Hatran, "Hatr")
DEF(Hebrew, "Hebr")
DEF(Hiragana, "Hira")
DEF(Imperial_Aramaic, "Armi")
DEF(Inherited, "Zinh,Qaai")
DEF(Inscriptional_Pahlavi, "Phli")
DEF(Inscriptional_Parthian, "Prti")
DEF(Javanese, "Java")
DEF(Kaithi, "Kthi")
DEF(Kannada, "Knda")
DEF(Katakana, "Kana")
DEF(Kawi, "Kawi")
DEF(Kayah_Li, "Kali")
DEF(Kharoshthi, "Khar")
DEF(Khmer, "Khmr")
DEF(Khojki, "Khoj")
DEF(Khitan_Small_Script, "Kits")
DEF(Khudawadi, "Sind")
DEF(Lao, "Laoo")
DEF(Latin, "Latn")
DEF(Lepcha, "Lepc")
DEF(Limbu, "Limb")
DEF(Linear_A, "Lina")
DEF(Linear_B, "Linb")
DEF(Lisu, "Lisu")
DEF(Lycian, "Lyci")
DEF(Lydian, "Lydi")
DEF(Makasar, "Maka")
DEF(Mahajani, "Mahj")
DEF(Malayalam, "Mlym")
DEF(Mandaic, "Mand")
DEF(Manichaean, "Mani")
DEF(Marchen, "Marc")
DEF(Masaram_Gondi, "Gonm")
DEF(Medefaidrin, "Medf")
DEF(Meetei_Mayek, "Mtei")
DEF(Mende_Kikakui, "Mend")
DEF(Meroitic_Cursive, "Merc")
DEF(Meroitic_Hieroglyphs, "Mero")
DEF(Miao, "Plrd")
DEF(Modi, "Modi")
DEF(Mongolian, "Mong")
DEF(Mro, "Mroo")
DEF(Multani, "Mult")
DEF(Myanmar, "Mymr")
DEF(Nabataean, "Nbat")
DEF(Nag_Mundari, "Nagm")
DEF(Nandinagari, "Nand")
DEF(New_Tai_Lue, "Talu")
DEF(Newa, "Newa")
DEF(Nko, "Nkoo")
DEF(Nushu, "Nshu")
DEF(Nyiakeng_Puachue_Hmong, "Hmnp")
DEF(Ogham, "Ogam")
DEF(Ol_Chiki, "Olck")
DEF(Old_Hungarian, "Hung")
DEF(Old_Italic, "Ital")
DEF(Old_North_Arabian, "Narb")
DEF(Old_Permic, "Perm")
DEF(Old_Persian, "Xpeo")
DEF(Old_Sogdian, "Sogo")
DEF(Old_South_Arabian, "Sarb")
DEF(Old_Turkic, "Orkh")
DEF(Old_Uyghur, "Ougr")
DEF(Oriya, "Orya")
DEF(Osage, "Osge")
DEF(Osmanya, "Osma")
DEF(Pahawh_Hmong, "Hmng")
DEF(Palmyrene, "Palm")
DEF(Pau_Cin_Hau, "Pauc")
DEF(Phags_Pa, "Phag")
DEF(Phoenician, "Phnx")
DEF(Psalter_Pahlavi, "Phlp")
DEF(Rejang, "Rjng")
DEF(Runic, "Runr")
DEF(Samaritan, "Samr")
DEF(Saurashtra, "Saur")
DEF(Sharada, "Shrd")
DEF(Shavian, "Shaw")
DEF(Siddham, "Sidd")
DEF(SignWriting, "Sgnw")
DEF(Sinhala, "Sinh")
DEF(Sogdian, "Sogd")
DEF(Sora_Sompeng, "Sora")
DEF(Soyombo, "Soyo")
DEF(Sundanese, "Sund")
DEF(Syloti_Nagri, "Sylo")
DEF(Syriac, "Syrc")
DEF(Tagalog, "Tglg")
DEF(Tagbanwa, "Tagb")
DEF(Tai_Le, "Tale")
DEF(Tai_Tham, "Lana")
DEF(Tai_Viet, "Tavt")
DEF(Takri, "Takr")
DEF(Tamil, "Taml")
DEF(Tangut, "Tang")
DEF(Telugu, "Telu")
DEF(Thaana, "Thaa")
DEF(Thai, "Thai")
DEF(Tibetan, "Tibt")
DEF(Tifinagh, "Tfng")
DEF(Tirhuta, "Tirh")
DEF(Tangsa, "Tnsa")
DEF(Toto, "Toto")
DEF(Ugaritic, "Ugar")
DEF(Vai, "Vaii")
DEF(Vithkuqi, "Vith")
DEF(Wancho, "Wcho")
DEF(Warang_Citi, "Wara")
DEF(Yezidi, "Yezi")
DEF(Yi, "Yiii")
DEF(Zanabazar_Square, "Zanb")
#endif
#ifdef UNICODE_PROP_LIST
/* Prop list not exported to regexp */
DEF(Hyphen, "")
DEF(Other_Math, "")
DEF(Other_Alphabetic, "")
DEF(Other_Lowercase, "")
DEF(Other_Uppercase, "")
DEF(Other_Grapheme_Extend, "")
DEF(Other_Default_Ignorable_Code_Point, "")
DEF(Other_ID_Start, "")
DEF(Other_ID_Continue, "")
DEF(Prepended_Concatenation_Mark, "")
/* additional computed properties for smaller tables */
DEF(ID_Continue1, "")
DEF(XID_Start1, "")
DEF(XID_Continue1, "")
DEF(Changes_When_Titlecased1, "")
DEF(Changes_When_Casefolded1, "")
DEF(Changes_When_NFKC_Casefolded1, "")
/* Prop list exported to JS */
DEF(ASCII_Hex_Digit, "AHex")
DEF(Bidi_Control, "Bidi_C")
DEF(Dash, "")
DEF(Deprecated, "Dep")
DEF(Diacritic, "Dia")
DEF(Extender, "Ext")
DEF(Hex_Digit, "Hex")
DEF(IDS_Binary_Operator, "IDSB")
DEF(IDS_Trinary_Operator, "IDST")
DEF(Ideographic, "Ideo")
DEF(Join_Control, "Join_C")
DEF(Logical_Order_Exception, "LOE")
DEF(Noncharacter_Code_Point, "NChar")
DEF(Pattern_Syntax, "Pat_Syn")
DEF(Pattern_White_Space, "Pat_WS")
DEF(Quotation_Mark, "QMark")
DEF(Radical, "")
DEF(Regional_Indicator, "RI")
DEF(Sentence_Terminal, "STerm")
DEF(Soft_Dotted, "SD")
DEF(Terminal_Punctuation, "Term")
DEF(Unified_Ideograph, "UIdeo")
DEF(Variation_Selector, "VS")
DEF(White_Space, "space")
DEF(Bidi_Mirrored, "Bidi_M")
DEF(Emoji, "")
DEF(Emoji_Component, "EComp")
DEF(Emoji_Modifier, "EMod")
DEF(Emoji_Modifier_Base, "EBase")
DEF(Emoji_Presentation, "EPres")
DEF(Extended_Pictographic, "ExtPict")
DEF(Default_Ignorable_Code_Point, "DI")
DEF(ID_Start, "IDS")
DEF(Case_Ignorable, "CI")
/* other binary properties */
DEF(ASCII,"")
DEF(Alphabetic, "Alpha")
DEF(Any, "")
DEF(Assigned,"")
DEF(Cased, "")
DEF(Changes_When_Casefolded, "CWCF")
DEF(Changes_When_Casemapped, "CWCM")
DEF(Changes_When_Lowercased, "CWL")
DEF(Changes_When_NFKC_Casefolded, "CWKCF")
DEF(Changes_When_Titlecased, "CWT")
DEF(Changes_When_Uppercased, "CWU")
DEF(Grapheme_Base, "Gr_Base")
DEF(Grapheme_Extend, "Gr_Ext")
DEF(ID_Continue, "IDC")
DEF(Lowercase, "Lower")
DEF(Math, "")
DEF(Uppercase, "Upper")
DEF(XID_Continue, "XIDC")
DEF(XID_Start, "XIDS")
/* internal tables with index */
DEF(Cased1, "")
#endif

BIN
deps/libs/darwin_amd64/libquickjs.a vendored Normal file

Binary file not shown.

BIN
deps/libs/darwin_arm64/libquickjs.a vendored Normal file

Binary file not shown.

BIN
deps/libs/linux_amd64/libquickjs.a vendored Normal file

Binary file not shown.

BIN
deps/libs/linux_arm64/libquickjs.a vendored Normal file

Binary file not shown.

BIN
deps/libs/windows_386/libquickjs.a vendored Normal file

Binary file not shown.

BIN
deps/libs/windows_amd64/libquickjs.a vendored Normal file

Binary file not shown.

11
go.mod Normal file
View File

@ -0,0 +1,11 @@
module github.com/buke/quickjs-go
go 1.20
require github.com/stretchr/testify v1.9.0
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

10
go.sum Normal file
View File

@ -0,0 +1,10 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

15
quickjs.go Normal file
View File

@ -0,0 +1,15 @@
/*
Package quickjs Go bindings to QuickJS: a fast, small, and embeddable ES2020 JavaScript interpreter
*/
package quickjs
/*
#cgo CFLAGS: -I./deps/include
#cgo darwin,amd64 LDFLAGS: -L${SRCDIR}/deps/libs/darwin_amd64 -lquickjs -lm
#cgo darwin,arm64 LDFLAGS: -L${SRCDIR}/deps/libs/darwin_arm64 -lquickjs -lm
#cgo linux,amd64 LDFLAGS: -L${SRCDIR}/deps/libs/linux_amd64 -lquickjs -lm
#cgo linux,arm64 LDFLAGS: -L${SRCDIR}/deps/libs/linux_arm64 -lquickjs -lm
#cgo windows,amd64 LDFLAGS: -L${SRCDIR}/deps/libs/windows_amd64 -lquickjs -lm
#cgo windows,386 LDFLAGS: -L${SRCDIR}/deps/libs/windows_386 -lquickjs -lm
*/
import "C"

831
quickjs_test.go Normal file
View File

@ -0,0 +1,831 @@
package quickjs_test
import (
"errors"
"fmt"
"math/big"
"strings"
"sync"
"testing"
"time"
"github.com/buke/quickjs-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Example() {
// Create a new runtime
rt := quickjs.NewRuntime(
quickjs.WithExecuteTimeout(30),
quickjs.WithMemoryLimit(128*1024),
quickjs.WithGCThreshold(256*1024),
quickjs.WithMaxStackSize(65534),
quickjs.WithCanBlock(true),
)
defer rt.Close()
// Create a new context
ctx := rt.NewContext()
defer ctx.Close()
// Create a new object
test := ctx.Object()
defer test.Free()
// bind properties to the object
test.Set("A", test.Context().String("String A"))
test.Set("B", ctx.Int32(0))
test.Set("C", ctx.Bool(false))
// bind go function to js object
test.Set("hello", ctx.Function(func(ctx *quickjs.Context, this quickjs.Value, args []quickjs.Value) quickjs.Value {
return ctx.String("Hello " + args[0].String())
}))
// bind "test" object to global object
ctx.Globals().Set("test", test)
// call js function by js
js_ret, _ := ctx.Eval(`test.hello("Javascript!")`)
fmt.Println(js_ret.String())
// call js function by go
go_ret := ctx.Globals().Get("test").Call("hello", ctx.String("Golang!"))
fmt.Println(go_ret.String())
//bind go function to Javascript async function
ctx.Globals().Set("testAsync", ctx.AsyncFunction(func(ctx *quickjs.Context, this quickjs.Value, promise quickjs.Value, args []quickjs.Value) quickjs.Value {
return promise.Call("resolve", ctx.String("Hello Async Function!"))
}))
ret, _ := ctx.Eval(`
var ret;
testAsync().then(v => ret = v)
`)
defer ret.Free()
// wait for promise resolve
ctx.Loop()
asyncRet, _ := ctx.Eval("ret")
defer asyncRet.Free()
fmt.Println(asyncRet.String())
// Output:
// Hello Javascript!
// Hello Golang!
// Hello Async Function!
}
func TestRuntimeGC(t *testing.T) {
rt := quickjs.NewRuntime(
quickjs.WithExecuteTimeout(30),
quickjs.WithMemoryLimit(128*1024),
quickjs.WithGCThreshold(256*1024),
quickjs.WithMaxStackSize(65534),
quickjs.WithCanBlock(true),
)
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
rt.RunGC()
result, _ := ctx.Eval(`"Hello GC!"`)
defer result.Free()
require.EqualValues(t, "Hello GC!", result.String())
}
func TestRuntimeMemoryLimit(t *testing.T) {
rt := quickjs.NewRuntime()
defer rt.Close()
// set runtime options
rt.SetMemoryLimit(128 * 1024) //512KB
ctx := rt.NewContext()
defer ctx.Close()
result, err := ctx.Eval(`var array = []; while (true) { array.push(null) }`)
defer result.Free()
if assert.Error(t, err, "expected a memory limit violation") {
require.Equal(t, "InternalError: out of memory", err.Error())
}
}
func TestRuntimeStackSize(t *testing.T) {
rt := quickjs.NewRuntime()
defer rt.Close()
rt.SetMaxStackSize(65534)
ctx := rt.NewContext()
defer ctx.Close()
result, err := ctx.Eval(`
function fib(n)
{
if (n <= 0)
return 0;
else if (n == 1)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
fib(128)
`)
defer result.Free()
if assert.Error(t, err, "expected a memory limit violation") {
require.Equal(t, "InternalError: stack overflow", err.Error())
}
}
func TestThrowError(t *testing.T) {
expected := errors.New("custom error")
rt := quickjs.NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
ctx.Globals().Set("A", ctx.Function(func(ctx *quickjs.Context, this quickjs.Value, args []quickjs.Value) quickjs.Value {
return ctx.ThrowError(expected)
}))
_, actual := ctx.Eval("A()")
require.Error(t, actual)
require.EqualValues(t, "Error: "+expected.Error(), actual.Error())
}
func TestThrowInternalError(t *testing.T) {
rt := quickjs.NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
ctx.Globals().Set("A", ctx.Function(func(ctx *quickjs.Context, this quickjs.Value, args []quickjs.Value) quickjs.Value {
return ctx.ThrowInternalError("%s", "custom error")
}))
_, actual := ctx.Eval("A()")
require.Error(t, actual)
require.EqualValues(t, "InternalError: custom error", actual.Error())
}
func TestThrowRangeError(t *testing.T) {
rt := quickjs.NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
ctx.Globals().Set("A", ctx.Function(func(ctx *quickjs.Context, this quickjs.Value, args []quickjs.Value) quickjs.Value {
return ctx.ThrowRangeError("%s", "custom error")
}))
_, actual := ctx.Eval("A()")
require.Error(t, actual)
require.EqualValues(t, "RangeError: custom error", actual.Error())
}
func TestThrowReferenceError(t *testing.T) {
rt := quickjs.NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
ctx.Globals().Set("A", ctx.Function(func(ctx *quickjs.Context, this quickjs.Value, args []quickjs.Value) quickjs.Value {
return ctx.ThrowReferenceError("%s", "custom error")
}))
_, actual := ctx.Eval("A()")
require.Error(t, actual)
require.EqualValues(t, "ReferenceError: custom error", actual.Error())
}
func TestThrowSyntaxError(t *testing.T) {
rt := quickjs.NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
ctx.Globals().Set("A", ctx.Function(func(ctx *quickjs.Context, this quickjs.Value, args []quickjs.Value) quickjs.Value {
return ctx.ThrowSyntaxError("%s", "custom error")
}))
_, actual := ctx.Eval("A()")
require.Error(t, actual)
require.EqualValues(t, "SyntaxError: custom error", actual.Error())
}
func TestThrowTypeError(t *testing.T) {
rt := quickjs.NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
ctx.Globals().Set("A", ctx.Function(func(ctx *quickjs.Context, this quickjs.Value, args []quickjs.Value) quickjs.Value {
return ctx.ThrowTypeError("%s", "custom error")
}))
_, actual := ctx.Eval("A()")
require.Error(t, actual)
require.EqualValues(t, "TypeError: custom error", actual.Error())
}
func TestValue(t *testing.T) {
rt := quickjs.NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
// require.EqualValues(t, big.NewInt(1), ctx.BigUint64(uint64(1)).)
require.EqualValues(t, true, ctx.Bool(true).IsBool())
require.EqualValues(t, true, ctx.Bool(true).Bool())
require.EqualValues(t, float64(0.1), ctx.Float64(0.1).Float64())
require.EqualValues(t, int32(1), ctx.Int32(1).Int32())
require.EqualValues(t, int64(1), ctx.Int64(1).Int64())
require.EqualValues(t, uint32(1), ctx.Uint32(1).Uint32())
require.EqualValues(t, big.NewInt(1), ctx.BigInt64(1).BigInt())
require.EqualValues(t, big.NewInt(1), ctx.BigUint64(1).BigInt())
require.EqualValues(t, false, ctx.Float64(0.1).IsBigDecimal())
require.EqualValues(t, false, ctx.Float64(0.1).IsBigFloat())
require.EqualValues(t, false, ctx.Float64(0.1).IsBigInt())
a := ctx.Array()
defer a.Free()
//require.True(t, a.IsArray())
o := ctx.Object()
defer o.Free()
require.True(t, o.IsObject())
s := ctx.String("hello")
defer s.Free()
require.EqualValues(t, true, s.IsString())
n := ctx.Null()
defer n.Free()
require.True(t, n.IsNull())
ud := ctx.Undefined()
defer ud.Free()
require.True(t, ud.IsUndefined())
ui := ctx.Uninitialized()
defer ui.Free()
require.True(t, ui.IsUninitialized())
sym, _ := ctx.Eval("Symbol()")
defer sym.Free()
require.True(t, sym.IsSymbol())
err := ctx.Error(errors.New("error"))
defer err.Free()
require.True(t, err.IsError())
}
func TestEvalFile(t *testing.T) {
// enable module import
rt := quickjs.NewRuntime(quickjs.WithModuleImport(true))
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
result, err := ctx.EvalFile("./test/hello_module.js")
defer result.Free()
require.NoError(t, err)
require.EqualValues(t, 55, ctx.Globals().Get("result").Int32())
}
func TestEvalBytecode(t *testing.T) {
rt := quickjs.NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
jsStr := `
function fib(n)
{
if (n <= 0)
return 0;
else if (n == 1)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
fib(10)
`
buf, err := ctx.Compile(jsStr)
require.NoError(t, err)
rt2 := quickjs.NewRuntime()
defer rt2.Close()
ctx2 := rt2.NewContext()
defer ctx2.Close()
result, err := ctx2.EvalBytecode(buf)
require.NoError(t, err)
require.EqualValues(t, 55, result.Int32())
}
func TestBadSyntax(t *testing.T) {
rt := quickjs.NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
_, err := ctx.Compile(`"bad syntax'`)
require.Error(t, err)
}
func TestBadBytecode(t *testing.T) {
rt := quickjs.NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
buf := make([]byte, 1)
_, err := ctx.EvalBytecode(buf)
require.Error(t, err)
}
func TestArrayBuffer(t *testing.T) {
rt := quickjs.NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
binaryData := []uint8{1, 2, 3, 4, 5}
value := ctx.ArrayBuffer(binaryData)
defer value.Free()
for i := 1; i <= len(binaryData); i++ {
data, err := value.ToByteArray(uint(i))
assert.NoError(t, err)
//fmt.Println(data)
assert.EqualValues(t, data, binaryData[:i])
}
_, err := value.ToByteArray(uint(len(binaryData)) + 1)
assert.Error(t, err)
assert.True(t, value.IsByteArray())
binaryLen := len(binaryData)
assert.Equal(t, value.ByteLen(), int64(binaryLen))
}
func TestConcurrency(t *testing.T) {
n := 32
m := 10000
var wg sync.WaitGroup
wg.Add(n)
req := make(chan struct{}, n)
res := make(chan int64, m)
for i := 0; i < n; i++ {
go func() {
defer wg.Done()
rt := quickjs.NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
for range req {
result, err := ctx.Eval(`new Date().getTime()`)
require.NoError(t, err)
res <- result.Int64()
result.Free()
}
}()
}
for i := 0; i < m; i++ {
req <- struct{}{}
}
close(req)
wg.Wait()
for i := 0; i < m; i++ {
<-res
}
}
func TestJson(t *testing.T) {
// Create a new runtime
rt := quickjs.NewRuntime()
defer rt.Close()
// Create a new context
ctx := rt.NewContext()
defer ctx.Close()
// Create a object from json
fooObj := ctx.ParseJSON(`{"foo":"bar"}`)
defer fooObj.Free()
// JSONStringify
jsonStr := fooObj.JSONStringify()
require.EqualValues(t, "{\"foo\":\"bar\"}", jsonStr)
}
func TestObject(t *testing.T) {
// Create a new runtime
rt := quickjs.NewRuntime()
defer rt.Close()
// Create a new context
ctx := rt.NewContext()
defer ctx.Close()
// Create a new object
test := ctx.Object()
test.Set("A", test.Context().String("String A"))
test.Set("B", ctx.Int32(0))
test.Set("C", ctx.Bool(false))
ctx.Globals().Set("test", test)
result, err := ctx.Eval(`Object.keys(test).map(key => test[key]).join(",")`)
require.NoError(t, err)
defer result.Free()
// eval js code
require.EqualValues(t, "String A,0,false", result.String())
// set function
test.Set("F", ctx.Function(func(ctx *quickjs.Context, this quickjs.Value, args []quickjs.Value) quickjs.Value {
arg_x := args[0].Int32()
arg_y := args[1].Int32()
return ctx.Int32(arg_x * arg_y)
}))
// call js function by go
F_ret := test.Call("F", ctx.Int32(2), ctx.Int32(3))
defer F_ret.Free()
require.True(t, F_ret.IsNumber() && F_ret.Int32() == 6)
// invoke js function by go
f_func := test.Get("F")
defer f_func.Free()
ret := ctx.Invoke(f_func, ctx.Null(), ctx.Int32(2), ctx.Int32(3))
require.True(t, ret.IsNumber() && ret.Int32() == 6)
// test error call
F_ret_err := test.Call("A", ctx.Int32(2), ctx.Int32(3))
defer F_ret_err.Free()
require.Error(t, F_ret_err.Error())
// get object property
require.True(t, test.Has("A"))
require.True(t, test.Get("A").String() == "String A")
// get object all property
pNames, _ := test.PropertyNames()
require.True(t, strings.Join(pNames[:], ",") == "A,B,C,F")
// delete object property
test.Delete("C")
pNames, _ = test.PropertyNames()
require.True(t, strings.Join(pNames[:], ",") == "A,B,F")
}
func TestArray(t *testing.T) {
rt := quickjs.NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
test := ctx.Array()
for i := int64(0); i < 3; i++ {
test.Push(ctx.String(fmt.Sprintf("test %d", i)))
require.True(t, test.HasIdx(i))
}
require.EqualValues(t, 3, test.Len())
for i := int64(0); int64(i) < test.Len(); i++ {
require.EqualValues(t, fmt.Sprintf("test %d", i), test.ToValue().GetIdx(i).String())
}
ctx.Globals().Set("test", test.ToValue())
result, err := ctx.Eval(`test.map(v => v.toUpperCase())`)
require.NoError(t, err)
defer result.Free()
require.EqualValues(t, `TEST 0,TEST 1,TEST 2`, result.String())
dFlag, _ := test.Delete(0)
require.True(t, dFlag)
result, err = ctx.Eval(`test.map(v => v.toUpperCase())`)
require.NoError(t, err)
defer result.Free()
require.EqualValues(t, `TEST 1,TEST 2`, result.String())
first, err := test.Get(0)
if err != nil {
fmt.Println(err)
}
require.EqualValues(t, first.String(), "test 1")
test.Push([]quickjs.Value{ctx.Int32(34), ctx.Bool(false), ctx.String("445")}...)
require.Equal(t, int(test.Len()), 5)
err = test.Set(test.Len()-1, ctx.Int32(2))
require.NoError(t, err)
require.EqualValues(t, test.ToValue().String(), "test 1,test 2,34,false,2")
}
func TestMap(t *testing.T) {
rt := quickjs.NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
test := ctx.Map()
defer test.Free()
require.True(t, test.ToValue().IsMap())
for i := int64(0); i < 3; i++ {
test.Put(ctx.Int64(i), ctx.String(fmt.Sprintf("test %d", i)))
require.True(t, test.Has(ctx.Int64(i)))
testValue := test.Get(ctx.Int64(i))
require.EqualValues(t, testValue.String(), fmt.Sprintf("test %d", i))
//testValue.Free()
}
count := 0
test.ForEach(func(key quickjs.Value, value quickjs.Value) {
count++
fmt.Printf("key:%s value:%s\n", key.String(), value.String())
})
require.EqualValues(t, count, 3)
test.Put(ctx.Int64(3), ctx.Int64(4))
fmt.Println("\nput after the content inside")
count = 0
test.ForEach(func(key quickjs.Value, value quickjs.Value) {
count++
fmt.Printf("key:%s value:%s\n", key.String(), value.String())
})
require.EqualValues(t, count, 4)
count = 0
test.Delete(ctx.Int64(3))
fmt.Println("\ndelete after the content inside")
test.ForEach(func(key quickjs.Value, value quickjs.Value) {
if key.String() == "3" {
panic(errors.New("map did not delete the key"))
}
count++
fmt.Printf("key:%s value:%s\n", key.String(), value.String())
})
require.EqualValues(t, count, 3)
}
func TestSet(t *testing.T) {
rt := quickjs.NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
test := ctx.Set()
defer test.Free()
require.True(t, test.ToValue().IsSet())
for i := int64(0); i < 3; i++ {
test.Add(ctx.Int64(i))
require.True(t, test.Has(ctx.Int64(i)))
}
count := 0
test.ForEach(func(key quickjs.Value) {
count++
fmt.Printf("value:%s\n", key.String())
})
require.EqualValues(t, count, 3)
test.Delete(ctx.Int64(0))
require.True(t, !test.Has(ctx.Int64(0)))
}
func TestFunction(t *testing.T) {
rt := quickjs.NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
ctx.Globals().Set("test", ctx.Function(func(ctx *quickjs.Context, this quickjs.Value, args []quickjs.Value) quickjs.Value {
return ctx.String("Hello " + args[0].String() + args[1].String())
}))
ret, _ := ctx.Eval(`
test('Go ', 'JS')
`)
defer ret.Free()
require.EqualValues(t, "Hello Go JS", ret.String())
}
func TestAsyncFunction(t *testing.T) {
rt := quickjs.NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
ctx.Globals().Set("testAsync", ctx.AsyncFunction(func(ctx *quickjs.Context, this quickjs.Value, promise quickjs.Value, args []quickjs.Value) quickjs.Value {
return promise.Call("resolve", ctx.String(args[0].String()+args[1].String()))
}))
ret1, _ := ctx.Eval(`
var ret = "";
`)
defer ret1.Free()
// wait for job resolve
ctx.Loop()
// testAsync
ret2, _ := ctx.Eval(`
testAsync('Hello ', 'Async').then(v => ret = ret + v)
`)
defer ret2.Free()
// wait promise execute
ctx.Loop()
ret3, _ := ctx.Eval("ret")
defer ret3.Free()
require.EqualValues(t, "Hello Async", ret3.String())
}
func TestSetInterruptHandler(t *testing.T) {
rt := quickjs.NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
startTime := time.Now().Unix()
ctx.SetInterruptHandler(func() int {
if time.Now().Unix()-startTime > 1 {
return 1
}
return 0
})
ret, err := ctx.Eval(`while(true){}`)
defer ret.Free()
assert.Error(t, err, "expected interrupted by quickjs")
require.Equal(t, "InternalError: interrupted", err.Error())
}
func TestSetExecuteTimeout(t *testing.T) {
rt := quickjs.NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
rt.SetExecuteTimeout(3)
ret, err := ctx.Eval(`while(true){}`)
defer ret.Free()
assert.Error(t, err, "expected interrupted by quickjs")
require.Equal(t, "InternalError: interrupted", err.Error())
}
func TestSetTimeout(t *testing.T) {
rt := quickjs.NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
ret, _ := ctx.Eval(`
var a = false;
setTimeout(() => {
a = true;
}, 50);
`)
defer ret.Free()
ctx.Loop()
a, _ := ctx.Eval("a")
defer a.Free()
require.EqualValues(t, true, a.Bool())
}
func TestAwait(t *testing.T) {
rt := quickjs.NewRuntime()
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
// async function bind to global
ctx.Globals().Set("testAsync", ctx.AsyncFunction(func(ctx *quickjs.Context, this quickjs.Value, promise quickjs.Value, args []quickjs.Value) quickjs.Value {
return promise.Call("resolve", ctx.String(args[0].String()+args[1].String()))
}))
// testAwait
promise, _ := ctx.Eval("testAsync('Hello ', 'Await')")
require.EqualValues(t, true, promise.IsPromise())
promiseAwait, _ := ctx.Await(promise)
require.EqualValues(t, "Hello Await", promiseAwait.String())
promiseAwaitEval, _ := ctx.Eval("testAsync('Hello ', 'AwaitEval')", quickjs.EvalAwait(true))
require.EqualValues(t, "Hello AwaitEval", promiseAwaitEval.String())
}
func TestModule(t *testing.T) {
// enable module import
rt := quickjs.NewRuntime(quickjs.WithModuleImport(true))
defer rt.Close()
ctx := rt.NewContext()
defer ctx.Close()
// eval module
r1, err := ctx.EvalFile("./test/hello_module.js")
defer r1.Free()
require.NoError(t, err)
require.EqualValues(t, 55, ctx.Globals().Get("result").Int32())
// load module
r2, err := ctx.LoadModuleFile("./test/fib_module.js", "fib_foo")
defer r2.Free()
require.NoError(t, err)
// call module
r3, err := ctx.Eval(`
import {fib} from 'fib_foo';
globalThis.result = fib(11);
`)
defer r3.Free()
require.NoError(t, err)
require.EqualValues(t, 89, ctx.Globals().Get("result").Int32())
// load module from bytecode
buf, err := ctx.CompileFile("./test/fib_module.js")
require.NoError(t, err)
r4, err := ctx.LoadModuleBytecode(buf, "fib_foo")
defer r4.Free()
require.NoError(t, err)
r5, err := ctx.Eval(`
import {fib} from 'fib_foo';
globalThis.result = fib(12);
`)
defer r5.Free()
require.NoError(t, err)
require.EqualValues(t, 144, ctx.Globals().Get("result").Int32())
}

181
runtime.go Normal file
View File

@ -0,0 +1,181 @@
package quickjs
/*
#include "bridge.h"
#include <time.h>
*/
import "C"
import (
"runtime"
"unsafe"
)
// Runtime represents a Javascript runtime corresponding to an object heap. Several runtimes can exist at the same time but they cannot exchange objects. Inside a given runtime, no multi-threading is supported.
type Runtime struct {
ref *C.JSRuntime
options *Options
}
type Options struct {
timeout uint64
memoryLimit uint64
gcThreshold uint64
maxStackSize uint64
canBlock bool
moduleImport bool
}
type Option func(*Options)
// WithExecuteTimeout will set the runtime's execute timeout; default is 0
func WithExecuteTimeout(timeout uint64) Option {
return func(o *Options) {
o.timeout = timeout
}
}
// WithMemoryLimit will set the runtime memory limit; if not set, it will be unlimit.
func WithMemoryLimit(memoryLimit uint64) Option {
return func(o *Options) {
o.memoryLimit = memoryLimit
}
}
// WithGCThreshold will set the runtime's GC threshold; use -1 to disable automatic GC.
func WithGCThreshold(gcThreshold uint64) Option {
return func(o *Options) {
o.gcThreshold = gcThreshold
}
}
// WithMaxStackSize will set max runtime's stack size; default is 255
func WithMaxStackSize(maxStackSize uint64) Option {
return func(o *Options) {
o.maxStackSize = maxStackSize
}
}
// WithCanBlock will set the runtime's can block; default is true
func WithCanBlock(canBlock bool) Option {
return func(o *Options) {
o.canBlock = canBlock
}
}
func WithModuleImport(moduleImport bool) Option {
return func(o *Options) {
o.moduleImport = moduleImport
}
}
// NewRuntime creates a new quickjs runtime.
func NewRuntime(opts ...Option) Runtime {
runtime.LockOSThread() // prevent multiple quickjs runtime from being created
options := &Options{
timeout: 0,
memoryLimit: 0,
gcThreshold: 0,
maxStackSize: 0,
canBlock: true,
moduleImport: false,
}
for _, opt := range opts {
opt(options)
}
rt := Runtime{ref: C.JS_NewRuntime(), options: options}
if rt.options.timeout > 0 {
rt.SetExecuteTimeout(rt.options.timeout)
}
if rt.options.memoryLimit > 0 {
rt.SetMemoryLimit(rt.options.memoryLimit)
}
if rt.options.gcThreshold > 0 {
rt.SetGCThreshold(rt.options.gcThreshold)
}
if rt.options.maxStackSize > 0 {
rt.SetMaxStackSize(rt.options.maxStackSize)
}
if rt.options.canBlock {
C.JS_SetCanBlock(rt.ref, C.int(1))
}
return rt
}
// RunGC will call quickjs's garbage collector.
func (r Runtime) RunGC() {
C.JS_RunGC(r.ref)
}
// Close will free the runtime pointer.
func (r Runtime) Close() {
C.JS_FreeRuntime(r.ref)
}
// SetCanBlock will set the runtime's can block; default is true
func (r Runtime) SetCanBlock(canBlock bool) {
if canBlock {
C.JS_SetCanBlock(r.ref, C.int(1))
} else {
C.JS_SetCanBlock(r.ref, C.int(0))
}
}
// SetMemoryLimit the runtime memory limit; if not set, it will be unlimit.
func (r Runtime) SetMemoryLimit(limit uint64) {
C.JS_SetMemoryLimit(r.ref, C.size_t(limit))
}
// SetGCThreshold the runtime's GC threshold; use -1 to disable automatic GC.
func (r Runtime) SetGCThreshold(threshold uint64) {
C.JS_SetGCThreshold(r.ref, C.size_t(threshold))
}
// SetMaxStackSize will set max runtime's stack size; default is 255
func (r Runtime) SetMaxStackSize(stack_size uint64) {
C.JS_SetMaxStackSize(r.ref, C.size_t(stack_size))
}
// SetExecuteTimeout will set the runtime's execute timeout; default is 0
func (r Runtime) SetExecuteTimeout(timeout uint64) {
C.SetExecuteTimeout(r.ref, C.time_t(timeout))
}
// NewContext creates a new JavaScript context.
// enable BigFloat/BigDecimal support and enable .
// enable operator overloading.
func (r Runtime) NewContext() *Context {
C.js_std_init_handlers(r.ref)
// create a new context (heap, global object and context stack
ctx_ref := C.JS_NewContext(r.ref)
C.JS_AddIntrinsicBigFloat(ctx_ref)
C.JS_AddIntrinsicBigDecimal(ctx_ref)
C.JS_AddIntrinsicOperators(ctx_ref)
C.JS_EnableBignumExt(ctx_ref, C.int(1))
// set the module loader for support dynamic import
if r.options.moduleImport {
C.JS_SetModuleLoaderFunc(r.ref, (*C.JSModuleNormalizeFunc)(unsafe.Pointer(nil)), (*C.JSModuleLoaderFunc)(C.js_module_loader), unsafe.Pointer(nil))
}
// import the 'std' and 'os' modules
C.js_init_module_std(ctx_ref, C.CString("std"))
C.js_init_module_os(ctx_ref, C.CString("os"))
// import setTimeout and clearTimeout from 'os' to globalThis
code := `
import { setTimeout, clearTimeout } from "os";
globalThis.setTimeout = setTimeout;
globalThis.clearTimeout = clearTimeout;
`
init_compile := C.JS_Eval(ctx_ref, C.CString(code), C.size_t(len(code)), C.CString("init.js"), C.JS_EVAL_TYPE_MODULE|C.JS_EVAL_FLAG_COMPILE_ONLY)
init_run := C.js_std_await(ctx_ref, C.JS_EvalFunction(ctx_ref, init_compile))
C.JS_FreeValue(ctx_ref, init_run)
// C.js_std_loop(ctx_ref)
return &Context{ref: ctx_ref, runtime: &r}
}

10
test/fib_module.js Normal file
View File

@ -0,0 +1,10 @@
/* fib module */
export function fib(n)
{
if (n <= 0)
return 0;
else if (n == 1)
return 1;
else
return fib(n - 1) + fib(n - 2);
}

8
test/hello_module.js Normal file
View File

@ -0,0 +1,8 @@
/* example of JS module */
import { fib } from "./fib_module.js";
// console.log("Hello World");
// console.log("fib(10)=", fib(10));
globalThis.result = fib(10);

367
value.go Normal file
View File

@ -0,0 +1,367 @@
package quickjs
/*
#include "bridge.h"
*/
import "C"
import (
"errors"
"math/big"
"unsafe"
)
type Error struct {
Cause string
Stack string
}
func (err Error) Error() string { return err.Cause }
// Object property names and some strings are stored as Atoms (unique strings) to save memory and allow fast comparison. Atoms are represented as a 32 bit integer. Half of the atom range is reserved for immediate integer literals from 0 to 2^{31}-1.
type Atom struct {
ctx *Context
ref C.JSAtom
}
// Free the value.
func (a Atom) Free() {
C.JS_FreeAtom(a.ctx.ref, a.ref)
}
// String returns the string representation of the value.
func (a Atom) String() string {
ptr := C.JS_AtomToCString(a.ctx.ref, a.ref)
defer C.JS_FreeCString(a.ctx.ref, ptr)
return C.GoString(ptr)
}
// Value returns the value of the Atom object.
func (a Atom) Value() Value {
return Value{ctx: a.ctx, ref: C.JS_AtomToValue(a.ctx.ref, a.ref)}
}
// propertyEnum is a wrapper around JSAtom.
type propertyEnum struct {
IsEnumerable bool
atom Atom
}
// String returns the atom string representation of the value.
func (p propertyEnum) String() string { return p.atom.String() }
// JSValue represents a Javascript value which can be a primitive type or an object. Reference counting is used, so it is important to explicitly duplicate (JS_DupValue(), increment the reference count) or free (JS_FreeValue(), decrement the reference count) JSValues.
type Value struct {
ctx *Context
ref C.JSValue
}
// Free the value.
func (v Value) Free() {
C.JS_FreeValue(v.ctx.ref, v.ref)
}
// Context represents a Javascript context.
func (v Value) Context() *Context {
return v.ctx
}
// Bool returns the boolean value of the value.
func (v Value) Bool() bool {
return C.JS_ToBool(v.ctx.ref, v.ref) == 1
}
// String returns the string representation of the value.
func (v Value) String() string {
ptr := C.JS_ToCString(v.ctx.ref, v.ref)
defer C.JS_FreeCString(v.ctx.ref, ptr)
return C.GoString(ptr)
}
// JSONString returns the JSON string representation of the value.
func (v Value) JSONStringify() string {
ref := C.JS_JSONStringify(v.ctx.ref, v.ref, C.JS_NewNull(), C.JS_NewNull())
ptr := C.JS_ToCString(v.ctx.ref, ref)
defer C.JS_FreeCString(v.ctx.ref, ptr)
return C.GoString(ptr)
}
func (v Value) ToByteArray(size uint) ([]byte, error) {
if v.ByteLen() < int64(size) {
return nil, errors.New("exceeds the maximum length of the current binary array")
}
cSize := C.size_t(size)
outBuf := C.JS_GetArrayBuffer(v.ctx.ref, &cSize, v.ref)
return C.GoBytes(unsafe.Pointer(outBuf), C.int(size)), nil
}
// IsByteArray return true if the value is array buffer
func (v Value) IsByteArray() bool {
return v.IsObject() && v.globalInstanceof("ArrayBuffer") || v.String() == "[object ArrayBuffer]"
}
// Int64 returns the int64 value of the value.
func (v Value) Int64() int64 {
val := C.int64_t(0)
C.JS_ToInt64(v.ctx.ref, &val, v.ref)
return int64(val)
}
// Int32 returns the int32 value of the value.
func (v Value) Int32() int32 {
val := C.int32_t(0)
C.JS_ToInt32(v.ctx.ref, &val, v.ref)
return int32(val)
}
// Uint32 returns the uint32 value of the value.
func (v Value) Uint32() uint32 {
val := C.uint32_t(0)
C.JS_ToUint32(v.ctx.ref, &val, v.ref)
return uint32(val)
}
// Float64 returns the float64 value of the value.
func (v Value) Float64() float64 {
val := C.double(0)
C.JS_ToFloat64(v.ctx.ref, &val, v.ref)
return float64(val)
}
// BigInt returns the big.Int value of the value.
func (v Value) BigInt() *big.Int {
if !v.IsBigInt() {
return nil
}
val, ok := new(big.Int).SetString(v.String(), 10)
if !ok {
return nil
}
return val
}
// BigFloat returns the big.Float value of the value.
func (v Value) BigFloat() *big.Float {
if !v.IsBigDecimal() && !v.IsBigFloat() {
return nil
}
val, ok := new(big.Float).SetString(v.String())
if !ok {
return nil
}
return val
}
// ToArray
//
// @Description: return array object
// @receiver v :
// @return *Array
func (v Value) ToArray() *Array {
if !v.IsArray() {
return nil
}
return NewQjsArray(v, v.ctx)
}
// ToMap
//
// @Description: return map object
// @receiver v :
// @return *Map
func (v Value) ToMap() *Map {
if !v.IsMap() {
return nil
}
return NewQjsMap(v, v.ctx)
}
// ToSet
//
// @Description: return set object
// @receiver v :
// @return *Set
func (v Value) ToSet() *Set {
if v.IsSet() {
return nil
}
return NewQjsSet(v, v.ctx)
}
// IsMap return true if the value is a map
func (v Value) IsMap() bool {
return v.IsObject() && v.globalInstanceof("Map") || v.String() == "[object Map]"
}
// IsSet return true if the value is a set
func (v Value) IsSet() bool {
return v.IsObject() && v.globalInstanceof("Set") || v.String() == "[object Set]"
}
// Len returns the length of the array.
func (v Value) Len() int64 {
return v.Get("length").Int64()
}
// ByteLen returns the length of the ArrayBuffer.
func (v Value) ByteLen() int64 {
return v.Get("byteLength").Int64()
}
// Set sets the value of the property with the given name.
func (v Value) Set(name string, val Value) {
namePtr := C.CString(name)
defer C.free(unsafe.Pointer(namePtr))
C.JS_SetPropertyStr(v.ctx.ref, v.ref, namePtr, val.ref)
}
// SetIdx sets the value of the property with the given index.
func (v Value) SetIdx(idx int64, val Value) {
C.JS_SetPropertyUint32(v.ctx.ref, v.ref, C.uint32_t(idx), val.ref)
}
// Get returns the value of the property with the given name.
func (v Value) Get(name string) Value {
namePtr := C.CString(name)
defer C.free(unsafe.Pointer(namePtr))
return Value{ctx: v.ctx, ref: C.JS_GetPropertyStr(v.ctx.ref, v.ref, namePtr)}
}
// GetIdx returns the value of the property with the given index.
func (v Value) GetIdx(idx int64) Value {
return Value{ctx: v.ctx, ref: C.JS_GetPropertyUint32(v.ctx.ref, v.ref, C.uint32_t(idx))}
}
// Call calls the function with the given arguments.
func (v Value) Call(fname string, args ...Value) Value {
if !v.IsObject() {
return v.ctx.Error(errors.New("Object not a object"))
}
fn := v.Get(fname) // get the function by name
defer fn.Free()
if !fn.IsFunction() {
return v.ctx.Error(errors.New("Object not a function"))
}
cargs := []C.JSValue{}
for _, x := range args {
cargs = append(cargs, x.ref)
}
if len(cargs) == 0 {
return Value{ctx: v.ctx, ref: C.JS_Call(v.ctx.ref, fn.ref, v.ref, C.int(0), nil)}
}
return Value{ctx: v.ctx, ref: C.JS_Call(v.ctx.ref, fn.ref, v.ref, C.int(len(cargs)), &cargs[0])}
}
// Error returns the error value of the value.
func (v Value) Error() error {
if !v.IsError() {
return nil
}
cause := v.String()
stack := v.Get("stack")
defer stack.Free()
if stack.IsUndefined() {
return &Error{Cause: cause}
}
return &Error{Cause: cause, Stack: stack.String()}
}
// propertyEnum is a wrapper around JSValue.
func (v Value) propertyEnum() ([]propertyEnum, error) {
var ptr *C.JSPropertyEnum
var size C.uint32_t
result := int(C.JS_GetOwnPropertyNames(v.ctx.ref, &ptr, &size, v.ref, C.int(1<<0|1<<1|1<<2)))
if result < 0 {
return nil, errors.New("value does not contain properties")
}
defer C.js_free(v.ctx.ref, unsafe.Pointer(ptr))
entries := unsafe.Slice(ptr, size) // Go 1.17 and later
names := make([]propertyEnum, len(entries))
for i := 0; i < len(names); i++ {
names[i].IsEnumerable = entries[i].is_enumerable == 1
names[i].atom = Atom{ctx: v.ctx, ref: entries[i].atom}
names[i].atom.Free()
}
return names, nil
}
// PropertyNames returns the names of the properties of the value.
func (v Value) PropertyNames() ([]string, error) {
pList, err := v.propertyEnum()
if err != nil {
return nil, err
}
names := make([]string, len(pList))
for i := 0; i < len(names); i++ {
names[i] = pList[i].String()
}
return names, nil
}
// Has returns true if the value has the property with the given name.
func (v Value) Has(name string) bool {
prop := v.ctx.Atom(name)
defer prop.Free()
return C.JS_HasProperty(v.ctx.ref, v.ref, prop.ref) == 1
}
// HasIdx returns true if the value has the property with the given index.
func (v Value) HasIdx(idx int64) bool {
prop := v.ctx.AtomIdx(idx)
defer prop.Free()
return C.JS_HasProperty(v.ctx.ref, v.ref, prop.ref) == 1
}
// Delete deletes the property with the given name.
func (v Value) Delete(name string) bool {
prop := v.ctx.Atom(name)
defer prop.Free()
return C.JS_DeleteProperty(v.ctx.ref, v.ref, prop.ref, C.int(1)) == 1
}
// DeleteIdx deletes the property with the given index.
func (v Value) DeleteIdx(idx int64) bool {
return C.JS_DeletePropertyInt64(v.ctx.ref, v.ref, C.int64_t(idx), C.int(1)) == 1
}
// globalInstanceof checks if the value is an instance of the given global constructor
func (v Value) globalInstanceof(name string) bool {
ctor := v.ctx.Globals().Get(name)
defer ctor.Free()
if ctor.IsUndefined() {
return false
}
return C.JS_IsInstanceOf(v.ctx.ref, v.ref, ctor.ref) == 1
}
func (v Value) IsNumber() bool { return C.JS_IsNumber(v.ref) == 1 }
func (v Value) IsBigInt() bool { return C.JS_IsBigInt(v.ctx.ref, v.ref) == 1 }
func (v Value) IsBigFloat() bool { return C.JS_IsBigFloat(v.ref) == 1 }
func (v Value) IsBigDecimal() bool { return C.JS_IsBigDecimal(v.ref) == 1 }
func (v Value) IsBool() bool { return C.JS_IsBool(v.ref) == 1 }
func (v Value) IsNull() bool { return C.JS_IsNull(v.ref) == 1 }
func (v Value) IsUndefined() bool { return C.JS_IsUndefined(v.ref) == 1 }
func (v Value) IsException() bool { return C.JS_IsException(v.ref) == 1 }
func (v Value) IsUninitialized() bool { return C.JS_IsUninitialized(v.ref) == 1 }
func (v Value) IsString() bool { return C.JS_IsString(v.ref) == 1 }
func (v Value) IsSymbol() bool { return C.JS_IsSymbol(v.ref) == 1 }
func (v Value) IsObject() bool { return C.JS_IsObject(v.ref) == 1 }
func (v Value) IsArray() bool { return C.JS_IsArray(v.ctx.ref, v.ref) == 1 }
func (v Value) IsError() bool { return C.JS_IsError(v.ctx.ref, v.ref) == 1 }
func (v Value) IsFunction() bool { return C.JS_IsFunction(v.ctx.ref, v.ref) == 1 }
func (v Value) IsPromise() bool {
state := C.JS_PromiseState(v.ctx.ref, v.ref)
if state == C.JS_PROMISE_PENDING || state == C.JS_PROMISE_FULFILLED || state == C.JS_PROMISE_REJECTED {
return true
}
return false
}
// func (v Value) IsConstructor() bool { return C.JS_IsConstructor(v.ctx.ref, v.ref) == 1 }