blob: 092b5f1d6d17ad54fdd75ebbb0b4b3b637e23550 [file] [log] [blame]
// Copyright (C) 2015 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 builder
import (
"fmt"
"reflect"
"android.googlesource.com/platform/tools/gpu/atom"
"android.googlesource.com/platform/tools/gpu/database"
"android.googlesource.com/platform/tools/gpu/log"
"android.googlesource.com/platform/tools/gpu/service"
"android.googlesource.com/platform/tools/gpu/service/path"
)
// BuildLazy creates a copy of the capture referenced by the request's path, but
// with the object, value or memory at p replaced with v. The path returned is
// identical to p, but with the base changed to refer to the new capture.
func (request *Set) BuildLazy(c interface{}, d database.Database, l log.Logger) (interface{}, error) {
paths := path.Flatten(request.Path)
v, err := resolveChain(paths, d, l)
if err != nil {
return nil, err
}
// Start by replacing the target value
v[len(v)-1] = request.Value
// Propagate changes back down to the root
for i := len(v) - 1; i >= 0; i-- {
switch p := paths[i].(type) {
case *path.Capture:
id, err := database.Store(v[i], d, l)
if err != nil {
return nil, err
}
// Return the path to the new capture
newPath := request.Path.Clone()
for p := newPath; p != nil; p = p.Base() {
if root, ok := p.(*path.Capture); ok {
root.ID = id
return newPath, nil
}
}
panic("Unreachable")
case *path.Atoms:
stream := v[i].(*atom.List)
streamID, err := database.Store(stream, d, l)
if err != nil {
return nil, err
}
capture := *v[i-1].(*service.Capture)
capture.Name = capture.Name + "*"
capture.Atoms = service.AtomsID(streamID)
v[i-1] = &capture
case *path.Report:
return nil, fmt.Errorf("Reports are immutable")
case *path.Atom:
if v[i] == nil {
return nil, fmt.Errorf("Atom cannot be nil")
}
stream := v[i-1].(*atom.List).Clone()
stream.Atoms[p.Index] = v[i].(atom.Atom)
v[i-1] = stream
case *path.State:
return nil, fmt.Errorf("State can not currently be mutated")
case *path.Field:
obj, err := clone(reflect.ValueOf(v[i-1]))
if err != nil {
return nil, err
}
s := obj
for s.Kind() == reflect.Ptr {
s = s.Elem() // Deref
}
if err := assign(s.FieldByName(p.Name), reflect.ValueOf(v[i])); err != nil {
return nil, err
}
v[i-1] = obj.Interface()
case *path.ArrayIndex:
a, err := clone(reflect.ValueOf(v[i-1]))
if err != nil {
return nil, err
}
val, ok := convert(reflect.ValueOf(v[i]), a.Type().Elem())
if !ok {
return nil, fmt.Errorf("Slice or array at %s has element of type %v, got type %v",
paths[i-1].Path(), a.Type().Elem(), val.Type())
}
if err := assign(a.Index(int(p.Index)), reflect.ValueOf(v[i])); err != nil {
return nil, err
}
v[i-1] = a.Interface()
case *path.MapIndex:
m, err := clone(reflect.ValueOf(v[i-1]))
if err != nil {
return nil, err
}
key, ok := convert(reflect.ValueOf(p.Key), m.Type().Key())
if !ok {
return nil, fmt.Errorf("Map at %s has key of type %v, got type %v",
paths[i-1].Path(), m.Type().Key(), key.Type())
}
val, ok := convert(reflect.ValueOf(v[i]), m.Type().Elem())
if !ok {
return nil, fmt.Errorf("Map at %s has value of type %v, got type %v",
paths[i-1].Path(), m.Type().Elem(), val.Type())
}
m.SetMapIndex(key, val)
v[i-1] = m.Interface()
default:
return nil, fmt.Errorf("Unknown path type %T", p)
}
}
panic("Unreachable")
}
func clone(v reflect.Value) (reflect.Value, error) {
var o reflect.Value
switch v.Kind() {
case reflect.Slice:
o = reflect.MakeSlice(v.Type(), v.Len(), v.Len())
case reflect.Map:
o = reflect.MakeMap(v.Type())
default:
o = reflect.New(v.Type()).Elem()
}
return o, shallowCopy(o, v)
}
func shallowCopy(dst, src reflect.Value) error {
switch dst.Kind() {
case reflect.Ptr, reflect.Interface:
if !src.IsNil() {
o := reflect.New(src.Elem().Type())
shallowCopy(o.Elem(), src.Elem())
dst.Set(o)
}
case reflect.Slice, reflect.Array:
reflect.Copy(dst, src)
case reflect.Map:
for _, k := range src.MapKeys() {
val := src.MapIndex(k)
dst.SetMapIndex(k, val)
}
default:
dst.Set(src)
}
return nil
}
func assign(dst, src reflect.Value) error {
if !dst.CanSet() {
return fmt.Errorf("Value is unassignable")
}
dstTy, srcTy := dst.Type(), src.Type()
switch {
case srcTy.AssignableTo(dstTy):
dst.Set(src)
return nil
case srcTy.ConvertibleTo(dstTy):
dst.Set(src.Convert(dstTy))
return nil
default:
return fmt.Errorf("Cannot assign type %T to type %T", srcTy.Name(), dstTy.Name())
}
}