| // 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()) |
| } |
| } |