MCPcopy Index your code
hub / github.com/buke/quickjs-go

github.com/buke/quickjs-go @v0.7.7

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.7.7 ↗ · + Follow
2,700 symbols 12,619 edges 176 files 914 documented · 34% updated 12d agov0.7.6 · 2026-04-13★ 172
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

quickjs-go

English | 简体中文

Test codecov Go Report Card GoDoc FOSSA Status

Go 语言的 QuickJS 绑定库:快速、小型、可嵌入的 ES2020 JavaScript 解释器。

⚠️ 此项目尚未准备好用于生产环境。请自行承担使用风险。API 可能会随时更改。

功能特性

  • 执行 JavaScript 脚本
  • 编译脚本到字节码并执行字节码
  • 在 Go 中操作 JavaScript 值和对象
  • 绑定 Go 函数到 JavaScript 同步/异步函数
  • 简单的异常抛出和捕获
  • Go 值与 JavaScript 值的 Marshal/Unmarshal
  • 完整的 TypedArray 支持 (Int8Array, Uint8Array, Float32Array 等)
  • 使用 ClassBuilder 从 Go 创建 JavaScript 类
  • 使用 ModuleBuilder 从 Go 创建 JavaScript 模块
  • 跨平台支持: 通过 cgo 直接编译仓库内 vendored 的 quickjs-ng 源码;ubuntu-latest、windows-latest、macos-latest 已在 Test CI 中覆盖并当前通过测试。 Windows 环境通常需要先安装并配置 MSYS2 等工具链(如 gcc、make、pkg-config),可参考 Issue #151

Benchmarks

以下基准测试结果由 benchcmp 工具生成。如需重新生成,请运行 cd benchcmp && go run . 并更新此文件。

默认 cd benchcmp && go run . 输出如下:

Factorial Calculation

Computing factorial(10) 1,000,000 times

Iteration quickjs-go(cgo / QuickJS-ng) GOJA(pure Go) ModerncQuickJS(ccgo / QuickJS) QJS(Wasm / wazero) V8go(cgo / V8 JIT)
1 213.363ms 712.622ms 879.695ms 626.162ms 37.025ms
2 309.421ms 728.404ms 878.465ms 626.668ms 22.883ms
3 208.848ms 697.569ms 913.305ms 629.525ms 22.682ms
4 217.621ms 672.554ms 951.755ms 627.514ms 22.803ms
5 317.197ms 695.230ms 964.052ms 627.884ms 23.183ms
Average 253.290ms 701.276ms 917.455ms 627.551ms 25.715ms
Total 1.266s 3.506s 4.587s 3.138s 128.576ms
Speed vs quickjs-go(cgo / QuickJS-ng) 1.00x 0.36x 0.28x 0.40x 9.85x

AreWeFastYet V8-V7

Metric quickjs-go(cgo / QuickJS-ng) GOJA(pure Go) ModerncQuickJS(ccgo / QuickJS) QJS(Wasm / wazero) V8go(cgo / V8 JIT)
Richards 1425 453 259 470 51086
DeltaBlue 1398 461 285 561 191967
Crypto 1021 287 304 382 97807
RayTrace 1628 588 519 760 208751
EarleyBoyer 3216 1220 774 1163 152728
RegExp 459 415 199 211 21456
Splay 3989 2049 1324 2331 68909
NavierStokes 1800 450 529 625 67832
Score (version 7) 1560 599 434 638 86266
Duration (seconds) 35.108s 61.477s 70.983s 57.522s 20.052s
Score vs quickjs-go(cgo / QuickJS-ng) 1.00x 0.38x 0.28x 0.41x 55.30x
Speed vs quickjs-go(cgo / QuickJS-ng) 1.00x 0.57x 0.49x 0.61x 1.75x

如果想看 benchmark 命令、套件选择和完整输出格式说明,可继续参考 benchcmp/README.md

本次 Benchmarks 运行环境:Apple M4,32GB RAM,macOS。

上游同步

本项目已将上游运行时源码从 bellard/quickjs 迁移到 quickjs-ng/quickjs

这次迁移的主要原因是长期可维护性,而不是对 quickjs-go 的 Go API 做出调整。quickjs-ng 在社区协作、发布节奏、跨平台支持,以及测试和 CI 覆盖方面更适合作为绑定库项目的长期上游。对于一个需要将引擎源码 vendored 到仓库中、并通过 cgo 在 Linux、macOS 和 Windows 上持续构建与测试的 Go 绑定库来说,这些工程能力会直接影响日常维护成本和版本升级效率。

此次迁移仅涉及底层 vendored JavaScript 引擎实现的上游切换;quickjs-go 对外提供的 Go API 设计与使用方式保持不变,现有 Go 侧集成代码无需因本次迁移而调整。

deps/quickjs 中的运行时源码不再通过 git submodule 更新,而是按 quickjs-ng GitHub release 进行同步。

使用指南

生命周期、内存与并发约束

  • QuickJS 本身不是线程安全的;同一个 Runtime 及其 Context 必须由同一个串行 owner goroutine 创建、使用和关闭。
  • 库默认对触达 QuickJS 的 API 强制执行 owner goroutine 校验(非 owner 调用按 fail-closed 语义拒绝);可以使用 WithOwnerGoroutineCheck(false) 显式关闭 owner goroutine 校验。注意 WithOwnerGoroutineCheck(false) 是显式不安全开关,仅适用于由你自己的调度器在外部严格串行化所有 QuickJS 访问的场景,一旦该前提被破坏,跨 goroutine 调用可能与 QuickJS 内部状态竞争并导致内存破坏。
  • 如需额外校验 Runtime 是否固定在同一个 OS 线程上,可启用 WithStrictOSThread(true);该选项只负责校验,不会自动绑定线程。如果启用了该模式,请由调用方在 owner goroutine 中自行调用 runtime.LockOSThread(),并在创建 Runtime 之前完成绑定。
  • Runtime.NewContext() 保持默认宿主 bootstrap 行为(注册 std/os 并注入全局 setTimeout/clearTimeout)。如需更细粒度控制,可使用 Runtime.NewBareContext() 后手动调用 BootstrapStdOS / BootstrapTimers,或使用 Runtime.NewContextWithOptions(...) 配合 DefaultBootstrapMinimalBootstrapNoBootstrap
  • Runtime.NewContextWithOptions(...) 中,启用 WithBootstrapTimers(true) 会隐式启用 std/os 注册,因为计时器注入依赖从 os 模块导入 setTimeout/clearTimeout
  • 对您创建或接收的 *Value 对象调用 value.Free();Runtime 和 Context 使用完毕后通过 Close() 清理。
  • Value 的上下文引用已失效时,Value.Free() 会 fail-closed,避免关闭后的不安全 cgo 调用。
  • Value 边界 API 在 foreign-context 或 closed/orphan context 输入下采用 fail-closed:Set/SetIdx 变为 no-op,Call/Execute/CallConstructor/已废弃的 Context.Invoke 返回 nil
  • Marshal/Unmarshal 在无效 context/value 输入下采用 fail-closed:对 nil/closed context、nil 源值或跨 Context 值返回受控错误,不再向 C 层传递无效 ref。
  • Context.Close() 之后,边界查询采用 fail-closed:Runtime() 返回 nilHasException() 返回 falseException() 返回 nilLoop() 变为 no-op,Schedule() 返回 false
  • bridge 映射和 handleStore 在遇到损坏条目(错误类型)时采用 fail-closed,避免 panic 级崩溃。
  • 类实例 opaque 数据通过 runtime 级对象身份(contextID + handleID)解析,finalizer 与 Value.GetGoObject() 使用确定性归属定位。
  • Go 回调在 function/method/getter/setter 代理里返回 nil 时,会统一映射为 JavaScript undefined
  • 模块初始化阶段会校验导出值:导出值必须非 nil、上下文可用且属于当前初始化上下文。
  • 谨慎使用 runtime.SetFinalizer(),因为它可能干扰 QuickJS GC。
  • 如需并发或隔离,建议使用线程池预初始化运行时,或为不同任务/用户管理独立 Runtime/Context。
  • 尽可能复用 Runtime/Context,减少 Go 与 JS 值之间的频繁转换;高频脚本建议使用字节码编译。

