-
Notifications
You must be signed in to change notification settings - Fork 24
Expand file tree
/
Copy pathmodule_bridge.go
More file actions
121 lines (106 loc) · 3.89 KB
/
module_bridge.go
File metadata and controls
121 lines (106 loc) · 3.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package quickjs
import (
"fmt"
"sync/atomic"
"unsafe"
)
/*
#include <stdint.h>
#include "bridge.h"
*/
import "C"
var moduleBuilderIDForceParseFailure atomic.Bool
func cleanupModuleBuilderHandle(ctx *Context, builderID C.int32_t) {
if ctx == nil || ctx.handleStore == nil {
return
}
ctx.handleStore.Delete(int32(builderID))
}
// getContextAndModuleBuilder retrieves context and ModuleBuilder from module private value.
func getContextAndModuleBuilder(ctx *C.JSContext, m *C.JSModuleDef) (*Context, *ModuleBuilder, C.int32_t, error) {
privateValue := C.JS_GetModulePrivateValue(ctx, m)
var builderID C.int32_t
if moduleBuilderIDForceParseFailure.Load() || C.JS_ToInt32(ctx, &builderID, privateValue) < 0 {
C.JS_FreeValue(ctx, privateValue)
return nil, nil, 0, fmt.Errorf("failed to parse module builder id from module private value")
}
C.JS_FreeValue(ctx, privateValue)
goCtx, builderInterface, err := getContextAndObject(ctx, C.int(builderID), errFunctionNotFound)
if err != nil {
cleanupModuleBuilderHandle(goCtx, builderID)
return nil, nil, 0, fmt.Errorf("failed to get context and module builder: %v", err.message)
}
builder, ok := builderInterface.(*ModuleBuilder)
if !ok || builder == nil {
cleanupModuleBuilderHandle(goCtx, builderID)
return nil, nil, 0, fmt.Errorf("failed to get context and module builder: invalid module builder handle")
}
return goCtx, builder, builderID, nil
}
//export goModuleInitProxy
func goModuleInitProxy(ctx *C.JSContext, m *C.JSModuleDef) C.int {
goCtx, builder, builderID, err := getContextAndModuleBuilder(ctx, m)
if err != nil {
return throwModuleError(ctx, err)
}
defer cleanupModuleBuilderHandle(goCtx, builderID)
type materializedModuleExport struct {
spec ValueSpec
value *Value
}
var materializedExports []materializedModuleExport
defer func() {
for _, item := range materializedExports {
if item.value == nil || item.value.ctx != goCtx || isContextValueSpec(item.spec) {
continue
}
item.value.Free()
}
}()
for _, export := range builder.exports {
if export.Spec == nil {
return throwModuleError(ctx, fmt.Errorf("invalid module export value: %s", export.Name))
}
value, matErr := materializeValueSpecSafely(goCtx, export.Spec)
if matErr != nil {
return throwModuleError(ctx, fmt.Errorf("invalid module export value: %s (materialize error: %v)", export.Name, matErr))
}
if value != nil && value.ctx == goCtx {
materializedExports = append(materializedExports, materializedModuleExport{spec: export.Spec, value: value})
}
if value == nil {
return throwModuleError(ctx, fmt.Errorf("invalid module export value: %s (materialize returned nil)", export.Name))
}
if !value.belongsTo(goCtx) {
return throwModuleError(ctx, fmt.Errorf("invalid module export value: %s (materialized in a different context)", export.Name))
}
exportName := C.CString(export.Name)
legacySpec := isContextValueSpec(export.Spec)
rc := C.JS_SetModuleExport(ctx, m, exportName, value.ref)
C.free(unsafe.Pointer(exportName))
if rc < 0 {
// JS_SetModuleExport frees val on failure, so source handles
// must be invalidated to avoid a later double free.
value.ref = C.JS_NewUndefined()
value.borrowed = false
return throwModuleError(ctx, fmt.Errorf("failed to set module export: %s", export.Name))
}
if legacySpec {
// Legacy Export(name, *Value) now keeps source values readable after Build.
// JS_SetModuleExport consumes the original ref, so mark this Go value as a
// borrowed non-owning alias. Lifetime is held by the module export slot,
// and Free only invalidates the Go wrapper without decref.
value.borrowed = true
} else {
value.ref = C.JS_NewUndefined()
value.borrowed = false
}
}
return C.int(0)
}
func forceModuleBuilderIDParseFailureForTest(enable bool) func() {
old := moduleBuilderIDForceParseFailure.Swap(enable)
return func() {
moduleBuilderIDForceParseFailure.Store(old)
}
}