PageRenderTime 47ms CodeModel.GetById 12ms app.highlight 30ms RepoModel.GetById 1ms app.codeStats 0ms

/IronPython_Main/Languages/Ruby/Ruby/Runtime/Calls/CallArguments.cs

#
C# | 353 lines | 252 code | 64 blank | 37 comment | 36 complexity | 63d5fdcc4a3801ae9864390a80a31374 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 * ironruby@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.Collections;
 23using System.Diagnostics;
 24using System.Dynamic;
 25using System.Runtime.CompilerServices;
 26using Microsoft.Scripting;
 27using Microsoft.Scripting.Runtime;
 28using Microsoft.Scripting.Utils;
 29using IronRuby.Builtins;
 30using IronRuby.Compiler;
 31using AstUtils = Microsoft.Scripting.Ast.Utils;
 32
 33namespace IronRuby.Runtime.Calls {
 34    using Ast = Expression;
 35    using AstExpressions = ReadOnlyCollectionBuilder<Expression>;
 36    using System.Collections.ObjectModel;
 37
 38    /// <summary>
 39    /// Wraps the arguments of a dynamic call site
 40    /// Includes the actual arguments, the expressions that produced those arguments,
 41    /// and the call signature.
 42    /// 
 43    /// These three things are grouped together to ensure that they are all in sync
 44    /// when we want to shift the arguments around during the method binding process.
 45    /// </summary>
 46    public sealed class CallArguments {
 47        private readonly bool _hasScopeOrContextArg;
 48        private readonly DynamicMetaObject/*!*/ _context;
 49        private DynamicMetaObject _scope;
 50
 51        // _args[0] might be target, if so _target is null:
 52        private DynamicMetaObject _target;
 53        private RubyClass _targetClass;
 54
 55        // Arguments must be readonly if _copyOnWrite is true. 
 56        private DynamicMetaObject[]/*!*/ _args;
 57        private bool _copyArgsOnWrite;
 58        
 59        private RubyCallSignature _signature;
 60
 61        public RubyCallSignature/*!*/ Signature {
 62            get { return _signature; }
 63        }
 64
 65        public int CallSiteArgumentCount {
 66            get {
 67                // (scope|context)?, target, arguments:
 68                return (_hasScopeOrContextArg ? 1 : 0) + ExplicitArgumentCount; 
 69            }
 70        }
 71
 72        public int ExplicitArgumentCount {
 73            get {
 74                // target, arguments:
 75                return (_target != null ? 1 : 0) + _args.Length;
 76            }
 77        }
 78
 79        // Index of the first argument in _args array.
 80        private int FirstArgumentIndex {
 81            get { return (_target == null) ? 1 : 0; }
 82        }
 83
 84        public int SimpleArgumentCount {
 85            get {
 86                return _args.Length - FirstArgumentIndex - (_signature.HasBlock ? 1 : 0) - 
 87                    (_signature.HasSplattedArgument ? 1 : 0) - (_signature.HasRhsArgument ? 1 : 0); 
 88            }
 89        }
 90
 91        public DynamicMetaObject/*!*/ MetaScope {
 92            get {
 93                if (_scope == null) {
 94                    Debug.Assert(!_signature.HasScope);
 95                    // we can burn the scope as a constant since we'll restrict the context arg to the current context:
 96                    var emptyScope = ((RubyContext)_context.Value).EmptyScope;
 97                    _scope = new DynamicMetaObject(Ast.Constant(emptyScope, typeof(RubyScope)), BindingRestrictions.Empty, emptyScope);
 98                }
 99                return _scope;
100            }
101        }
102
103        public DynamicMetaObject/*!*/ MetaContext {
104            get { return _context; }
105        }
106
107        public RubyScope/*!*/ Scope {
108            get { return (RubyScope)MetaScope.Value; }
109        }
110
111        public RubyContext/*!*/ RubyContext {
112            get { return (RubyContext)MetaContext.Value; }
113        }
114
115        public DynamicMetaObject/*!*/ MetaTarget {
116            get { return _target ?? _args[0]; }            
117        }
118
119        public Expression/*!*/ TargetExpression {
120            get { return MetaTarget.Expression; }
121        }
122
123        public RubyClass/*!*/ TargetClass {
124            get {
125                if (_targetClass == null) {
126                    _targetClass = RubyContext.GetImmediateClassOf(Target);
127                }
128                return _targetClass;
129            }
130        }
131
132        public object Target {
133            get { return MetaTarget.Value; }
134        }
135
136        public Proc GetBlock() {
137            return (Proc)_args[GetBlockIndex()].Value;
138        }
139
140        public IList/*!*/ GetSplattedArgument() {
141            return (IList)_args[GetSplattedArgumentIndex()].Value;
142        }
143
144        public object GetRhsArgument() {
145            return _args[GetRhsArgumentIndex()].Value;
146        }
147
148        public Expression GetBlockExpression() {
149            return _signature.HasBlock ? _args[GetBlockIndex()].Expression : null;
150        }
151
152        public DynamicMetaObject GetMetaBlock() {
153            return _signature.HasBlock ? _args[GetBlockIndex()] : null;
154        }
155
156        public DynamicMetaObject GetSplattedMetaArgument() {
157            return _signature.HasSplattedArgument ? _args[GetSplattedArgumentIndex()] : null;
158        }
159
160        public Expression GetSplattedArgumentExpression() {
161            return _signature.HasSplattedArgument ? _args[GetSplattedArgumentIndex()].Expression : null;
162        }
163
164        public DynamicMetaObject GetRhsMetaArgument() {
165            return _signature.HasRhsArgument ? _args[GetRhsArgumentIndex()] : null;
166        }
167
168        public Expression GetRhsArgumentExpression() {
169            return _signature.HasRhsArgument ? _args[GetRhsArgumentIndex()].Expression : null;
170        }
171
172
173        public AstExpressions/*!*/ GetSimpleArgumentExpressions() {
174            int count = SimpleArgumentCount;
175            var result = new AstExpressions(count);
176            for (int i = 0, j = GetSimpleArgumentIndex(0); i < count; j++, i++) {
177                result.Add(_args[j].Expression);
178            }
179            return result;
180        }
181
182        internal ReadOnlyCollection<Expression>/*!*/ GetCallSiteArguments(Expression/*!*/ targetExpression) {
183            // context, target, arguments:
184            var result = new AstExpressions(CallSiteArgumentCount);
185
186            if (_hasScopeOrContextArg) {
187                result.Add(_signature.HasScope ? MetaScope.Expression : MetaContext.Expression);
188            }
189            result.Add(targetExpression);
190
191            for (int j = FirstArgumentIndex; j < _args.Length; j++) {
192                result.Add(_args[j].Expression);
193            }
194
195            return result.ToReadOnlyCollection();
196        }
197
198        private int GetSimpleArgumentIndex(int i) {
199            return FirstArgumentIndex + (_signature.HasBlock ? 1 : 0) + i;
200        }
201
202        internal object GetSimpleArgument(int i) {
203            return GetSimpleMetaArgument(i).Value;
204        }
205
206        internal Expression/*!*/ GetSimpleArgumentExpression(int i) {
207            return GetSimpleMetaArgument(i).Expression;
208        }
209
210        internal DynamicMetaObject/*!*/ GetSimpleMetaArgument(int i) {
211            return _args[GetSimpleArgumentIndex(i)];
212        }
213        
214        internal int GetBlockIndex() {
215            Debug.Assert(_signature.HasBlock);
216            return FirstArgumentIndex;
217        }
218
219        internal int GetSplattedArgumentIndex() {
220            Debug.Assert(_signature.HasSplattedArgument);
221            return _args.Length - (_signature.HasRhsArgument ? 2 : 1);
222        }
223
224        internal int GetRhsArgumentIndex() {
225            Debug.Assert(_signature.HasRhsArgument);
226            return _args.Length - 1;
227        }
228
229        // Ruby binders: 
230        internal CallArguments(RubyContext context, DynamicMetaObject/*!*/ scopeOrContextOrTargetOrArgArray, DynamicMetaObject/*!*/[]/*!*/ args, RubyCallSignature signature) {
231            Assert.NotNull(scopeOrContextOrTargetOrArgArray);
232            Assert.NotNullItems(args);
233
234            ArgumentArray argArray = scopeOrContextOrTargetOrArgArray.Value as ArgumentArray;
235            if (argArray != null) {
236                Debug.Assert(args.Length == 0 && argArray.Count >= 1);
237
238                // build meta-objects for arguments wrapped in the array:
239                args = new DynamicMetaObject[argArray.Count - 1];
240                for (int i = 0; i < args.Length; i++) {
241                    args[i] = argArray.GetMetaObject(scopeOrContextOrTargetOrArgArray.Expression, 1 + i);
242                }
243                scopeOrContextOrTargetOrArgArray = argArray.GetMetaObject(scopeOrContextOrTargetOrArgArray.Expression, 0);
244            }
245
246            Debug.Assert(signature.HasScope == scopeOrContextOrTargetOrArgArray.Value is RubyScope);
247            Debug.Assert((context == null && !signature.HasScope) == scopeOrContextOrTargetOrArgArray.Value is RubyContext);
248
249            if (context != null) {
250                // bound site:
251                _context = new DynamicMetaObject(AstUtils.Constant(context), BindingRestrictions.Empty, context);
252                if (signature.HasScope) {
253                    _scope = scopeOrContextOrTargetOrArgArray;
254                    _hasScopeOrContextArg = true;
255                } else {
256                    _target = scopeOrContextOrTargetOrArgArray;
257                }
258            } else if (signature.HasScope) {
259                // unbound site with scope:
260                _context = new DynamicMetaObject(
261                    Methods.GetContextFromScope.OpCall(scopeOrContextOrTargetOrArgArray.Expression), BindingRestrictions.Empty, 
262                    ((RubyScope)scopeOrContextOrTargetOrArgArray.Value).RubyContext
263                );
264                _scope = scopeOrContextOrTargetOrArgArray;
265                _hasScopeOrContextArg = true;
266                _target = null;
267            } else {
268                // unbound site with context:
269                _context = scopeOrContextOrTargetOrArgArray;
270                _hasScopeOrContextArg = true;
271                _target = null;
272            }
273
274            Debug.Assert(_target != null || args.Length > 0);
275
276            _args = args;
277            _copyArgsOnWrite = true;
278            _signature = signature;
279
280            Debug.Assert(!signature.HasSplattedArgument || GetSplattedArgument() != null);
281        }
282
283        // interop binders: the target is a Ruby meta-object closed over the context
284        internal CallArguments(DynamicMetaObject/*!*/ context, DynamicMetaObject/*!*/ target, DynamicMetaObject/*!*/[]/*!*/ args, RubyCallSignature signature) {
285            Assert.NotNull(target, context);
286            Assert.NotNullItems(args);
287
288            Debug.Assert(!signature.HasScope && !signature.HasSplattedArgument);
289
290            _target = target;
291            _context = context;
292            _args = args;
293            _copyArgsOnWrite = true;
294            _signature = signature;
295        }
296
297        // interop binders: the target is a foreign meta-object, the binder is context-bound:
298        internal CallArguments(RubyContext/*!*/ context, DynamicMetaObject/*!*/ target, DynamicMetaObject/*!*/[]/*!*/ args, CallInfo/*!*/ callInfo) 
299            : this (
300                new DynamicMetaObject(AstUtils.Constant(context), BindingRestrictions.Empty, context),
301                target,
302                args,
303                RubyCallSignature.Interop(callInfo.ArgumentCount)
304            ) {
305        }
306
307        public void InsertSimple(int index, DynamicMetaObject/*!*/ arg) {
308            index = GetSimpleArgumentIndex(index);
309
310            _args = ArrayUtils.InsertAt(_args, index, arg);
311            _signature = new RubyCallSignature(_signature.ArgumentCount + 1, _signature.Flags);
312        }
313
314        internal void InsertMethodName(string/*!*/ methodName) {
315            // insert the method name argument into the args
316            object symbol = RubyContext.EncodeIdentifier(methodName);
317            InsertSimple(0, new DynamicMetaObject(AstUtils.Constant(symbol), BindingRestrictions.Empty, symbol));
318        }
319
320        public void SetSimpleArgument(int index, DynamicMetaObject/*!*/ arg) {
321            SetArgument(GetSimpleArgumentIndex(index), arg);
322        }
323
324        private void SetArgument(int index, DynamicMetaObject/*!*/ arg) {
325            if (_copyArgsOnWrite) {
326                _args = ArrayUtils.Copy(_args);
327                _copyArgsOnWrite = false;
328            }
329
330            _args[index] = arg;
331        }
332
333        public void SetTarget(Expression/*!*/ expression, object value) {
334            Assert.NotNull(expression);
335
336            var metaTarget = new DynamicMetaObject(expression, BindingRestrictions.Empty, value);
337
338            if (_target == null) {
339                if (_copyArgsOnWrite) {
340                    _args = ArrayUtils.RemoveFirst(_args);
341                    _copyArgsOnWrite = false;
342                    _target = metaTarget;
343                } else {
344                    _args[0] = metaTarget;
345                }
346            } else {
347                _target = metaTarget;
348            }
349
350            _targetClass = null;
351        }
352    }
353}