错误处理

  • 使用 Value.IsException()Context.HasException() 检查异常
  • 使用 Context.Exception() 获取异常作为 Go error
  • 始终对返回的值调用 defer value.Free() 以防止内存泄漏
  • 在可能抛出异常的操作后检查 Context.HasException()

用法

import "github.com/buke/quickjs-go"

Context Bootstrap 模式

rt := quickjs.NewRuntime()
defer rt.Close()

// 默认模式(与历史行为兼容)
ctxDefault := rt.NewContext()
defer ctxDefault.Close()

// 裸 Context(不注册 std/os,不注入计时器)
ctxBare := rt.NewBareContext()
defer ctxBare.Close()

// 手动应用 bootstrap
quickjs.BootstrapStdOS(ctxBare)
quickjs.BootstrapTimers(ctxBare)

// 通过选项选择 bootstrap
ctxMinimal := rt.NewContextWithOptions(quickjs.MinimalBootstrap())
defer ctxMinimal.Close()

执行脚本

package main

import (
    "fmt"
    "runtime"

    "github.com/buke/quickjs-go"
)

func main() {
    runtime.LockOSThread()
    defer runtime.UnlockOSThread()

    // 创建新的运行时
    rt := quickjs.NewRuntime()
    defer rt.Close()

    // 创建新的上下文
    ctx := rt.NewContext()
    defer ctx.Close()

    ret := ctx.Eval("'Hello ' + 'QuickJS!'")
    defer ret.Free()

    if ret.IsException() {
        err := ctx.Exception()
        println(err.Error())
        return
    }

    fmt.Println(ret.ToString())
}

获取/设置 JavaScript 对象

package main

import (
    "fmt"

    "github.com/buke/quickjs-go"
)

func main() {
    // 创建新的运行时
    rt := quickjs.NewRuntime()
    defer rt.Close()

    // 创建新的上下文
    ctx := rt.NewContext()
    defer ctx.Close()

    test := ctx.NewObject()
    test.Set("A", ctx.NewString("String A"))
    test.Set("B", ctx.NewString("String B"))
    test.Set("C", ctx.NewString("String C"))
    ctx.Globals().Set("test", test)

    ret := ctx.Eval(`Object.keys(test).map(key => test[key]).join(" ")`)
    defer ret.Free()

    if ret.IsException() {
        err := ctx.Exception()
        fmt.Println("Error:", err.Error())
        return
    }

    fmt.Println(ret.ToString())
}

绑定 Go 函数到 JavaScript 同步/异步函数

package main

import (
    "fmt"
    "time"

    "github.com/buke/quickjs-go"
)

