1// Copyright 2020 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package unsafeheader_test
6
7import (
8	"bytes"
9	"internal/unsafeheader"
10	"reflect"
11	"testing"
12	"unsafe"
13)
14
15// TestTypeMatchesReflectType ensures that the name and layout of the
16// unsafeheader types matches the corresponding Header types in the reflect
17// package.
18func TestTypeMatchesReflectType(t *testing.T) {
19	t.Run("Slice", func(t *testing.T) {
20		testHeaderMatchesReflect(t, unsafeheader.Slice{}, reflect.SliceHeader{})
21	})
22
23	t.Run("String", func(t *testing.T) {
24		testHeaderMatchesReflect(t, unsafeheader.String{}, reflect.StringHeader{})
25	})
26}
27
28func testHeaderMatchesReflect(t *testing.T, header, reflectHeader any) {
29	h := reflect.TypeOf(header)
30	rh := reflect.TypeOf(reflectHeader)
31
32	for i := 0; i < h.NumField(); i++ {
33		f := h.Field(i)
34		rf, ok := rh.FieldByName(f.Name)
35		if !ok {
36			t.Errorf("Field %d of %v is named %s, but no such field exists in %v", i, h, f.Name, rh)
37			continue
38		}
39		if !typeCompatible(f.Type, rf.Type) {
40			t.Errorf("%v.%s has type %v, but %v.%s has type %v", h, f.Name, f.Type, rh, rf.Name, rf.Type)
41		}
42		if f.Offset != rf.Offset {
43			t.Errorf("%v.%s has offset %d, but %v.%s has offset %d", h, f.Name, f.Offset, rh, rf.Name, rf.Offset)
44		}
45	}
46
47	if h.NumField() != rh.NumField() {
48		t.Errorf("%v has %d fields, but %v has %d", h, h.NumField(), rh, rh.NumField())
49	}
50	if h.Align() != rh.Align() {
51		t.Errorf("%v has alignment %d, but %v has alignment %d", h, h.Align(), rh, rh.Align())
52	}
53}
54
55var (
56	unsafePointerType = reflect.TypeOf(unsafe.Pointer(nil))
57	uintptrType       = reflect.TypeOf(uintptr(0))
58)
59
60func typeCompatible(t, rt reflect.Type) bool {
61	return t == rt || (t == unsafePointerType && rt == uintptrType)
62}
63
64// TestWriteThroughHeader ensures that the headers in the unsafeheader package
65// can successfully mutate variables of the corresponding built-in types.
66//
67// This test is expected to fail under -race (which implicitly enables
68// -d=checkptr) if the runtime views the header types as incompatible with the
69// underlying built-in types.
70func TestWriteThroughHeader(t *testing.T) {
71	t.Run("Slice", func(t *testing.T) {
72		s := []byte("Hello, checkptr!")[:5]
73
74		var alias []byte
75		hdr := (*unsafeheader.Slice)(unsafe.Pointer(&alias))
76		hdr.Data = unsafe.Pointer(&s[0])
77		hdr.Cap = cap(s)
78		hdr.Len = len(s)
79
80		if !bytes.Equal(alias, s) {
81			t.Errorf("alias of %T(%q) constructed via Slice = %T(%q)", s, s, alias, alias)
82		}
83		if cap(alias) != cap(s) {
84			t.Errorf("alias of %T with cap %d has cap %d", s, cap(s), cap(alias))
85		}
86	})
87
88	t.Run("String", func(t *testing.T) {
89		s := "Hello, checkptr!"
90
91		var alias string
92		hdr := (*unsafeheader.String)(unsafe.Pointer(&alias))
93		hdr.Data = (*unsafeheader.String)(unsafe.Pointer(&s)).Data
94		hdr.Len = len(s)
95
96		if alias != s {
97			t.Errorf("alias of %q constructed via String = %q", s, alias)
98		}
99	})
100}
101