Repository: goccy/go-json (go)

Issue:
Panic caused by omitempty in an embedded struct
This problem can be reproduced with the following code:
```go
package main

import (
	"fmt"

	json "github.com/goccy/go-json"
)

type CustomString string

type EmbedStruct struct {
	CustomString1 CustomString `json:"custom_string1,omitempty"` // causes a panic when zero
}

type MainStruct struct {
	EmbedStruct
	Hello         string       `json:"hello"`
	CustomString2 CustomString `json:"custom_string2,omitempty"` // works without any problems
}

func (m MainStruct) MarshalJSON() ([]byte, error) {
	// substitute is needed to add extra info and avoid recursion
	type substitute MainStruct

	return json.Marshal(struct {
		substitute
		ExtraInfo string `json:"extra_info"`
	}{substitute: substitute(m), ExtraInfo: "test"})
}

func main() {
	v := MainStruct{
		// uncomment this to make it work
		//EmbedStruct: EmbedStruct{
		//CustomString1: "test",
		//},
		Hello: "123",
	}

	res, err := json.Marshal(v)
	fmt.Println(string(res), err)
}
```

Panic information:
```
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4f165c]

goroutine 1 [running]:
github.com/goccy/go-json/internal/encoder/vm.Run(0xc0001044e0, {0xc000122800?, 0x0?, 0x0?}, 0x0?)
	github.com/goccy/go-json@v0.9.10/internal/encoder/vm/vm.go:26 +0x7c
github.com/goccy/go-json.encodeRunCode(0xc00012e5d8?, {0xc000122800?, 0x203000?, 0xc000024100?}, 0xc000122c00?)
	github.com/goccy/go-json@v0.9.10/encode.go:310 +0x68
github.com/goccy/go-json.encode(0xc0001044e0, {0x530a20, 0xc0000240c0})
	github.com/goccy/go-json@v0.9.10/encode.go:235 +0x233
github.com/goccy/go-json.marshal({0x530a20, 0xc0000240c0}, {0x0, 0x0, 0xc00012e6c8?})
	github.com/goccy/go-json@v0.9.10/encode.go:150 +0xb9
github.com/goccy/go-json.MarshalWithOption(...)
	github.com/goccy/go-json@v0.9.10/json.go:186
github.com/goccy/go-json.Marshal({0x530a20?, 0xc0000240c0?})
	github.com/goccy/go-json@v0.9.10/json.go:171 +0x2a
main.MainStruct.MarshalJSON({{{0x0, 0x0}}, {0x542a6c, 0x3}, {0x0, 0x0}})
	./main.go:25 +0xaa
github.com/goccy/go-json/internal/encoder.AppendMarshalJSON(0xc000104410, 0xc0001260f0, {0xc000122000, 0x0, 0x400}, {0x537880?, 0xc00007e150?})
	github.com/goccy/go-json@v0.9.10/internal/encoder/encoder.go:430 +0x49f
github.com/goccy/go-json/internal/encoder/vm.appendMarshalJSON(0x0?, 0x0?, {0xc000122000?, 0x0?, 0x0?}, {0x537880?, 0xc00007e150?})
	github.com/goccy/go-json@v0.9.10/internal/encoder/vm/util.go:152 +0x28
github.com/goccy/go-json/internal/encoder/vm.Run(0xc000104410, {0xc000122000?, 0x0?, 0x400?}, 0xc000128000?)
	github.com/goccy/go-json@v0.9.10/internal/encoder/vm/vm.go:271 +0x2673
github.com/goccy/go-json.encodeRunCode(0xc00007ae08?, {0xc000122000?, 0x674e60?, 0xc000024080?}, 0xc000122400?)
	github.com/goccy/go-json@v0.9.10/encode.go:310 +0x68
github.com/goccy/go-json.encode(0xc000104410, {0x537880, 0xc00007e150})
	github.com/goccy/go-json@v0.9.10/encode.go:235 +0x233
github.com/goccy/go-json.marshal({0x537880, 0xc00007e150}, {0x0, 0x0, 0xc00007af40?})
	github.com/goccy/go-json@v0.9.10/encode.go:150 +0xb9
github.com/goccy/go-json.MarshalWithOption(...)
	github.com/goccy/go-json@v0.9.10/json.go:186
github.com/goccy/go-json.Marshal({0x537880?, 0xc00007e150?})
	github.com/goccy/go-json@v0.9.10/json.go:171 +0x2a
main.main()
	./main.go:40 +0x6d
```