func main() {
    // 创建新的运行时
    rt := quickjs.NewRuntime()
    defer rt.Close()

    // 创建新的上下文
    ctx := rt.NewContext()
    defer ctx.Close()

    // 创建新对象
    test := ctx.NewObject()
    // 将属性绑定到对象
    test.Set("A", ctx.NewString("String A"))
    test.Set("B", ctx.NewInt32(0))
    test.Set("C", ctx.NewBool(false))
    // 将 go 函数绑定到 js 对象
    test.Set("hello", ctx.NewFunction(func(ctx *quickjs.Context, this *quickjs.Value, args []*quickjs.Value) *quickjs.Value {
        return ctx.NewString("Hello " + args[0].ToString())
    }))

    // 将 "test" 对象绑定到全局对象
    ctx.Globals().Set("test", test)

    // 通过 js 调用 js 函数(同步)
    js_ret := ctx.Eval(`test.hello("Javascript!")`)
    defer js_ret.Free()
    if js_ret.IsException() {
        err := ctx.Exception()
        fmt.Println("Error:", err.Error())
        return
    }
    fmt.Println(js_ret.ToString())

    // 通过 go 调用 js 函数(同步)
    go_ret := test.Call("hello", ctx.NewString("Golang!"))
    defer go_ret.Free()
    if go_ret.IsException() {
        err := ctx.Exception()
        fmt.Println("Error:", err.Error())
        return
    }
    fmt.Println(go_ret.ToString())

    // --- 同步 Promise 示例(立即 resolve/reject,不开 goroutine)---
    ctx.Globals().Set("syncPromise", ctx.NewFunction(func(ctx *quickjs.Context, this *quickjs.Value, args []*quickjs.Value) *quickjs.Value {
        return ctx.NewPromise(func(resolve, reject func(*quickjs.Value)) {
            // 可以在 executor 中直接同步 resolve:
            msg := ctx.NewString("Hello from sync Promise")
            defer msg.Free()
            resolve(msg)

            // 如需同步失败,可以改为:
            // errVal := ctx.NewString("sync error")
            // defer errVal.Free()
            // reject(errVal)
        })
    }))

    syncPromise := ctx.Eval(`syncPromise()`)
    defer syncPromise.Free()

    syncRet := ctx.Await(syncPromise)
    defer syncRet.Free()
    if syncRet.IsException() {
        err := ctx.Exception()
        fmt.Println("Error:", err.Error())
        return
    }
    fmt.Println(syncRet.ToString())

    // --- 使用 Function + Promise + 调度器 的异步示例 ---
    ctx.Globals().Set("testAsync", ctx.NewFunction(func(ctx *quickjs.Context, this *quickjs.Value, args []*quickjs.Value) *quickjs.Value {
        return ctx.NewPromise(func(resolve, reject func(*quickjs.Value)) {
            // 耗时或阻塞操作可以放在 goroutine 中
            go func() {
                time.Sleep(10 * time.Millisecond)

                // 但 QuickJS/Context API 必须回到 Context 线程上
                // 通过 ctx.Schedule 调度执行
                ctx.Schedule(func(inner *quickjs.Context) {
                    value := inner.NewString("Hello Async Function!")
                    defer value.Free()
                    resolve(value)
                })
            }()
        })
    }))

    // 从 JS 角度看,这只是一个返回 Promise 的普通函数
    promiseResult := ctx.Eval(`testAsync()`)
    defer promiseResult.Free()

    // ctx.Await 内部会驱动 QuickJS 的 pending-job 队列
    // 以及 Context 级调度器,因此如果你只关心 Promise
    // 的最终结果,通常不需要单独调用 ctx.Loop()
    asyncRet := ctx.Await(promiseResult)
    defer asyncRet.Free()

    if asyncRet.IsException() {
        err := ctx.Exception()
        fmt.Println("Error:", err.Error())
        return
    }

    fmt.Println(asyncRet.ToString())

    // 输出:
    // Hello Javascript!
    // Hello Golang!
    // Hello from sync Promise
    // Hello Async Function!
}

// 注意:
// - 线程归属由调用方负责;如果一个 Runtime 必须固定在同一个 OS 线程,请由调用方自行在 owner goroutine 中调用 runtime.LockOSThread()。
// - WithOwnerGoroutineCheck(false) 是不安全开关,只应在你能保证外部串行化 QuickJS 访问时使用。
// - 不要在 goroutine 中直接调用 Context 或任何 QuickJS API。
// - 所有 QuickJS 相关操作都应该通过 ctx.Schedule 调度回 Context 线程后再执行。
// - ctx.Await 会在内部驱动 pending jobs 和调度器,直至 Promise 解决。

错误处理

package main

import (
    "fmt"
    "errors"

    "github.com/buke/quickjs-go"
)

func main() {
    // 创建新的运行时
    rt := quickjs.NewRuntime()
    defer rt.Close()

    // 创建新的上下文
    ctx := rt.NewContext()
    defer ctx.Close()

    ctx.Globals().SetFunction("A", func(ctx *quickjs.Context, this *quickjs.Value, args []*quickjs.Value) *quickjs.Value {
        // 抛出错误
        return ctx.ThrowError(errors.New("expected error"))
    })

    result := ctx.Eval("A()")
    defer result.Free()

    if result.IsException() {
        actual := ctx.Exception()
        fmt.Println(actual.Error())
    }
}

