summaryrefslogtreecommitdiff
path: root/csharp/src/Ice/Patcher.cs
blob: 7f87024f707354d824b29c0965a2bfbbc30e6d05 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//
// Copyright (c) ZeroC, Inc. All rights reserved.
//

using System;
using System.Collections.Generic;
using System.Reflection;

namespace IceInternal
{
    public sealed class Patcher
    {
        static public System.Action<T> arrayReadValue<T>(T[] arr, int index) where T : Ice.Value
        {
            return (T v) => { arr[index] = v; };
        }

        static public System.Action<T> listReadValue<T>(List<T> seq, int index) where T : Ice.Value
        {
            return (T v) => {
                int count = seq.Count;
                if(index >= count) // Need to grow the sequence.
                {
                    for(int i = count; i < index; i++)
                    {
                        seq.Add(default(T));
                    }
                    seq.Add(v);
                }
                else
                {
                    seq[index] = v;
                }
            };
        }

        static public System.Action<T> customSeqReadValue<T>(IEnumerable<T> seq, int index) where T : Ice.Value
        {
            return (T v) => {
                var info = getInvokeInfo<T>(seq.GetType());
                int count = info.getCount(seq);
                if(index >= count) // Need to grow the sequence.
                {
                    for(int i = count; i < index; i++)
                    {
                        info.invokeAdd(seq, default(T));
                    }
                    info.invokeAdd(seq, v);
                }
                else
                {
                    info.invokeSet(seq, index, v);
                }
            };
        }

        private static InvokeInfo getInvokeInfo<T>(Type t)
        {
            lock(_methodTable)
            {
                InvokeInfo i;
                if(_methodTable.TryGetValue(t, out i))
                {
                    return i;
                }

                MethodInfo am = t.GetMethod("Add", new Type[] { typeof(T) });
                if(am == null)
                {
                    throw new Ice.MarshalException("Cannot patch a collection without an Add() method");
                }

                PropertyInfo pi = t.GetProperty("Item");
                if(pi == null)
                {
                    throw new Ice.MarshalException("Cannot patch a collection without an indexer");
                }
                MethodInfo sm = pi.GetSetMethod();
                if(sm == null)
                {
                    throw new Ice.MarshalException("Cannot patch a collection without an indexer to set a value");
                }

                pi = t.GetProperty("Count");
                if(pi == null)
                {
                    throw new Ice.MarshalException("Cannot patch a collection without a Count property");
                }
                MethodInfo cm = pi.GetGetMethod();
                if(cm == null)
                {
                    throw new Ice.MarshalException("Cannot patch a collection without a readable Count property");
                }

                i = new InvokeInfo(am, sm, cm);
                _methodTable.Add(t, i);
                return i;
            }
        }

        private class InvokeInfo
        {
            public InvokeInfo(MethodInfo am, MethodInfo sm, MethodInfo cm)
            {
                _addMethod = am;
                _setMethod = sm;
                _countMethod = cm;
            }

            internal int getCount(System.Collections.IEnumerable seq)
            {
                try
                {
                    return (int)_countMethod.Invoke(seq, null);
                }
                catch(Exception ex)
                {
                    throw  new Ice.MarshalException("Could not read Count property during patching", ex);
                }
            }

            internal void invokeAdd(System.Collections.IEnumerable seq, object v)
            {
                try
                {
                    object[] arg = new object[] { v };
                    _addMethod.Invoke(seq, arg);
                }
                catch(Exception ex)
                {
                    throw  new Ice.MarshalException("Could not invoke Add method during patching", ex);
                }
            }

            internal void invokeSet(System.Collections.IEnumerable seq, int index, object v)
            {
                try
                {
                    object[] args = new object[] { index, v };
                    _setMethod.Invoke(seq, args);
                }
                catch(Exception ex)
                {
                    throw  new Ice.MarshalException("Could not call indexer during patching", ex);
                }
            }

            private MethodInfo _addMethod;
            private MethodInfo _setMethod;
            private MethodInfo _countMethod;
        }

        private static Dictionary<Type, InvokeInfo> _methodTable = new Dictionary<Type, InvokeInfo>();
    }
}