@@ -2,12 +2,79 @@ package core
22
33import (
44 "reflect"
5+ "strconv"
56 "strings"
7+ "sync"
68
79 "github.com/ARM-software/bob-build/core/config"
810 "github.com/ARM-software/bob-build/internal/utils"
911)
1012
13+ var featureTypeCache = struct {
14+ sync.RWMutex
15+ coalesced map [string ]reflect.Type
16+ features map [featureStructTypeKey ]reflect.Type
17+ }{
18+ coalesced : make (map [string ]reflect.Type ),
19+ features : make (map [featureStructTypeKey ]reflect.Type ),
20+ }
21+
22+ type featureStructTypeKey struct {
23+ featureList string
24+ propsType reflect.Type
25+ }
26+
27+ func reflectTypeCacheKey (t reflect.Type ) string {
28+ return t .PkgPath () + "\x00 " + t .String ()
29+ }
30+
31+ func reflectTypeListCacheKey (list []reflect.Type ) string {
32+ var builder strings.Builder
33+ for _ , typ := range list {
34+ key := reflectTypeCacheKey (typ )
35+ builder .WriteString (strconv .Itoa (len (key )))
36+ builder .WriteByte (':' )
37+ builder .WriteString (key )
38+ }
39+ return builder .String ()
40+ }
41+
42+ func featureListCacheKey (features []string ) string {
43+ return strings .Join (features , "\x00 " )
44+ }
45+
46+ func featureStructType (properties * config.Properties , propsType reflect.Type ) reflect.Type {
47+ key := featureStructTypeKey {
48+ featureList : featureListCacheKey (properties .FeatureList ),
49+ propsType : propsType ,
50+ }
51+
52+ featureTypeCache .RLock ()
53+ cached , ok := featureTypeCache .features [key ]
54+ featureTypeCache .RUnlock ()
55+ if ok {
56+ return cached
57+ }
58+
59+ featureTypeCache .Lock ()
60+ defer featureTypeCache .Unlock ()
61+ if cached , ok := featureTypeCache .features [key ]; ok {
62+ return cached
63+ }
64+
65+ fields := make ([]reflect.StructField , len (properties .FeatureList ))
66+ for i , featureName := range properties .FeatureList {
67+ fields [i ] = reflect.StructField {
68+ Name : featurePropertyName (featureName ),
69+ Type : reflect .PtrTo (propsType ),
70+ }
71+ }
72+
73+ cached = reflect .StructOf (fields )
74+ featureTypeCache .features [key ] = cached
75+ return cached
76+ }
77+
1178// featurePropertyName returns name of feature. Name needs to start from capital letter because
1279// this is how it works in go exported/unexported properties
1380// e.g. Android, Foo_bar
@@ -62,17 +129,7 @@ func (f *Features) Init(properties *config.Properties, list ...interface{}) {
62129 }
63130
64131 propsType := coalesceTypes (typesOf (list ... )... )
65- fields := make ([]reflect.StructField , len (properties .FeatureList ))
66-
67- for i , featureName := range properties .FeatureList {
68- fields [i ] = reflect.StructField {
69- Name : featurePropertyName (featureName ),
70- Type : reflect .PtrTo (propsType ),
71- }
72- }
73-
74- bpFeatureStruct := reflect .StructOf (fields )
75- instancePtr := reflect .New (bpFeatureStruct )
132+ instancePtr := reflect .New (featureStructType (properties , propsType ))
76133 f .BlueprintEmbed = instancePtr .Interface ()
77134
78135}
@@ -130,6 +187,21 @@ func coalesceTypes(list ...reflect.Type) reflect.Type {
130187 return list [0 ]
131188 }
132189
190+ key := reflectTypeListCacheKey (list )
191+
192+ featureTypeCache .RLock ()
193+ cached , ok := featureTypeCache .coalesced [key ]
194+ featureTypeCache .RUnlock ()
195+ if ok {
196+ return cached
197+ }
198+
199+ featureTypeCache .Lock ()
200+ defer featureTypeCache .Unlock ()
201+ if cached , ok := featureTypeCache .coalesced [key ]; ok {
202+ return cached
203+ }
204+
133205 fieldsKeys := map [string ]bool {}
134206 fields := []reflect.StructField {}
135207
@@ -146,7 +218,9 @@ func coalesceTypes(list ...reflect.Type) reflect.Type {
146218 }
147219 }
148220
149- return reflect .StructOf (fields )
221+ cached = reflect .StructOf (fields )
222+ featureTypeCache .coalesced [key ] = cached
223+ return cached
150224}
151225
152226// AppendProps merges properties from BlueprintEmbed to dst, but only for enabled features
0 commit comments