TypedArray 支持

QuickJS-Go 提供对 JavaScript TypedArray 的支持,实现 Go 和 JavaScript 之间的二进制数据处理。

从 Go 创建 TypedArray

```go package main

import ( "fmt" "github.com/buke/quickjs-go" )

func main() { rt := quickjs.NewRuntime() defer rt.Close() ctx := rt.NewContext() defer ctx.Close()

// 从 Go 切片创建各种 TypedArray
int8Data := []int8{-128, -1, 0, 1, 127}
int8Array := ctx.NewInt8Array(int8Data)

uint8Data := []uint8{0, 128, 255}
uint8Array := ctx.NewUint8Array(uint8Data)

float32Data := []float32{-3.14, 0.0, 2.718, 100.5}
float32Array := ctx.NewFloat32Array(float32Data)

int64Data := []int64{-9223372036854775808, 0, 9223372036854775807}
bigInt64Array := ctx.NewBigInt64Array(int64Data)

// 将 TypedArray 设置为全局变量
ctx.Globals().Set("int8Array", int8Array)
ctx.Globals().Set("uint8Array", uint8Array)
ctx.Globals().Set("float32Array", float32Array)
ctx.Globals().Set("bigInt64Array", bigInt64Array)

// 在 JavaScript 中使用
result := ctx.Eval(`
    // 检查类型
    const results = {
        int8Type: int8Array instanceof Int8Array,
        uint8Type: uint8Array instanceof Uint8Arr

Extension points exported contracts — how you extend this code

ValueSpec (Interface)
ValueSpec describes how to materialize a JavaScript value for a target Context. Module/Class builders use ValueSpec to s [3 …
value_spec.go
Marshaler (Interface)
Marshaler is the interface implemented by types that can marshal themselves into a JavaScript value. [3 implementers]
marshal.go
ClassFinalizer (Interface)
============================================================================= CLASS FINALIZER INTERFACE ================ [2 …
class.go
InterruptHandler (FuncType)
InterruptHandler is a function type for interrupt handler. Return != 0 if the JS code needs to be interrupted
runtime.go
ReflectOption (FuncType)
ReflectOption configures ReflectOptions using functional options pattern
class_reflect.go
EvalOption (FuncType)
(no doc)
context.go
Unmarshaler (Interface)
Unmarshaler is the interface implemented by types that can unmarshal a JavaScript value into themselves. [3 implementers]
marshal.go
ClassConstructorFunc (FuncType)
============================================================================= CLASS-RELATED FUNCTION TYPES - MODIFIED FO
class.go

Core symbols most depended-on inside this repo

assert
called by 1107
deps/quickjs/tests/assert.js
Free
called by 853
atom.go
IsException
called by 470
value.go
Equal
called by 387
value.go
Eval
called by 328
context.go
ToString
called by 279
atom.go
Close
called by 267
runtime.go
NewContext
called by 246
runtime.go

Shape

Function 2,128
Method 408
Class 75
Struct 66
FuncType 10
Enum 6
Interface 6
TypeAlias 1

Languages

TypeScript38%
Go32%
C23%
C++7%

Modules by API surface

benchcmp/assets/v8-v7/earley-boyer.js312 symbols
deps/quickjs/quickjs-libc.c181 symbols
value.go134 symbols
benchcmp/assets/v8-v7/crypto.js131 symbols
context.go126 symbols
deps/quickjs/cutils.h110 symbols
deps/quickjs/repl.js108 symbols
runtime.go99 symbols
deps/quickjs/tests/microbench.js86 symbols
deps/quickjs/run-test262.c83 symbols
deps/quickjs/tests/test_language.js75 symbols
deps/quickjs/unicode_gen.c66 symbols

For agents

$ claude mcp add quickjs-go \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact