Skip to content

refactor(clone.go): optimize cloning process by introducing unsafeCopyValue function#32

Merged
m-mizutani merged 1 commit intomainfrom
fix/unexported-field-handling
Aug 14, 2025
Merged

refactor(clone.go): optimize cloning process by introducing unsafeCopyValue function#32
m-mizutani merged 1 commit intomainfrom
fix/unexported-field-handling

Conversation

@m-mizutani
Copy link
Owner

No description provided.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @m-mizutani, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refactors and optimizes the internal cloning mechanism, particularly focusing on how unexported fields are handled. The core change involves introducing a new unsafeCopyValue helper function to centralize direct memory copying operations. This update aims to improve the efficiency and robustness of the cloning process by prioritizing safe reflection methods and only resorting to unsafe memory copying when absolutely necessary. The changes ensure that the system can reliably clone complex data structures, including those with unexported fields, without encountering runtime panics.

Highlights

  • New unsafeCopyValue Function: A new utility function, unsafeCopyValue, has been introduced. This function performs direct memory copying between reflect.Values using unsafe operations, centralizing the logic for handling cases where standard reflection cannot access unexported fields.
  • Refactoring of Unsafe Copy Logic: Existing direct unsafe.Pointer and copy calls within the clone method have been replaced with calls to the new unsafeCopyValue function, improving code readability and maintainability by abstracting the low-level memory operations.
  • Improved Handling of Unexported Fields: The cloning logic now prioritizes safe reflection by attempting to use reflect.Value.Set first. It only falls back to unsafeCopyValue when direct setting is not possible (e.g., for unexported fields or non-settable values), enhancing both robustness and performance.
  • Enhanced Test Coverage for Unexported Fields: Comprehensive new test cases have been added to verify that the cloning process correctly handles structs, slices, and pointers involving unexported fields without causing panics, ensuring the reliability of the unsafe operations.
  • Minor Optimization for Nil Pointer Cloning: A minor optimization was applied to nil pointer cloning, changing reflect.New(src.Type()).Elem() to reflect.Zero(src.Type()), which is a more idiomatic and potentially efficient way to create a zero value.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@m-mizutani m-mizutani merged commit 2b5cd2a into main Aug 14, 2025
5 checks passed
@m-mizutani m-mizutani deleted the fix/unexported-field-handling branch August 14, 2025 01:14
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request is a solid refactoring that introduces an unsafeCopyValue function to centralize unsafe memory copy operations, which improves code clarity and maintainability. It also resolves several potential panics when cloning types with unexported fields. The addition of tests for these scenarios is a great step. I have a few suggestions to further enhance the robustness of the new utility function and the completeness of the new tests.

Comment on lines +24 to +32
func unsafeCopyValue(dst, src reflect.Value) {
if !dst.CanAddr() || !src.CanAddr() {
return
}
dstPtr := unsafe.Pointer(dst.UnsafeAddr())
srcPtr := unsafe.Pointer(src.UnsafeAddr())
size := src.Type().Size()
copy((*[1 << 30]byte)(dstPtr)[:size], (*[1 << 30]byte)(srcPtr)[:size])
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The use of (*[1 << 30]byte)(...) to create a slice for copying can panic if the size of the source value exceeds 1GB, as the slice index will be out of bounds. While this may be rare, it represents a potential correctness issue for very large objects.

For Go versions 1.17 and later, it is safer and more robust to use unsafe.Slice, which does not have this arbitrary 1GB limit.

Here is an example of how you could implement it:

func unsafeCopyValue(dst, src reflect.Value) {
	if !dst.CanAddr() || !src.CanAddr() {
		return
	}
	size := src.Type().Size()
	if size == 0 {
		return
	}

	dstPtr := unsafe.Pointer(dst.UnsafeAddr())
	srcPtr := unsafe.Pointer(src.UnsafeAddr())

	dstSlice := unsafe.Slice((*byte)(dstPtr), size)
	srcSlice := unsafe.Slice((*byte)(srcPtr), size)
	copy(dstSlice, srcSlice)
}

cloned := gt.Cast[*structWithUnexported](t, mask.Redact(original))

gt.V(t, cloned.PublicField).Equal("public")
// Main goal: no panic during cloning

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This test correctly checks that cloning doesn't cause a panic, which is great. To make it more robust, I suggest also asserting that the value of the unexported field was copied correctly. Since structWithUnexported is defined locally, you can access its private fields within the test.

Suggested change
// Main goal: no panic during cloning
gt.V(t, cloned.privateField).Equal("private")

// This should not panic
cloned := gt.Cast[[]unexportedStruct](t, mask.Redact(original))

gt.V(t, len(cloned)).Equal(2)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

It's good that you're checking the length of the cloned slice. To make this test more comprehensive, I recommend also asserting the values of the elements within the slice to ensure they have been cloned correctly.

For example:

gt.V(t, cloned[0].value).Equal("item1")
gt.V(t, cloned[1].value).Equal("item2")

// This should not panic
cloned := gt.Cast[*container](t, mask.Redact(original))

gt.V(t, cloned.Data).NotNil()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Checking that the cloned pointer is not nil is a good start. To fully verify the cloning logic, I suggest also asserting that the content of the pointed-to struct has been copied correctly.

For example:

gt.V(t, cloned.Data.content).Equal("test")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant