github.com/AlpineAIO/wails/v2@v2.0.0-beta.32.0.20240505041856-1047a8fa5fef/internal/binding/db.go (about)

     1  package binding
     2  
     3  import (
     4  	"encoding/json"
     5  	"sync"
     6  	"unsafe"
     7  )
     8  
     9  // DB is our database of method bindings
    10  type DB struct {
    11  	//  map[packagename] -> map[structname] -> map[methodname]*method
    12  	store map[string]map[string]map[string]*BoundMethod
    13  
    14  	// This uses fully qualified method names as a shortcut for store traversal.
    15  	// It used for performance gains at runtime
    16  	methodMap map[string]*BoundMethod
    17  
    18  	// This uses ids to reference bound methods at runtime
    19  	obfuscatedMethodArray []*ObfuscatedMethod
    20  
    21  	// Lock to ensure sync access to the data
    22  	lock sync.RWMutex
    23  }
    24  
    25  type ObfuscatedMethod struct {
    26  	method     *BoundMethod
    27  	methodName string
    28  }
    29  
    30  func newDB() *DB {
    31  	return &DB{
    32  		store:                 make(map[string]map[string]map[string]*BoundMethod),
    33  		methodMap:             make(map[string]*BoundMethod),
    34  		obfuscatedMethodArray: []*ObfuscatedMethod{},
    35  	}
    36  }
    37  
    38  // GetMethodFromStore returns the method for the given package/struct/method names
    39  // nil is returned if any one of those does not exist
    40  func (d *DB) GetMethodFromStore(packageName string, structName string, methodName string) *BoundMethod {
    41  	// Lock the db whilst processing and unlock on return
    42  	d.lock.RLock()
    43  	defer d.lock.RUnlock()
    44  
    45  	structMap, exists := d.store[packageName]
    46  	if !exists {
    47  		return nil
    48  	}
    49  	methodMap, exists := structMap[structName]
    50  	if !exists {
    51  		return nil
    52  	}
    53  	return methodMap[methodName]
    54  }
    55  
    56  // GetMethod returns the method for the given qualified method name
    57  // qualifiedMethodName is "packagename.structname.methodname"
    58  func (d *DB) GetMethod(qualifiedMethodName string) *BoundMethod {
    59  	// Lock the db whilst processing and unlock on return
    60  	d.lock.RLock()
    61  	defer d.lock.RUnlock()
    62  
    63  	return d.methodMap[qualifiedMethodName]
    64  }
    65  
    66  // GetObfuscatedMethod returns the method for the given ID
    67  func (d *DB) GetObfuscatedMethod(id int) *BoundMethod {
    68  	// Lock the db whilst processing and unlock on return
    69  	d.lock.RLock()
    70  	defer d.lock.RUnlock()
    71  
    72  	if len(d.obfuscatedMethodArray) <= id {
    73  		return nil
    74  	}
    75  
    76  	return d.obfuscatedMethodArray[id].method
    77  }
    78  
    79  // AddMethod adds the given method definition to the db using the given qualified path: packageName.structName.methodName
    80  func (d *DB) AddMethod(packageName string, structName string, methodName string, methodDefinition *BoundMethod) {
    81  	// Lock the db whilst processing and unlock on return
    82  	d.lock.Lock()
    83  	defer d.lock.Unlock()
    84  
    85  	// Get the map associated with the package name
    86  	structMap, exists := d.store[packageName]
    87  	if !exists {
    88  		// Create a new map for this packagename
    89  		d.store[packageName] = make(map[string]map[string]*BoundMethod)
    90  		structMap = d.store[packageName]
    91  	}
    92  
    93  	// Get the map associated with the struct name
    94  	methodMap, exists := structMap[structName]
    95  	if !exists {
    96  		// Create a new map for this packagename
    97  		structMap[structName] = make(map[string]*BoundMethod)
    98  		methodMap = structMap[structName]
    99  	}
   100  
   101  	// Store the method definition
   102  	methodMap[methodName] = methodDefinition
   103  
   104  	// Store in the methodMap
   105  	key := packageName + "." + structName + "." + methodName
   106  	d.methodMap[key] = methodDefinition
   107  	d.obfuscatedMethodArray = append(d.obfuscatedMethodArray, &ObfuscatedMethod{method: methodDefinition, methodName: key})
   108  }
   109  
   110  // ToJSON converts the method map to JSON
   111  func (d *DB) ToJSON() (string, error) {
   112  	// Lock the db whilst processing and unlock on return
   113  	d.lock.RLock()
   114  	defer d.lock.RUnlock()
   115  
   116  	d.UpdateObfuscatedCallMap()
   117  
   118  	bytes, err := json.Marshal(&d.store)
   119  
   120  	// Return zero copy string as this string will be read only
   121  	result := *(*string)(unsafe.Pointer(&bytes))
   122  	return result, err
   123  }
   124  
   125  // UpdateObfuscatedCallMap sets up the secure call mappings
   126  func (d *DB) UpdateObfuscatedCallMap() map[string]int {
   127  	mappings := make(map[string]int)
   128  
   129  	for id, k := range d.obfuscatedMethodArray {
   130  		mappings[k.methodName] = id
   131  	}
   132  
   133  	return mappings
   134  }