Skip to content

Commit 8301a0e

Browse files
authored
fix: bug where when adding multiple objects across a yaml tree, we ended up re-using memory which could have downstream effect (#19)
* fix: bug where when adding multiple objects across a yaml tree, we ended up re-using memory which could have downstream effect * chore: rebuild wasm * chore: fix test * chore: clean test
1 parent 6d0098b commit 8301a0e

File tree

10 files changed

+133
-30
lines changed

10 files changed

+133
-30
lines changed

Makefile

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,6 @@
11

22
SOURCE=$(shell find . -iname "*.go")
33

4-
web/src/assets/wasm/lib.wasm: $(SOURCE)
5-
mkdir -p dist
6-
rm -f dist/*
7-
cp "$(shell go env GOROOT)/misc/wasm/wasm_exec.js" web/src/assets/wasm/wasm_exec.js
8-
GOOS=js GOARCH=wasm go build -o ./web/src/assets/wasm/lib.wasm cmd/wasm/functions.go
9-
10-
.PHONY: tinygo web/src/assets/wasm/lib.tinygo.wasm web/src/assets/wasm/lib.wasm
11-
tinygo:
12-
brew tap tinygo-org/tools
13-
brew install tinygo
144

15-
16-
web/src/assets/wasm/lib.tinygo.wasm: $(SOURCE)
17-
echo "Not working: requires regex which is not supported by tinygo"
18-
exit 1
19-
mkdir -p dist
20-
rm -f dist/*
21-
cp "${shell brew --prefix tinygo}/targets/wasm_exec.js" web/src/assets/wasm/wasm_exec.js
22-
GOOS=js GOARCH=wasm tinygo build -target=wasm -o ./web/src/assets/wasm/lib.tinygo.wasm cmd/wasm/functions.go
5+
web/src/assets/wasm/lib.wasm: $(SOURCE)
6+
./build.sh

build.sh

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/bin/bash
2+
readonly SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
3+
readonly REPO_ROOT="$(git rev-parse --show-toplevel)"
4+
5+
# ANSI color codes
6+
GREEN='\033[0;32m'
7+
RED='\033[0;31m'
8+
NC='\033[0m' # No Color
9+
10+
# Validate Go version higher or equal to than this.
11+
REQUIRED_GO_VERSION="1.24.1"
12+
CURRENT_GO_VERSION=$(go version | awk '{print $3}' | sed 's/go//')
13+
14+
if ! echo "$CURRENT_GO_VERSION $REQUIRED_GO_VERSION" | awk '{
15+
split($1, current, ".")
16+
split($2, required, ".")
17+
if ((current[1] > required[1]) || \
18+
(current[1] == required[1] && current[2] > required[2]) || \
19+
(current[1] == required[1] && current[2] == required[2] && current[3] >= required[3])) {
20+
exit 0;
21+
}
22+
exit 1
23+
}'; then
24+
echo -e "${RED}Error: Go version must be $REQUIRED_GO_VERSION or higher, but found $CURRENT_GO_VERSION${NC}"
25+
exit 1
26+
fi
27+
28+
get_file_size() {
29+
if [[ "$OSTYPE" == "darwin"* ]]; then
30+
BYTES=$(stat -f%z "$1")
31+
else
32+
BYTES=$(stat -c%s "$1")
33+
fi
34+
MB=$((BYTES / 1024 / 1024))
35+
echo "$MB MB"
36+
}
37+
38+
# Function to build the WASM binary
39+
build_wasm() {
40+
printf "Installing dependencies... "
41+
(cd "$SCRIPT_DIR" && go mod tidy) > /dev/null 2>&1
42+
printf "Done\n"
43+
44+
printf "Building WASM binary..."
45+
GOOS=js GOARCH=wasm go build -o ./web/src/assets/wasm/lib.wasm cmd/wasm/functions.go
46+
SIZE="$(get_file_size "./web/src/assets/wasm/lib.wasm")"
47+
printf " Done (%s)\n" "$SIZE"
48+
cp "$(go env GOROOT)/lib/wasm/wasm_exec.js" "./web/src/assets/wasm/wasm_exec.js"
49+
50+
}
51+
52+
# Initial build
53+
build_wasm "$VERSION"

pkg/overlay/apply.go

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,22 +92,22 @@ func applyUpdateAction(root *yaml.Node, action Action) error {
9292
nodes := p.Query(root)
9393

9494
for _, node := range nodes {
95-
if err := updateNode(node, action.Update); err != nil {
95+
if err := updateNode(node, &action.Update); err != nil {
9696
return err
9797
}
9898
}
9999

100100
return nil
101101
}
102102

103-
func updateNode(node *yaml.Node, updateNode yaml.Node) error {
103+
func updateNode(node *yaml.Node, updateNode *yaml.Node) error {
104104
mergeNode(node, updateNode)
105105
return nil
106106
}
107107

108-
func mergeNode(node *yaml.Node, merge yaml.Node) {
108+
func mergeNode(node *yaml.Node, merge *yaml.Node) {
109109
if node.Kind != merge.Kind {
110-
*node = merge
110+
*node = *clone(merge)
111111
return
112112
}
113113
switch node.Kind {
@@ -122,7 +122,7 @@ func mergeNode(node *yaml.Node, merge yaml.Node) {
122122

123123
// mergeMappingNode will perform a shallow merge of the merge node into the main
124124
// node.
125-
func mergeMappingNode(node *yaml.Node, merge yaml.Node) {
125+
func mergeMappingNode(node *yaml.Node, merge *yaml.Node) {
126126
NextKey:
127127
for i := 0; i < len(merge.Content); i += 2 {
128128
mergeKey := merge.Content[i].Value
@@ -131,16 +131,39 @@ NextKey:
131131
for j := 0; j < len(node.Content); j += 2 {
132132
nodeKey := node.Content[j].Value
133133
if nodeKey == mergeKey {
134-
mergeNode(node.Content[j+1], *mergeValue)
134+
mergeNode(node.Content[j+1], mergeValue)
135135
continue NextKey
136136
}
137137
}
138138

139-
node.Content = append(node.Content, merge.Content[i], mergeValue)
139+
node.Content = append(node.Content, merge.Content[i], clone(mergeValue))
140140
}
141141
}
142142

143143
// mergeSequenceNode will append the merge node's content to the original node.
144-
func mergeSequenceNode(node *yaml.Node, merge yaml.Node) {
145-
node.Content = append(node.Content, merge.Content...)
144+
func mergeSequenceNode(node *yaml.Node, merge *yaml.Node) {
145+
node.Content = append(node.Content, clone(merge).Content...)
146+
}
147+
148+
func clone(node *yaml.Node) *yaml.Node {
149+
newNode := &yaml.Node{
150+
Kind: node.Kind,
151+
Style: node.Style,
152+
Tag: node.Tag,
153+
Value: node.Value,
154+
Anchor: node.Anchor,
155+
HeadComment: node.HeadComment,
156+
LineComment: node.LineComment,
157+
FootComment: node.FootComment,
158+
}
159+
if node.Alias != nil {
160+
newNode.Alias = clone(node.Alias)
161+
}
162+
if node.Content != nil {
163+
newNode.Content = make([]*yaml.Node, len(node.Content))
164+
for i, child := range node.Content {
165+
newNode.Content[i] = clone(child)
166+
}
167+
}
168+
return newNode
146169
}

pkg/overlay/compare_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func TestCompare(t *testing.T) {
5454
assert.NoError(t, err)
5555

5656
// Uncomment this if we've improved the output
57-
os.WriteFile("testdata/overlay-generated.yaml", []byte(o2s), 0644)
57+
//os.WriteFile("testdata/overlay-generated.yaml", []byte(o2s), 0644)
5858
assert.Equal(t, o1s, o2s)
5959

6060
// round trip it

pkg/overlay/testdata/openapi-overlayed.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,14 @@ paths:
6969
X-Optional-Header:
7070
schema:
7171
type: string
72+
x-drop: true
7273
/authenticate:
7374
post:
7475
operationId: authenticate
7576
summary: Authenticate with the API by providing a username and password.
7677
security: []
7778
tags:
79+
- dont-add-x-drop-false
7880
- authentication
7981
requestBody:
8082
required: true
@@ -103,6 +105,7 @@ paths:
103105
$ref: "#/components/responses/APIError"
104106
default:
105107
$ref: "#/components/responses/UnknownError"
108+
x-drop: false
106109
/drinks:
107110
x-speakeasy-note:
108111
"$ref": "./removeNote.yaml"
@@ -138,6 +141,7 @@ paths:
138141
$ref: "#/components/responses/APIError"
139142
default:
140143
$ref: "#/components/responses/UnknownError"
144+
x-drop: true
141145
/order:
142146
post:
143147
operationId: createOrder
@@ -195,6 +199,7 @@ paths:
195199
$ref: "#/components/responses/APIError"
196200
default:
197201
$ref: "#/components/responses/UnknownError"
202+
x-drop: true
198203
/webhooks/subscribe:
199204
post:
200205
operationId: subscribeToWebhooks
@@ -224,6 +229,7 @@ paths:
224229
$ref: "#/components/responses/APIError"
225230
default:
226231
$ref: "#/components/responses/UnknownError"
232+
x-drop: true
227233
webhooks:
228234
stockUpdate:
229235
post:

pkg/overlay/testdata/openapi.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ paths:
7171
summary: Authenticate with the API by providing a username and password.
7272
security: []
7373
tags:
74+
- dont-add-x-drop-false
7475
- authentication
7576
requestBody:
7677
required: true

pkg/overlay/testdata/overlay-generated.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ actions:
1212
servers:
1313
- url: http://localhost:35123
1414
description: The default server.
15+
- target: $["paths"]["/anything/selectGlobalServer"]["get"]
16+
update:
17+
x-drop: true
18+
- target: $["paths"]["/authenticate"]["post"]
19+
update:
20+
x-drop: false
1521
- target: $["paths"]["/drinks"]
1622
update:
1723
x-speakeasy-note:
@@ -20,3 +26,12 @@ actions:
2026
remove: true
2127
- target: $["paths"]["/drink/{name}"]["get"]
2228
remove: true
29+
- target: $["paths"]["/ingredients"]["get"]
30+
update:
31+
x-drop: true
32+
- target: $["paths"]["/order"]["post"]
33+
update:
34+
x-drop: true
35+
- target: $["paths"]["/webhooks/subscribe"]["post"]
36+
update:
37+
x-drop: true

pkg/overlay/testdata/overlay.yaml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,11 @@ actions:
4242
servers:
4343
- url: http://localhost:35123
4444
description: The default server.
45-
x-top-level-extension: true
45+
- target: $.paths.*[[email protected]]
46+
description: 'add x-drop: true to all paths'
47+
update:
48+
x-drop: true
49+
- target: $.paths.*[?length(@.tags[?(@ == "dont-add-x-drop-false")]) > 0]
50+
description: 'add x-drop: false to any operation which has the dont-add-x-drop-false tag'
51+
update:
52+
x-drop: false

web/src/assets/wasm/lib.wasm

325 KB
Binary file not shown.

web/src/assets/wasm/wasm_exec.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
if (!globalThis.fs) {
1515
let outputBuf = "";
1616
globalThis.fs = {
17-
constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
17+
constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1, O_DIRECTORY: -1 }, // unused
1818
writeSync(fd, buf) {
1919
outputBuf += decoder.decode(buf);
2020
const nl = outputBuf.lastIndexOf("\n");
@@ -73,6 +73,14 @@
7373
}
7474
}
7575

76+
if (!globalThis.path) {
77+
globalThis.path = {
78+
resolve(...pathSegments) {
79+
return pathSegments.join("/");
80+
}
81+
}
82+
}
83+
7684
if (!globalThis.crypto) {
7785
throw new Error("globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)");
7886
}
@@ -208,10 +216,16 @@
208216
return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
209217
}
210218

219+
const testCallExport = (a, b) => {
220+
this._inst.exports.testExport0();
221+
return this._inst.exports.testExport(a, b);
222+
}
223+
211224
const timeOrigin = Date.now() - performance.now();
212225
this.importObject = {
213226
_gotest: {
214227
add: (a, b) => a + b,
228+
callExport: testCallExport,
215229
},
216230
gojs: {
217231
// Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)

0 commit comments

Comments
 (0)