blob: 806cd5ef834433223654091ca1aa8ad2ee6df40c [file] [log] [blame]
// Copyright (C) 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package analysis
import (
"fmt"
"sort"
"strings"
"android.googlesource.com/platform/tools/gpu/api/semantic"
)
// Value interface compliance check.
var _ = Value(&MapValue{})
// MapValue is an implementation of Value that represents a map variable.
// Map entries are stored in the KeyToValue and ValueToKey fields. These two
// maps are always kept in sync.
//
// MapValue attempts to reduce the number of entries stored to a minimum to
// maintain decent performance while also trying to keep enough information to
// provide useful information (avoid tending towards unbounded keys and values).
//
// All insertions and merges use the following method when merging the key value
// pair (k, v) into the map:
// • If m contains an equivalent key to k, then change the existing value to be
// a union of the existing value and v.
// • If m contains an equivalent value to v, then change the existing key to be
// a union of the existing key and k.
// • If m does not contain an equivalent key to k or an equivalent value to v
// then add a new entry to m.
type MapValue struct {
// The map type.
Map *semantic.Map
// A map of keys to values. The inverse of ValueToKey.
KeyToValue map[Value]Value
// A map of values to keys. The inverse of KeyToValue.
ValueToKey map[Value]Value
}
func (v *MapValue) String() string {
parts := make([]string, 0, len(v.KeyToValue))
for k, v := range v.KeyToValue {
parts = append(parts, fmt.Sprintf("<%v: %v>", k, v))
}
sort.Strings(parts)
return fmt.Sprintf("{ %v }", strings.Join(parts, ", "))
}
// Print returns a textual representation of the value.
func (v *MapValue) Print(results *Results) string {
parts := make([]string, 0, len(v.KeyToValue))
for k, v := range v.KeyToValue {
parts = append(parts, fmt.Sprintf("<%v: %v>", k.Print(results), v.Print(results)))
}
sort.Strings(parts)
return fmt.Sprintf("{ %v }", strings.Join(parts, ", "))
}
// Type returns the semantic map type of the value.
func (v *MapValue) Type() semantic.Type {
return v.Map
}
// Equivalent returns false as maps do not support equivalency tests.
func (v *MapValue) Equivalent(o Value) bool {
return false // Maps don't do equivalency.
}
// Equals returns False as maps do not support equality tests.
func (v *MapValue) Equals(o Value) Possibility {
return False // Maps don't do equality.
}
// Valid returns true.
func (v *MapValue) Valid() bool {
return true
}
// Union (∪) returns a map value with all the keys of v and o mapped to their
// respective values.
// o must be of type *MapValue.
func (v *MapValue) Union(o Value) Value {
if v == o {
return v
}
out := v.Clone().(*MapValue)
for key, val := range o.(*MapValue).KeyToValue {
out.mergeInline(key, val)
}
return out
}
// Intersect is not supported by MapValue and will panic if called.
func (v *MapValue) Intersect(o Value) Value {
panic("Intersect not implemented for maps")
}
// Difference is not supported by MapValue and will panic if called.
func (v *MapValue) Difference(o Value) Value {
panic("Difference not implemented for maps")
}
// Clone returns a copy of v with a unique pointer.
func (v *MapValue) Clone() Value {
out := &MapValue{
Map: v.Map,
KeyToValue: make(map[Value]Value, len(v.KeyToValue)),
ValueToKey: make(map[Value]Value, len(v.KeyToValue)),
}
for k, v := range v.KeyToValue {
out.KeyToValue[k] = v
out.ValueToKey[v] = k
}
return out
}
// ContainsKey returns the possibility of the key existing in the map.
func (v *MapValue) ContainsKey(key Value) Possibility {
result := False
for k := range v.KeyToValue {
switch k.Equals(key) {
case Maybe:
result = Maybe
case True:
return True
}
}
return result
}
// Put returns a copy of v with the new mapping of key to val.
func (v *MapValue) Put(key, val Value) *MapValue {
out := v.Clone().(*MapValue)
if k, v := out.findEqualByKey(key); k != nil {
delete(out.KeyToValue, k)
delete(out.ValueToKey, v)
}
out.mergeInline(key, val)
return out
}
// Get return the union of all values that might have a key equal to key.
func (v *MapValue) Get(s *scope, key Value) Value {
candidates := []Value{}
for k, v := range v.KeyToValue {
if k.Equals(key).MaybeTrue() {
candidates = append(candidates, v)
}
}
if len(candidates) == 0 {
// TODO: Track potential invalid map index.
return s.unknownOf(v.Map.ValueType)
}
return unionOf(candidates...)
}
// findEqualByKey looks for an existing key in v that is equal to keyin,
// returning the existing key and value if found. If no key equal to keyin
// is found then (nil, nil) is returned.
func (v *MapValue) findEqualByKey(keyin Value) (key, val Value) {
if val, ok := v.KeyToValue[keyin]; ok {
return keyin, val
}
for k, v := range v.KeyToValue {
if k.Equals(keyin) == True {
return k, v
}
}
return nil, nil
}
// findEquivalentByKey looks for an existing key in v that is equivalent to
// keyin, returning the existing key and value if found. If no key equivalent to
// keyin is found then (nil, nil) is returned.
func (v *MapValue) findEquivalentByKey(keyin Value) (key, val Value) {
if val, ok := v.KeyToValue[keyin]; ok {
return keyin, val
}
for k, v := range v.KeyToValue {
if k.Equivalent(keyin) {
return k, v
}
}
return nil, nil
}
// findEquivalentByVal looks for an existing value in v that is equivalent to
// valin, returning the existing key and value if found. If no value equivalent
// to valin is found then (nil, nil) is returned.
func (v *MapValue) findEquivalentByVal(valin Value) (key, val Value) {
if key, ok := v.ValueToKey[valin]; ok {
return key, valin
}
for v, k := range v.ValueToKey {
if v.Equivalent(valin) {
return k, v
}
}
return nil, nil
}
// mergeInline merges the key-val pair into the map v using the method described
// in the MapValue documentation.
func (v *MapValue) mergeInline(key, val Value) {
if !key.Valid() {
panic("Attempting to put invalid key into map")
}
if existingKey, existingVal := v.findEquivalentByKey(key); existingVal != nil {
delete(v.KeyToValue, existingKey)
delete(v.ValueToKey, existingVal)
val = val.Union(existingVal)
}
if existingKey, existingVal := v.findEquivalentByVal(val); existingKey != nil {
delete(v.KeyToValue, existingKey)
delete(v.ValueToKey, existingVal)
key = key.Union(existingKey)
}
v.KeyToValue[key] = val
v.ValueToKey[val] = key
}