What I've noticed so far:
- There's no panic when `type substitute MainStruct` isn't used
- There's no panic when `omitempty` isn't used by `CustomString1` in `EmbedStruct`
- There's no panic when `CustomString1` is non-zero
- It works without any problems when `encoding/json` is used

Pull request:
fix: fixed an issue that could not set the correct NextField for fields in the embedded structure
fix #391

before:
![before](https://user-images.githubusercontent.com/1029249/224690048-a2079910-5b52-4e97-ba50-6c8452281472.png)

after:
![after](https://user-images.githubusercontent.com/1029249/224690086-efe0dcbf-3364-4ab5-bfda-e02e8744cb58.png)


Patch:
diff --git a/internal/encoder/code.go b/internal/encoder/code.go
index 8d62a9c..66425a8 100644
--- a/internal/encoder/code.go
+++ b/internal/encoder/code.go
@@ -397,7 +397,10 @@ func (c *StructCode) lastFieldCode(field *StructFieldCode, firstField *Opcode) *
 func (c *StructCode) lastAnonymousFieldCode(firstField *Opcode) *Opcode {
 	// firstField is special StructHead operation for anonymous structure.
 	// So, StructHead's next operation is truly struct head operation.
-	lastField := firstField.Next
+	for firstField.Op == OpStructHead {
+		firstField = firstField.Next
+	}
+	lastField := firstField
 	for lastField.NextField != nil {
 		lastField = lastField.NextField
 	}
@@ -437,11 +440,6 @@ func (c *StructCode) ToOpcode(ctx *compileContext) Opcodes {
 		}
 		if isEndField {
 			endField := fieldCodes.Last()
-			if isEmbeddedStruct(field) {
-				firstField.End = endField
-				lastField := c.lastAnonymousFieldCode(firstField)
-				lastField.NextField = endField
-			}
 			if len(codes) > 0 {
 				codes.First().End = endField
 			} else {
@@ -698,7 +696,15 @@ func (c *StructFieldCode) addStructEndCode(ctx *compileContext, codes Opcodes) O
 		Indent:     ctx.indent,
 	}
 	codes.Last().Next = end
-	codes.First().NextField = end
+	code := codes.First()
+	for code.Op == OpStructField || code.Op == OpStructHead {
+		code = code.Next
+	}
+	for code.NextField != nil {
+		code = code.NextField
+	}
+	code.NextField = end
+
 	codes = codes.Add(end)
 	ctx.incOpcodeIndex()
 	return codes


Test patch:
diff --git a/encode_test.go b/encode_test.go
index f3b57bf..d328c02 100644
--- a/encode_test.go
+++ b/encode_test.go
@@ -2630,6 +2630,40 @@ func TestCustomMarshalForMapKey(t *testing.T) {
 	assertEq(t, "custom map key", string(expected), string(got))
 }
 
+func TestIssue391(t *testing.T) {
+	type A struct {
+		X string `json:"x,omitempty"`
+	}
+	type B struct {
+		A
+	}
+	type C struct {
+		X []int `json:"x,omitempty"`
+	}
+	for _, tc := range []struct {
+		name string
+		in   interface{}
+		out  string
+	}{
+		{in: struct{ B }{}, out: "{}"},
+		{in: struct {
+			B
+			Y string `json:"y"`
+		}{}, out: `{"y":""}`},
+		{in: struct {
+			Y string `json:"y"`
+			B
+		}{}, out: `{"y":""}`},
+		{in: struct{ C }{}, out: "{}"},
+	} {
+		t.Run(tc.name, func(t *testing.T) {
+			b, err := json.Marshal(tc.in)
+			assertErr(t, err)
+			assertEq(t, "unexpected result", tc.out, string(b))
+		})
+	}
+}
+
 func TestIssue417(t *testing.T) {
 	x := map[string]string{
 		"b": "b",