PageRenderTime 50ms CodeModel.GetById 13ms app.highlight 29ms RepoModel.GetById 1ms app.codeStats 0ms

/IronPython_Main/Languages/IronPython/IronPython/Runtime/Binding/MetaOldClass.cs

#
C# | 375 lines | 299 code | 56 blank | 20 comment | 12 complexity | 90dd4c90429a39fef0383ae4d8bc8c1b MD5 | raw file
  1/* ****************************************************************************
  2 *
  3 * Copyright (c) Microsoft Corporation. 
  4 *
  5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
  6 * copy of the license can be found in the License.html file at the root of this distribution. If 
  7 * you cannot locate the  Apache License, Version 2.0, please send an email to 
  8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
  9 * by the terms of the Apache License, Version 2.0.
 10 *
 11 * You must not remove this notice, or any other, from this software.
 12 *
 13 *
 14 * ***************************************************************************/
 15
 16#if !CLR2
 17using System.Linq.Expressions;
 18#else
 19using Microsoft.Scripting.Ast;
 20#endif
 21
 22using System;
 23using System.Collections.Generic;
 24using System.Dynamic;
 25
 26using Microsoft.Scripting;
 27using Microsoft.Scripting.Actions;
 28using Microsoft.Scripting.Runtime;
 29using Microsoft.Scripting.Utils;
 30
 31using IronPython.Runtime.Operations;
 32using IronPython.Runtime.Types;
 33
 34namespace IronPython.Runtime.Binding {
 35    using Ast = Expression;
 36    using AstUtils = Microsoft.Scripting.Ast.Utils;
 37
 38    class MetaOldClass : MetaPythonObject, IPythonInvokable, IPythonGetable, IPythonOperable, IPythonConvertible {
 39        public MetaOldClass(Expression/*!*/ expression, BindingRestrictions/*!*/ restrictions, OldClass/*!*/ value)
 40            : base(expression, BindingRestrictions.Empty, value) {
 41            Assert.NotNull(value);
 42        }
 43
 44        #region IPythonInvokable Members
 45
 46        public DynamicMetaObject/*!*/ Invoke(PythonInvokeBinder/*!*/ pythonInvoke, Expression/*!*/ codeContext, DynamicMetaObject/*!*/ target, DynamicMetaObject/*!*/[]/*!*/ args) {            
 47            return MakeCallRule(pythonInvoke, codeContext, args);
 48        }
 49
 50        #endregion
 51
 52        #region IPythonGetable Members
 53
 54        public DynamicMetaObject GetMember(PythonGetMemberBinder member, DynamicMetaObject codeContext) {
 55            // no codeContext filtering but avoid an extra site by handling this action directly
 56            return MakeGetMember(member, codeContext);
 57        }
 58
 59        #endregion
 60
 61        #region MetaObject Overrides
 62
 63        public override DynamicMetaObject/*!*/ BindInvokeMember(InvokeMemberBinder/*!*/ action, DynamicMetaObject/*!*/[]/*!*/ args) {
 64            return BindingHelpers.GenericInvokeMember(action, null, this, args);
 65        }
 66
 67        public override DynamicMetaObject/*!*/ BindInvoke(InvokeBinder/*!*/ call, params DynamicMetaObject/*!*/[]/*!*/ args) {
 68            return MakeCallRule(call, AstUtils.Constant(PythonContext.GetPythonContext(call).SharedContext), args);
 69        }
 70
 71        public override DynamicMetaObject/*!*/ BindCreateInstance(CreateInstanceBinder/*!*/ create, params DynamicMetaObject/*!*/[]/*!*/ args) {
 72            return MakeCallRule(create, AstUtils.Constant(PythonContext.GetPythonContext(create).SharedContext), args);
 73        }
 74
 75        public override DynamicMetaObject/*!*/ BindGetMember(GetMemberBinder/*!*/ member) {
 76            return MakeGetMember(member, PythonContext.GetCodeContextMO(member));
 77        }
 78
 79        public override DynamicMetaObject/*!*/ BindSetMember(SetMemberBinder/*!*/ member, DynamicMetaObject/*!*/ value) {
 80            return MakeSetMember(member.Name, value);
 81        }
 82
 83        public override DynamicMetaObject/*!*/ BindDeleteMember(DeleteMemberBinder/*!*/ member) {
 84            return MakeDeleteMember(member);
 85        }
 86
 87        public override DynamicMetaObject BindConvert(ConvertBinder/*!*/ conversion) {
 88            return ConvertWorker(conversion, conversion.Type, conversion.Explicit ? ConversionResultKind.ExplicitCast : ConversionResultKind.ImplicitCast);
 89        }
 90
 91        public DynamicMetaObject BindConvert(PythonConversionBinder binder) {
 92            return ConvertWorker(binder, binder.Type, binder.ResultKind);
 93        }
 94
 95        public DynamicMetaObject ConvertWorker(DynamicMetaObjectBinder binder, Type toType, ConversionResultKind kind) {        
 96            PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "OldClass Convert");
 97            PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, "OldClass Convert");
 98            if (toType.IsSubclassOf(typeof(Delegate))) {
 99                return MakeDelegateTarget(binder, toType, Restrict(typeof(OldClass)));
100            }
101            return FallbackConvert(binder);
102        }
103
104        public override System.Collections.Generic.IEnumerable<string> GetDynamicMemberNames() {
105            foreach (object o in ((IPythonMembersList)Value).GetMemberNames(DefaultContext.Default)) {
106                if (o is string) {
107                    yield return (string)o;
108                }
109            }
110        }
111
112        #endregion
113
114        #region Calls
115
116        private DynamicMetaObject/*!*/ MakeCallRule(DynamicMetaObjectBinder/*!*/ call, Expression/*!*/ codeContext, DynamicMetaObject[] args) {
117            PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "OldClass Invoke w/ " + args.Length + " args");
118            PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, "OldClass Invoke");
119
120            CallSignature signature = BindingHelpers.GetCallSignature(call);
121            // TODO: If we know __init__ wasn't present we could construct the OldInstance directly.
122
123            Expression[] exprArgs = new Expression[args.Length];
124            for (int i = 0; i < args.Length; i++) {
125                exprArgs[i] = args[i].Expression;
126            }
127
128            ParameterExpression init = Ast.Variable(typeof(object), "init");
129            ParameterExpression instTmp = Ast.Variable(typeof(object), "inst");
130            DynamicMetaObject self = Restrict(typeof(OldClass));
131
132            return new DynamicMetaObject(
133                Ast.Block(
134                    new ParameterExpression[] { init, instTmp },
135                    Ast.Assign(
136                        instTmp,
137                        Ast.New(
138                            typeof(OldInstance).GetConstructor(new Type[] { typeof(CodeContext), typeof(OldClass) }),
139                            codeContext,
140                            self.Expression
141                        )
142                    ),
143                    Ast.Condition(
144                        Expression.Not(
145                            Expression.TypeIs(
146                                Expression.Assign(
147                                    init,
148                                    Ast.Call(
149                                        typeof(PythonOps).GetMethod("OldClassTryLookupInit"),
150                                        self.Expression,
151                                        instTmp
152                                    )
153                                ),
154                                typeof(OperationFailed)
155                            )
156                        ),
157                        Ast.Dynamic(
158                            PythonContext.GetPythonContext(call).Invoke(
159                                signature
160                            ),
161                            typeof(object),
162                            ArrayUtils.Insert<Expression>(codeContext, init, exprArgs)
163                        ),
164                        NoInitCheckNoArgs(signature, self, args)
165                    ),
166                    instTmp
167                ),
168                self.Restrictions.Merge(BindingRestrictions.Combine(args))
169            );
170        }
171
172        private static Expression NoInitCheckNoArgs(CallSignature signature, DynamicMetaObject self, DynamicMetaObject[] args) {
173            int unusedCount = args.Length;
174
175            Expression dictExpr = GetArgumentExpression(signature, ArgumentType.Dictionary, ref unusedCount, args);
176            Expression listExpr = GetArgumentExpression(signature, ArgumentType.List, ref unusedCount, args);
177
178            if (signature.IsSimple || unusedCount > 0) {
179                if (args.Length > 0) {
180                    return Ast.Call(
181                        typeof(PythonOps).GetMethod("OldClassMakeCallError"),
182                        self.Expression
183                    );
184                }
185
186                return AstUtils.Constant(null);
187            }
188
189            return Ast.Call(
190                typeof(PythonOps).GetMethod("OldClassCheckCallError"),
191                self.Expression,
192                dictExpr,
193                listExpr
194            );
195        }
196
197        private static Expression GetArgumentExpression(CallSignature signature, ArgumentType kind, ref int unusedCount, DynamicMetaObject/*!*/[]/*!*/ args) {
198            int index = signature.IndexOf(kind);
199            if (index != -1) {
200                unusedCount--;
201                return args[index].Expression;
202            }
203
204            return AstUtils.Constant(null);
205        }
206
207        public static object MakeCallError() {
208            // Normally, if we have an __init__ method, the method binder detects signature mismatches.
209            // This can happen when a class does not define __init__ and therefore does not take any arguments.
210            // Beware that calls like F(*(), **{}) have 2 arguments but they're empty and so it should still
211            // match against def F(). 
212            throw PythonOps.TypeError("this constructor takes no arguments");
213        }
214
215        #endregion
216
217        #region Member Access
218
219        private DynamicMetaObject/*!*/ MakeSetMember(string/*!*/ name, DynamicMetaObject/*!*/ value) {
220            PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "OldClass SetMember");
221            PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, "OldClass SetMember");
222            DynamicMetaObject self = Restrict(typeof(OldClass));
223
224            Expression call, valueExpr = AstUtils.Convert(value.Expression, typeof(object));
225            switch (name) {
226                case "__bases__":
227                    call = Ast.Call(
228                        typeof(PythonOps).GetMethod("OldClassSetBases"),
229                        self.Expression,
230                        valueExpr
231                    );
232                    break;
233                case "__name__":
234                    call = Ast.Call(
235                        typeof(PythonOps).GetMethod("OldClassSetName"),
236                        self.Expression,
237                        valueExpr
238                    );
239                    break;
240                case "__dict__":
241                    call = Ast.Call(
242                        typeof(PythonOps).GetMethod("OldClassSetDictionary"),
243                        self.Expression,
244                        valueExpr
245                    );
246                    break;
247                default:
248                    call = Ast.Call(
249                        typeof(PythonOps).GetMethod("OldClassSetNameHelper"),
250                        self.Expression,
251                        AstUtils.Constant(name),
252                        valueExpr
253                    );
254                    break;
255            }
256
257            return new DynamicMetaObject(
258                call,
259                self.Restrictions.Merge(value.Restrictions)
260            );
261        }
262
263        private DynamicMetaObject/*!*/ MakeDeleteMember(DeleteMemberBinder/*!*/ member) {
264            PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "OldClass DeleteMember");
265            PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, "OldClass DeleteMember");
266            DynamicMetaObject self = Restrict(typeof(OldClass));
267
268            return new DynamicMetaObject(
269                Ast.Call(
270                    typeof(PythonOps).GetMethod("OldClassDeleteMember"),
271                    AstUtils.Constant(PythonContext.GetPythonContext(member).SharedContext),
272                    self.Expression,
273                    AstUtils.Constant(member.Name)
274                ),
275                self.Restrictions
276            );
277        }
278
279        private DynamicMetaObject/*!*/ MakeGetMember(DynamicMetaObjectBinder/*!*/ member, DynamicMetaObject codeContext) {
280            PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "OldClass GetMember");
281            PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, "OldClass GetMember");
282            DynamicMetaObject self = Restrict(typeof(OldClass));
283
284            Expression target;
285            string memberName = GetGetMemberName(member);
286            switch (memberName) {
287                case "__dict__":
288                    target = Ast.Block(
289                        Ast.Call(
290                            typeof(PythonOps).GetMethod("OldClassDictionaryIsPublic"),
291                            self.Expression
292                        ),
293                        Ast.Call(
294                            typeof(PythonOps).GetMethod("OldClassGetDictionary"),
295                            self.Expression
296                        )
297                    );
298                    break;
299                case "__bases__":
300                    target = Ast.Call(
301                        typeof(PythonOps).GetMethod("OldClassGetBaseClasses"),
302                        self.Expression
303                    );
304                    break;
305                case "__name__":
306                    target = Ast.Call(
307                        typeof(PythonOps).GetMethod("OldClassGetName"),
308                        self.Expression
309                    );
310                    break;
311                default:
312                    ParameterExpression tmp = Ast.Variable(typeof(object), "lookupVal");
313                    return new DynamicMetaObject(
314                        Ast.Block(
315                            new ParameterExpression[] { tmp },
316                            Ast.Condition(
317                                Expression.Not(
318                                    Expression.TypeIs(
319                                        Expression.Assign(
320                                            tmp,
321                                            Ast.Call(
322                                                typeof(PythonOps).GetMethod("OldClassTryLookupValue"),
323                                                AstUtils.Constant(PythonContext.GetPythonContext(member).SharedContext),
324                                                self.Expression,
325                                                AstUtils.Constant(memberName)
326                                            )
327                                        ),
328                                        typeof(OperationFailed)
329                                    )
330                                ),
331                                tmp,
332                                AstUtils.Convert(
333                                    GetMemberFallback(this, member, codeContext).Expression,
334                                    typeof(object)
335                                )
336                            )
337                        ),
338                        self.Restrictions
339                    );
340            }
341
342            return new DynamicMetaObject(
343                target,
344                self.Restrictions
345            );
346        }       
347
348        #endregion
349
350        #region Helpers
351
352        public new OldClass/*!*/ Value {
353            get {
354                return (OldClass)base.Value;
355            }
356        }
357
358        #endregion        
359    
360        #region IPythonOperable Members
361
362        DynamicMetaObject IPythonOperable.BindOperation(PythonOperationBinder action, DynamicMetaObject[] args) {
363            if (action.Operation == PythonOperationKind.IsCallable) {
364                return new DynamicMetaObject(
365                    AstUtils.Constant(true),
366                    Restrictions.Merge(BindingRestrictions.GetTypeRestriction(Expression, typeof(OldClass)))
367                );
368            }
369
370            return null;
371        }
372
373        #endregion
374    }
375}