// Copyright (c) 2014 Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.

// Package areflect provides utilities to help with reflection.
package areflect

import (
	"reflect"
	"unsafe"
)

// ForceExport returns a new reflect.Value that is identical to the one passed
// in argument except that it's considered as an exported symbol even if in
// reality it isn't.
//
// The `reflect' package intentionally makes it impossible to access the value
// of an unexported attribute.  The implementation of reflect.DeepEqual() cheats
// as it bypasses this check.  Unfortunately, we can't use the same cheat, which
// prevents us from re-implementing DeepEqual properly or implementing some other
// reflection-based tools.  So this is our cheat on top of theirs.  It makes
// the given reflect.Value appear as if it was exported.
//
// This function requires go1.6 or newer.
func ForceExport(v reflect.Value) reflect.Value {
	// constants from reflect/value.go
	const flagStickyRO uintptr = 1 << 5
	const flagEmbedRO uintptr = 1 << 6 // new in go1.6 (was flagIndir before)
	const flagRO uintptr = flagStickyRO | flagEmbedRO
	ptr := unsafe.Pointer(&v)
	rv := (*struct {
		typ  unsafe.Pointer // a *reflect.rtype (reflect.Type)
		ptr  unsafe.Pointer // The value wrapped by this reflect.Value
		flag uintptr
	})(ptr)
	rv.flag &= ^flagRO // Unset the flag so this value appears to be exported.
	return v
}
