github.com/secoba/wails/v2@v2.6.4/internal/binding/db.go (about)

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