1// Copyright 2020 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package android 16 17import ( 18 "fmt" 19 "reflect" 20) 21 22// depSet is designed to be conceptually compatible with Bazel's depsets: 23// https://docs.bazel.build/versions/master/skylark/depsets.html 24 25type DepSetOrder int 26 27const ( 28 PREORDER DepSetOrder = iota 29 POSTORDER 30 TOPOLOGICAL 31) 32 33func (o DepSetOrder) String() string { 34 switch o { 35 case PREORDER: 36 return "PREORDER" 37 case POSTORDER: 38 return "POSTORDER" 39 case TOPOLOGICAL: 40 return "TOPOLOGICAL" 41 default: 42 panic(fmt.Errorf("Invalid DepSetOrder %d", o)) 43 } 44} 45 46// A depSet efficiently stores a slice of an arbitrary type from transitive dependencies without 47// copying. It is stored as a DAG of depSet nodes, each of which has some direct contents and a list 48// of dependency depSet nodes. 49// 50// A depSet has an order that will be used to walk the DAG when ToList() is called. The order 51// can be POSTORDER, PREORDER, or TOPOLOGICAL. POSTORDER and PREORDER orders return a postordered 52// or preordered left to right flattened list. TOPOLOGICAL returns a list that guarantees that 53// elements of children are listed after all of their parents (unless there are duplicate direct 54// elements in the depSet or any of its transitive dependencies, in which case the ordering of the 55// duplicated element is not guaranteed). 56// 57// A depSet is created by newDepSet or newDepSetBuilder.Build from the slice for direct contents 58// and the *depSets of dependencies. A depSet is immutable once created. 59// 60// This object uses reflection to remain agnostic to the type it contains. It should be replaced 61// with generics once those exist in Go. Callers should generally use a thin wrapper around depSet 62// that provides type-safe methods like DepSet for Paths. 63type depSet struct { 64 preorder bool 65 reverse bool 66 order DepSetOrder 67 direct interface{} 68 transitive []*depSet 69} 70 71type depSetInterface interface { 72 embeddedDepSet() *depSet 73} 74 75func (d *depSet) embeddedDepSet() *depSet { 76 return d 77} 78 79var _ depSetInterface = (*depSet)(nil) 80 81// newDepSet returns an immutable depSet with the given order, direct and transitive contents. 82// direct must be a slice, but is not type-safe due to the lack of generics in Go. It can be a 83// nil slice, but not a nil interface{}, i.e. []string(nil) but not nil. 84func newDepSet(order DepSetOrder, direct interface{}, transitive interface{}) *depSet { 85 var directCopy interface{} 86 transitiveDepSet := sliceToDepSets(transitive, order) 87 88 if order == TOPOLOGICAL { 89 directCopy = reverseSlice(direct) 90 reverseSliceInPlace(transitiveDepSet) 91 } else { 92 directCopy = copySlice(direct) 93 } 94 95 return &depSet{ 96 preorder: order == PREORDER, 97 reverse: order == TOPOLOGICAL, 98 order: order, 99 direct: directCopy, 100 transitive: transitiveDepSet, 101 } 102} 103 104// depSetBuilder is used to create an immutable depSet. 105type depSetBuilder struct { 106 order DepSetOrder 107 direct reflect.Value 108 transitive []*depSet 109} 110 111// newDepSetBuilder returns a depSetBuilder to create an immutable depSet with the given order and 112// type, represented by a slice of type that will be in the depSet. 113func newDepSetBuilder(order DepSetOrder, typ interface{}) *depSetBuilder { 114 empty := reflect.Zero(reflect.TypeOf(typ)) 115 return &depSetBuilder{ 116 order: order, 117 direct: empty, 118 } 119} 120 121// sliceToDepSets converts a slice of any type that implements depSetInterface (by having a depSet 122// embedded in it) into a []*depSet. 123func sliceToDepSets(in interface{}, order DepSetOrder) []*depSet { 124 slice := reflect.ValueOf(in) 125 length := slice.Len() 126 out := make([]*depSet, length) 127 for i := 0; i < length; i++ { 128 vi := slice.Index(i) 129 depSetIntf, ok := vi.Interface().(depSetInterface) 130 if !ok { 131 panic(fmt.Errorf("element %d is a %s, not a depSetInterface", i, vi.Type())) 132 } 133 depSet := depSetIntf.embeddedDepSet() 134 if depSet.order != order { 135 panic(fmt.Errorf("incompatible order, new depSet is %s but transitive depSet is %s", 136 order, depSet.order)) 137 } 138 out[i] = depSet 139 } 140 return out 141} 142 143// DirectSlice adds direct contents to the depSet being built by a depSetBuilder. Newly added direct 144// contents are to the right of any existing direct contents. The argument must be a slice, but 145// is not type-safe due to the lack of generics in Go. 146func (b *depSetBuilder) DirectSlice(direct interface{}) *depSetBuilder { 147 b.direct = reflect.AppendSlice(b.direct, reflect.ValueOf(direct)) 148 return b 149} 150 151// Direct adds direct contents to the depSet being built by a depSetBuilder. Newly added direct 152// contents are to the right of any existing direct contents. The argument must be the same type 153// as the element of the slice passed to newDepSetBuilder, but is not type-safe due to the lack of 154// generics in Go. 155func (b *depSetBuilder) Direct(direct interface{}) *depSetBuilder { 156 b.direct = reflect.Append(b.direct, reflect.ValueOf(direct)) 157 return b 158} 159 160// Transitive adds transitive contents to the DepSet being built by a DepSetBuilder. Newly added 161// transitive contents are to the right of any existing transitive contents. The argument can 162// be any slice of type that has depSet embedded in it. 163func (b *depSetBuilder) Transitive(transitive interface{}) *depSetBuilder { 164 depSets := sliceToDepSets(transitive, b.order) 165 b.transitive = append(b.transitive, depSets...) 166 return b 167} 168 169// Returns the depSet being built by this depSetBuilder. The depSetBuilder retains its contents 170// for creating more depSets. 171func (b *depSetBuilder) Build() *depSet { 172 return newDepSet(b.order, b.direct.Interface(), b.transitive) 173} 174 175// walk calls the visit method in depth-first order on a DepSet, preordered if d.preorder is set, 176// otherwise postordered. 177func (d *depSet) walk(visit func(interface{})) { 178 visited := make(map[*depSet]bool) 179 180 var dfs func(d *depSet) 181 dfs = func(d *depSet) { 182 visited[d] = true 183 if d.preorder { 184 visit(d.direct) 185 } 186 for _, dep := range d.transitive { 187 if !visited[dep] { 188 dfs(dep) 189 } 190 } 191 192 if !d.preorder { 193 visit(d.direct) 194 } 195 } 196 197 dfs(d) 198} 199 200// ToList returns the depSet flattened to a list. The order in the list is based on the order 201// of the depSet. POSTORDER and PREORDER orders return a postordered or preordered left to right 202// flattened list. TOPOLOGICAL returns a list that guarantees that elements of children are listed 203// after all of their parents (unless there are duplicate direct elements in the DepSet or any of 204// its transitive dependencies, in which case the ordering of the duplicated element is not 205// guaranteed). 206// 207// This method uses a reflection-based implementation to find the unique elements in slice, which 208// is around 3x slower than a concrete implementation. Type-safe wrappers around depSet can 209// provide their own implementation of ToList that calls depSet.toList with a method that 210// uses a concrete implementation. 211func (d *depSet) ToList() interface{} { 212 return d.toList(firstUnique) 213} 214 215// toList returns the depSet flattened to a list. The order in the list is based on the order 216// of the depSet. POSTORDER and PREORDER orders return a postordered or preordered left to right 217// flattened list. TOPOLOGICAL returns a list that guarantees that elements of children are listed 218// after all of their parents (unless there are duplicate direct elements in the DepSet or any of 219// its transitive dependencies, in which case the ordering of the duplicated element is not 220// guaranteed). The firstUniqueFunc is used to remove duplicates from the list. 221func (d *depSet) toList(firstUniqueFunc func(interface{}) interface{}) interface{} { 222 if d == nil { 223 return nil 224 } 225 slice := reflect.Zero(reflect.TypeOf(d.direct)) 226 d.walk(func(paths interface{}) { 227 slice = reflect.AppendSlice(slice, reflect.ValueOf(paths)) 228 }) 229 list := slice.Interface() 230 list = firstUniqueFunc(list) 231 if d.reverse { 232 reverseSliceInPlace(list) 233 } 234 return list 235} 236 237// firstUnique returns all unique elements of a slice, keeping the first copy of each. It 238// modifies the slice contents in place, and returns a subslice of the original slice. The 239// argument must be a slice, but is not type-safe due to the lack of reflection in Go. 240// 241// Performance of the reflection-based firstUnique is up to 3x slower than a concrete type 242// version such as FirstUniqueStrings. 243func firstUnique(slice interface{}) interface{} { 244 // 4 was chosen based on Benchmark_firstUnique results. 245 if reflect.ValueOf(slice).Len() > 4 { 246 return firstUniqueMap(slice) 247 } 248 return firstUniqueList(slice) 249} 250 251// firstUniqueList is an implementation of firstUnique using an O(N^2) list comparison to look for 252// duplicates. 253func firstUniqueList(in interface{}) interface{} { 254 writeIndex := 0 255 slice := reflect.ValueOf(in) 256 length := slice.Len() 257outer: 258 for readIndex := 0; readIndex < length; readIndex++ { 259 readValue := slice.Index(readIndex) 260 for compareIndex := 0; compareIndex < writeIndex; compareIndex++ { 261 compareValue := slice.Index(compareIndex) 262 // These two Interface() calls seem to cause an allocation and significantly 263 // slow down this list-based implementation. The map implementation below doesn't 264 // have this issue because reflect.Value.MapIndex takes a Value and appears to be 265 // able to do the map lookup without an allocation. 266 if readValue.Interface() == compareValue.Interface() { 267 // The value at readIndex already exists somewhere in the output region 268 // of the slice before writeIndex, skip it. 269 continue outer 270 } 271 } 272 if readIndex != writeIndex { 273 writeValue := slice.Index(writeIndex) 274 writeValue.Set(readValue) 275 } 276 writeIndex++ 277 } 278 return slice.Slice(0, writeIndex).Interface() 279} 280 281var trueValue = reflect.ValueOf(true) 282 283// firstUniqueList is an implementation of firstUnique using an O(N) hash set lookup to look for 284// duplicates. 285func firstUniqueMap(in interface{}) interface{} { 286 writeIndex := 0 287 slice := reflect.ValueOf(in) 288 length := slice.Len() 289 seen := reflect.MakeMapWithSize(reflect.MapOf(slice.Type().Elem(), trueValue.Type()), slice.Len()) 290 for readIndex := 0; readIndex < length; readIndex++ { 291 readValue := slice.Index(readIndex) 292 if seen.MapIndex(readValue).IsValid() { 293 continue 294 } 295 seen.SetMapIndex(readValue, trueValue) 296 if readIndex != writeIndex { 297 writeValue := slice.Index(writeIndex) 298 writeValue.Set(readValue) 299 } 300 writeIndex++ 301 } 302 return slice.Slice(0, writeIndex).Interface() 303} 304 305// reverseSliceInPlace reverses the elements of a slice in place. The argument must be a slice, but 306// is not type-safe due to the lack of reflection in Go. 307func reverseSliceInPlace(in interface{}) { 308 swapper := reflect.Swapper(in) 309 slice := reflect.ValueOf(in) 310 length := slice.Len() 311 for i, j := 0, length-1; i < j; i, j = i+1, j-1 { 312 swapper(i, j) 313 } 314} 315 316// reverseSlice returns a copy of a slice in reverse order. The argument must be a slice, but is 317// not type-safe due to the lack of reflection in Go. 318func reverseSlice(in interface{}) interface{} { 319 slice := reflect.ValueOf(in) 320 if !slice.IsValid() || slice.IsNil() { 321 return in 322 } 323 if slice.Kind() != reflect.Slice { 324 panic(fmt.Errorf("%t is not a slice", in)) 325 } 326 length := slice.Len() 327 if length == 0 { 328 return in 329 } 330 out := reflect.MakeSlice(slice.Type(), length, length) 331 for i := 0; i < length; i++ { 332 out.Index(i).Set(slice.Index(length - 1 - i)) 333 } 334 return out.Interface() 335} 336 337// copySlice returns a copy of a slice. The argument must be a slice, but is not type-safe due to 338// the lack of reflection in Go. 339func copySlice(in interface{}) interface{} { 340 slice := reflect.ValueOf(in) 341 if !slice.IsValid() || slice.IsNil() { 342 return in 343 } 344 length := slice.Len() 345 if length == 0 { 346 return in 347 } 348 out := reflect.MakeSlice(slice.Type(), length, length) 349 reflect.Copy(out, slice) 350 return out.Interface() 351} 352