PageRenderTime 215ms CodeModel.GetById 120ms app.highlight 16ms RepoModel.GetById 75ms app.codeStats 0ms

/Microsoft.Scripting/Hosting/ScriptScope.cs

https://bitbucket.org/stefanrusek/xronos
C# | 349 lines | 218 code | 36 blank | 95 comment | 4 complexity | 960ae5eeab0437e87ba7d1d0e5d3d8c9 MD5 | raw file
  1/* ****************************************************************************
  2 *
  3 * Copyright (c) Microsoft Corporation. 
  4 *
  5 * This source code is subject to terms and conditions of the Microsoft Public License. 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  Microsoft Public License, 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 Microsoft Public License.
 10 *
 11 * You must not remove this notice, or any other, from this software.
 12 *
 13 *
 14 * ***************************************************************************/
 15
 16#if CODEPLEX_40
 17using System;
 18#else
 19using System; using Microsoft;
 20#endif
 21using System.Collections.Generic;
 22using System.Collections.ObjectModel;
 23using System.Diagnostics;
 24#if CODEPLEX_40
 25using System.Linq.Expressions;
 26#else
 27using Microsoft.Linq.Expressions;
 28#endif
 29using System.Runtime.Remoting;
 30using System.Runtime.Serialization;
 31#if CODEPLEX_40
 32using System.Dynamic;
 33#else
 34using Microsoft.Scripting;
 35#endif
 36using Microsoft.Scripting.Runtime;
 37using Microsoft.Scripting.Utils;
 38using AstUtils = Microsoft.Scripting.Ast.Utils;
 39
 40namespace Microsoft.Scripting.Hosting {
 41    /// <summary>
 42    /// A ScriptScope is a unit of execution for code.  It consists of a global Scope which
 43    /// all code executes in.  A ScriptScope can have an arbitrary initializer and arbitrary
 44    /// reloader. 
 45    /// 
 46    /// ScriptScope is not thread safe. Host should either lock when multiple threads could 
 47    /// access the same module or should make a copy for each thread.
 48    ///
 49    /// Hosting API counterpart for <see cref="Scope"/>.
 50    /// </summary>
 51#if SILVERLIGHT
 52    public sealed class ScriptScope : IDynamicMetaObjectProvider {
 53#else
 54    [DebuggerTypeProxy(typeof(ScriptScope.DebugView))]
 55    public sealed class ScriptScope : MarshalByRefObject, IDynamicMetaObjectProvider {
 56#endif
 57        private readonly Scope _scope;
 58        private readonly ScriptEngine _engine;
 59
 60        internal ScriptScope(ScriptEngine engine, Scope scope) {
 61            Assert.NotNull(engine);
 62            Assert.NotNull(scope);
 63
 64            _scope = scope;
 65            _engine = engine;
 66        }
 67
 68        internal Scope Scope {
 69            get { return _scope; }
 70        }
 71
 72        /// <summary>
 73        /// Gets an engine for the language associated with this scope.
 74        /// Returns invariant engine if the scope is language agnostic.
 75        /// </summary>
 76        public ScriptEngine Engine {
 77            get {
 78                // InvariantContext should not have an engine
 79                // TODO: If _engine itself could be set to null, we wouldn't
 80                // need this check
 81                if (_engine.LanguageContext is InvariantContext) {
 82                    return null;
 83                }
 84                return _engine;
 85            }
 86        }
 87        
 88        /// <summary>
 89        /// Gets a value stored in the scope under the given name.
 90        /// </summary>
 91        /// <exception cref="MissingMemberException">The specified name is not defined in the scope.</exception>
 92        /// <exception cref="ArgumentNullException"><paramref name="name"/> is a <c>null</c> reference.</exception>
 93        public object GetVariable(string name) {
 94            return _scope.LookupName(_engine.LanguageContext, SymbolTable.StringToId(name));
 95        }
 96
 97        /// <summary>
 98        /// Gets a value stored in the scope under the given name.
 99        /// Converts the result to the specified type using the conversion that the language associated with the scope defines.
100        /// If no language is associated with the scope, the default CLR conversion is attempted.
101        /// </summary>
102        /// <exception cref="MissingMemberException">The specified name is not defined in the scope.</exception>
103        /// <exception cref="ArgumentNullException"><paramref name="name"/> is a <c>null</c> reference.</exception>
104        public T GetVariable<T>(string name) {
105            return _engine.Operations.ConvertTo<T>(_engine.GetVariable(this, name));
106        }
107
108        /// <summary>
109        /// Tries to get a value stored in the scope under the given name.
110        /// </summary>
111        /// <exception cref="ArgumentNullException"><paramref name="name"/> is a <c>null</c> reference.</exception>
112        public bool TryGetVariable(string name, out object value) {
113            return _scope.TryGetName(SymbolTable.StringToId(name), out value);
114        }
115
116        /// <summary>
117        /// Tries to get a value stored in the scope under the given name.
118        /// Converts the result to the specified type using the conversion that the language associated with the scope defines.
119        /// If no language is associated with the scope, the default CLR conversion is attempted.
120        /// </summary>
121        /// <exception cref="ArgumentNullException"><paramref name="name"/> is a <c>null</c> reference.</exception>
122        public bool TryGetVariable<T>(string name, out T value) {
123            object result;
124            if (_scope.TryGetName(SymbolTable.StringToId(name), out result)) {
125                value = _engine.Operations.ConvertTo<T>(result);
126                return true;
127            }
128            value = default(T);
129            return false;
130        }
131
132        /// <summary>
133        /// Sets the name to the specified value.
134        /// </summary>
135        /// <exception cref="ArgumentNullException"><paramref name="name"/> is a <c>null</c> reference.</exception>
136        public void SetVariable(string name, object value) {
137            _scope.SetName(SymbolTable.StringToId(name), value);
138        }
139
140#if !SILVERLIGHT
141        /// <summary>
142        /// Gets a handle for a value stored in the scope under the given name.
143        /// </summary>
144        /// <exception cref="MissingMemberException">The specified name is not defined in the scope.</exception>
145        /// <exception cref="ArgumentNullException"><paramref name="name"/> is a <c>null</c> reference.</exception>
146        public ObjectHandle GetVariableHandle(string name) {
147            return new ObjectHandle(GetVariable(name));
148        }
149
150        /// <summary>
151        /// Tries to get a handle for a value stored in the scope under the given name.
152        /// Returns <c>true</c> if there is such name, <c>false</c> otherwise. 
153        /// </summary>
154        /// <exception cref="ArgumentNullException"><paramref name="name"/> is a <c>null</c> reference.</exception>
155        public bool TryGetVariableHandle(string name, out ObjectHandle handle) {
156            object value;
157            if (TryGetVariable(name, out value)) {
158                handle = new ObjectHandle(value);
159                return true;
160            } else {
161                handle = null;
162                return false;
163            }
164        }
165
166        /// <summary>
167        /// Sets the name to the specified value.
168        /// </summary>
169        /// <exception cref="SerializationException">
170        /// The value held by the handle isn't from the scope's app-domain and isn't serializable or MarshalByRefObject.
171        /// </exception>
172        /// <exception cref="ArgumentNullException"><paramref name="name"/> or <paramref name="handle"/> is a <c>null</c> reference.</exception>
173        public void SetVariable(string name, ObjectHandle handle) {
174            ContractUtils.RequiresNotNull(handle, "handle");
175            SetVariable(name, handle.Unwrap());
176        }
177#endif
178
179        /// <summary>
180        /// Determines if this context or any outer scope contains the defined name.
181        /// </summary>
182        /// <exception cref="ArgumentNullException"><paramref name="name"/> is a <c>null</c> reference.</exception>
183        public bool ContainsVariable(string name) {
184            return _scope.ContainsName(SymbolTable.StringToId(name));
185        }
186
187        /// <summary>
188        /// Removes the variable of the given name from this scope.
189        /// </summary> 
190        /// <returns><c>true</c> if the value existed in the scope before it has been removed.</returns>
191        /// <exception cref="ArgumentNullException"><paramref name="name"/> is a <c>null</c> reference.</exception>
192        public bool RemoveVariable(string name) {
193            return _scope.TryRemoveName(SymbolTable.StringToId(name));
194        }
195
196        /// <summary>
197        /// Gets a list of variable names stored in the scope.
198        /// </summary>
199        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
200        public IEnumerable<string> GetVariableNames() {
201            // Remoting: we eagerly enumerate all variables to avoid cross domain calls for each item.
202
203            var result = new List<string>();
204            foreach (var entry in _scope.Items) {
205                result.Add(SymbolTable.IdToString(entry.Key));
206            }
207            result.TrimExcess();
208            return result;
209        }
210
211        /// <summary>
212        /// Gets an array of variable names and their values stored in the scope.
213        /// </summary>
214        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
215        public IEnumerable<KeyValuePair<string, object>> GetItems() {
216            // Remoting: we eagerly enumerate all variables to avoid cross domain calls for each item.
217
218            var result = new List<KeyValuePair<string, object>>();
219            foreach (var entry in _scope.Items) {
220                result.Add(new KeyValuePair<string, object>(SymbolTable.IdToString(entry.Key), entry.Value));
221            }
222            result.TrimExcess();
223            return result;
224        }
225
226        #region DebugView
227#if !SILVERLIGHT
228        internal sealed class DebugView {
229            private readonly ScriptScope _scope;
230
231            public DebugView(ScriptScope scope) {
232                Assert.NotNull(scope);
233                _scope = scope;
234            }
235
236            public ScriptEngine Language {
237                get { return _scope._engine; }
238            }
239
240            public System.Collections.Hashtable Variables {
241                get {
242                    System.Collections.Hashtable result = new System.Collections.Hashtable();
243                    foreach (KeyValuePair<string, object> variable in _scope.GetItems()) {
244                        result[variable.Key] = variable.Value;
245                    }
246                    return result;
247                }
248            }
249        }
250#endif
251        #endregion
252
253        #region IDynamicMetaObjectProvider implementation
254
255        DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) {
256            return new Meta(parameter, this);
257        }
258
259        private sealed class Meta : DynamicMetaObject {
260            internal Meta(Expression parameter, ScriptScope scope)
261                : base(parameter, BindingRestrictions.Empty, scope) {
262            }
263
264            // TODO: support for IgnoreCase in underlying ScriptScope APIs
265            public override DynamicMetaObject BindGetMember(GetMemberBinder action) {
266                var result = Expression.Variable(typeof(object), "result");
267                var fallback = action.FallbackGetMember(this);
268
269                return new DynamicMetaObject(
270                    Expression.Block(
271                        new ParameterExpression[] { result },
272                        Expression.Condition(
273                            Expression.Call(
274                                AstUtils.Convert(Expression, typeof(ScriptScope)),
275                                typeof(ScriptScope).GetMethod("TryGetVariable", new[] { typeof(string), typeof(object).MakeByRefType() }),
276                                AstUtils.Constant(action.Name),
277                                result
278                            ),
279                            result,
280                            AstUtils.Convert(fallback.Expression, typeof(object))
281                        )
282                    ),
283                    BindingRestrictionsHelpers.GetRuntimeTypeRestriction(Expression, typeof(ScriptScope)).Merge(fallback.Restrictions)
284                );
285            }
286
287            // TODO: support for IgnoreCase in underlying ScriptScope APIs
288            public override DynamicMetaObject BindSetMember(SetMemberBinder action, DynamicMetaObject value) {
289                return new DynamicMetaObject(
290                    Expression.Call(
291                        AstUtils.Convert(Expression, typeof(ScriptScope)),
292                        typeof(ScriptScope).GetMethod("SetVariable", new[] { typeof(string), typeof(object) }),
293                        AstUtils.Constant(action.Name),
294                        AstUtils.Convert(value.Expression, typeof(object))
295                    ),
296                    Restrictions.Merge(value.Restrictions).Merge(BindingRestrictionsHelpers.GetRuntimeTypeRestriction(Expression, typeof(ScriptScope)))
297                );
298            }
299
300            // TODO: support for IgnoreCase in underlying ScriptScope APIs
301            public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder action) {
302                var fallback = action.FallbackDeleteMember(this);
303                return new DynamicMetaObject(
304                    Expression.IfThenElse(
305                        Expression.Call(
306                            AstUtils.Convert(Expression, typeof(ScriptScope)),
307                            typeof(ScriptScope).GetMethod("RemoveVariable"),
308                            AstUtils.Constant(action.Name)
309                        ),
310                        AstUtils.Empty(),
311                        fallback.Expression
312                    ),
313                    Restrictions.Merge(BindingRestrictionsHelpers.GetRuntimeTypeRestriction(Expression, typeof(ScriptScope))).Merge(fallback.Restrictions)
314                );
315            }
316
317            // TODO: support for IgnoreCase in underlying ScriptScope APIs
318            public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder action, DynamicMetaObject[] args) {
319                var fallback = action.FallbackInvokeMember(this, args);
320                var result = Expression.Variable(typeof(object), "result");
321
322                var fallbackInvoke = action.FallbackInvoke(new DynamicMetaObject(result, BindingRestrictions.Empty), args, null);
323
324                return new DynamicMetaObject(
325                    Expression.Block(
326                        new ParameterExpression[] { result },
327                        Expression.Condition(
328                            Expression.Call(
329                                AstUtils.Convert(Expression, typeof(ScriptScope)),
330                                typeof(ScriptScope).GetMethod("TryGetVariable", new[] { typeof(string), typeof(object).MakeByRefType() }),
331                                AstUtils.Constant(action.Name),
332                                result
333                            ),
334                            AstUtils.Convert(fallbackInvoke.Expression, typeof(object)),
335                            AstUtils.Convert(fallback.Expression, typeof(object))
336                        )
337                    ),
338                    BindingRestrictions.Combine(args).Merge(BindingRestrictionsHelpers.GetRuntimeTypeRestriction(Expression, typeof(ScriptScope))).Merge(fallback.Restrictions)
339                );
340            }
341
342            public override IEnumerable<string> GetDynamicMemberNames() {
343                return ((ScriptScope)Value).GetVariableNames();
344            }
345        }
346
347        #endregion
348    }
349}