PageRenderTime 28ms CodeModel.GetById 16ms app.highlight 8ms RepoModel.GetById 1ms app.codeStats 1ms

/Microsoft.Scripting/Hosting/ScriptRuntime.cs

https://bitbucket.org/stefanrusek/xronos
C# | 384 lines | 249 code | 63 blank | 72 comment | 20 complexity | 35dc04a7ce7298f689d92a503701a11a 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.Diagnostics;
 23using System.IO;
 24using System.Reflection;
 25using System.Security.Permissions;
 26using System.Text;
 27using System.Threading;
 28using Microsoft.Scripting.Runtime;
 29using Microsoft.Scripting.Utils;
 30
 31namespace Microsoft.Scripting.Hosting {
 32    /// <summary>
 33    /// Represents a Dynamic Language Runtime in Hosting API. 
 34    /// Hosting API counterpart for <see cref="ScriptDomainManager"/>.
 35    /// </summary>
 36    public sealed class ScriptRuntime
 37#if !SILVERLIGHT
 38        : MarshalByRefObject
 39#endif
 40    {
 41        private readonly Dictionary<LanguageContext, ScriptEngine> _engines;
 42        private readonly ScriptDomainManager _manager;
 43        private readonly InvariantContext _invariantContext;
 44        private readonly ScriptIO _io;
 45        private readonly ScriptHost _host;
 46        private readonly ScriptRuntimeSetup _setup;
 47        private ScriptScope _globals;
 48        private ScriptEngine _invariantEngine;
 49
 50        /// <summary>
 51        /// Creates ScriptRuntime in the current app-domain and initialized according to the the specified settings.
 52        /// Creates an instance of host class specified in the setup and associates it with the created runtime.
 53        /// Both Runtime and ScriptHost are collocated in the current app-domain.
 54        /// </summary>
 55        public ScriptRuntime(ScriptRuntimeSetup setup) {
 56            ContractUtils.RequiresNotNull(setup, "setup");
 57
 58            // Do this first, so we detect configuration errors immediately
 59            DlrConfiguration config = setup.ToConfiguration();
 60            _setup = setup;
 61
 62            _host = ReflectionUtils.CreateInstance<ScriptHost>(setup.HostType, setup.HostArguments.ToArray<object>());
 63
 64            ScriptHostProxy hostProxy = new ScriptHostProxy(_host);
 65
 66            _manager = new ScriptDomainManager(hostProxy, config);
 67            _invariantContext = new InvariantContext(_manager);
 68
 69            _io = new ScriptIO(_manager.SharedIO);
 70            _engines = new Dictionary<LanguageContext, ScriptEngine>();
 71
 72            bool freshEngineCreated;
 73            _globals = new ScriptScope(GetEngineNoLockNoNotification(_invariantContext, out freshEngineCreated), _manager.Globals);
 74
 75            // runtime needs to be all set at this point, host code is called
 76
 77            _host.SetRuntime(this);
 78
 79            object noDefaultRefs;
 80            if (!setup.Options.TryGetValue("NoDefaultReferences", out noDefaultRefs) || Convert.ToBoolean(noDefaultRefs) == false) {
 81                LoadAssembly(typeof(string).Assembly);
 82                LoadAssembly(typeof(System.Diagnostics.Debug).Assembly);
 83            }
 84        }
 85
 86        internal ScriptDomainManager Manager {
 87            get { return _manager; }
 88        }
 89
 90        public ScriptHost Host {
 91            get { return _host; }
 92        }
 93
 94        public ScriptIO IO {
 95            get { return _io; }
 96        }
 97        
 98        /// <summary>
 99        /// Creates a new runtime with languages set up according to the current application configuration 
100        /// (using System.Configuration).
101        /// </summary>
102        public static ScriptRuntime CreateFromConfiguration() {
103            return new ScriptRuntime(ScriptRuntimeSetup.ReadConfiguration());
104        }
105
106        #region Remoting
107
108#if !SILVERLIGHT
109
110        /// <summary>
111        /// Creates ScriptRuntime in the current app-domain and initialized according to the the specified settings.
112        /// Creates an instance of host class specified in the setup and associates it with the created runtime.
113        /// Both Runtime and ScriptHost are collocated in the specified app-domain.
114        /// </summary>
115        public static ScriptRuntime CreateRemote(AppDomain domain, ScriptRuntimeSetup setup) {
116            ContractUtils.RequiresNotNull(domain, "domain");
117#if SYSTEM_CORE
118            return (ScriptRuntime)domain.CreateInstanceAndUnwrap(
119                typeof(ScriptRuntime).Assembly.FullName, 
120                typeof(ScriptRuntime).FullName, 
121                false, 
122                BindingFlags.Default, 
123                null, 
124                new object[] { setup }, 
125                null,
126                null
127            );
128#else
129            //The API CreateInstanceAndUnwrap is obsolete in Dev10.
130            return (ScriptRuntime)domain.CreateInstanceAndUnwrap(
131                typeof(ScriptRuntime).Assembly.FullName, 
132                typeof(ScriptRuntime).FullName, 
133                false, 
134                BindingFlags.Default, 
135                null, 
136                new object[] { setup }, 
137                null,
138                null,
139                null
140            );
141#endif
142        }
143
144        // TODO: Figure out what is the right lifetime
145        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
146        public override object InitializeLifetimeService() {
147            return null;
148        }
149#endif
150        #endregion
151
152        public ScriptRuntimeSetup Setup {
153            get {
154                return _setup;
155            }
156        }
157
158        #region Engines
159
160        public ScriptEngine GetEngine(string languageName) {
161            ContractUtils.RequiresNotNull(languageName, "languageName");
162
163            ScriptEngine engine;
164            if (!TryGetEngine(languageName, out engine)) {
165                throw new ArgumentException(String.Format("Unknown language name: '{0}'", languageName));
166            }
167
168            return engine;
169        }
170
171        public ScriptEngine GetEngineByTypeName(string assemblyQualifiedTypeName) {
172            ContractUtils.RequiresNotNull(assemblyQualifiedTypeName, "assemblyQualifiedTypeName");
173            return GetEngine(_manager.GetLanguageByTypeName(assemblyQualifiedTypeName));
174        }
175
176        /// <exception cref="ArgumentException"></exception>
177        /// <exception cref="ArgumentNullException"></exception>
178        public ScriptEngine GetEngineByFileExtension(string fileExtension) {
179            ContractUtils.RequiresNotNull(fileExtension, "fileExtension");
180
181            ScriptEngine engine;
182            if (!TryGetEngineByFileExtension(fileExtension, out engine)) {
183                throw new ArgumentException(String.Format("Unknown file extension: '{0}'", fileExtension));
184            }
185
186            return engine;
187        }
188
189        public bool TryGetEngine(string languageName, out ScriptEngine engine) {
190            LanguageContext language;
191            if (!_manager.TryGetLanguage(languageName, out language)) {
192                engine = null;
193                return false;
194            }
195
196            engine = GetEngine(language);
197            return true;
198        }
199
200        public bool TryGetEngineByFileExtension(string fileExtension, out ScriptEngine engine) {
201            LanguageContext language;
202            if (!_manager.TryGetLanguageByFileExtension(fileExtension, out language)) {
203                engine = null;
204                return false;
205            }
206
207            engine = GetEngine(language);
208            return true;
209        }
210
211        /// <summary>
212        /// Gets engine for the specified language.
213        /// </summary>
214        internal ScriptEngine GetEngine(LanguageContext language) {
215            Assert.NotNull(language);
216
217            ScriptEngine engine;
218            bool freshEngineCreated;
219            lock (_engines) {
220                engine = GetEngineNoLockNoNotification(language, out freshEngineCreated);
221            }
222
223            if (freshEngineCreated && !ReferenceEquals(language, _invariantContext)) {
224                _host.EngineCreated(engine);
225            }
226
227            return engine;
228        }
229
230        /// <summary>
231        /// Looks up the engine for the specified language. It the engine hasn't been created in this Runtime, it is instantiated here.
232        /// The method doesn't lock nor send notifications to the host.
233        /// </summary>
234        private ScriptEngine GetEngineNoLockNoNotification(LanguageContext language, out bool freshEngineCreated) {
235            Debug.Assert(_engines != null, "Invalid ScriptRuntime initialiation order");
236
237            ScriptEngine engine;
238            if (freshEngineCreated = !_engines.TryGetValue(language, out engine)) {
239                engine = new ScriptEngine(this, language);
240                Thread.MemoryBarrier();
241                _engines.Add(language, engine);
242            }
243            return engine;
244        }
245
246        #endregion
247
248        #region Compilation, Module Creation
249
250        public ScriptScope CreateScope() {
251            return InvariantEngine.CreateScope();
252        }
253
254        public ScriptScope CreateScope(string languageId) {
255            return GetEngine(languageId).CreateScope();
256        }
257
258        public ScriptScope CreateScope(IAttributesCollection dictionary) {
259            return InvariantEngine.CreateScope(dictionary);
260        }
261
262        public ScriptScope CreateScope(string languageId, IAttributesCollection dictionary) {
263            return GetEngine(languageId).CreateScope(dictionary);
264        }
265
266        #endregion
267
268        // TODO: file IO exceptions, parse exceptions, execution exceptions, etc.
269        /// <exception cref="ArgumentException">
270        /// path is empty, contains one or more of the invalid characters defined in GetInvalidPathChars or doesn't have an extension.
271        /// </exception>
272        public ScriptScope ExecuteFile(string path) {
273            ContractUtils.RequiresNotEmpty(path, "path");
274            string extension = Path.GetExtension(path);
275
276            ScriptEngine engine;
277            if (!TryGetEngineByFileExtension(extension, out engine)) {
278                throw new ArgumentException(String.Format("File extension '{0}' is not associated with any language.", extension));
279            }
280
281            return engine.ExecuteFile(path);
282        }
283
284        /// <exception cref="ArgumentNullException">path is null</exception>
285        /// <exception cref="ArgumentException">file extension does not map to language engine</exception>
286        /// <exception cref="InvalidOperationException">language does not have any search paths</exception>
287        /// <exception cref="FileNotFoundException">file does exist in language's search path</exception>
288        public ScriptScope UseFile(string path) {
289            ContractUtils.RequiresNotEmpty(path, "path");
290            string extension = Path.GetExtension(path);
291
292            ScriptEngine engine;
293            if (!TryGetEngineByFileExtension(extension, out engine)) {
294                throw new ArgumentException(string.Format("File extension '{0}' is not associated with any language.", extension));
295            }
296
297            var searchPaths = engine.GetSearchPaths();
298            if (searchPaths.Count == 0) {
299                throw new InvalidOperationException(string.Format("No search paths defined for language '{0}'", engine.Setup.DisplayName));
300            }
301
302            // See if the file is already loaded, if so return the scope
303            foreach (string searchPath in searchPaths) {
304                string filePath = Path.Combine(searchPath, path);
305                ScriptScope scope = engine.GetScope(filePath);
306                if (scope != null) {
307                    return scope;
308                }
309            }
310
311            // Find the file on disk, then load it
312            foreach (string searchPath in searchPaths) {
313                string filePath = Path.Combine(searchPath, path);
314                if (_manager.Platform.FileExists(filePath)) {
315                    return ExecuteFile(filePath);
316                }
317            }
318
319            // Didn't find the file, throw
320            string allPaths = searchPaths.Aggregate((x, y) => x + ", " + y);
321            throw new FileNotFoundException(string.Format("File '{0}' not found in language's search path: {1}", path, allPaths));
322        }
323
324        /// <summary>
325        /// This property returns the "global object" or name bindings of the ScriptRuntime as a ScriptScope.  
326        /// 
327        /// You can set the globals scope, which you might do if you created a ScriptScope with an 
328        /// IAttributesCollection so that your host could late bind names.
329        /// </summary>
330        public ScriptScope Globals {
331            get { return _globals; }
332            set {
333                ContractUtils.RequiresNotNull(value, "value");
334
335                // TODO: this is wrong, we ignore other parts of the scope here
336                _globals = value;
337                _manager.SetGlobalsDictionary(_globals.Scope.Dict);
338            }
339        }
340
341        /// <summary>
342        /// This method walks the assembly's namespaces and name bindings to ScriptRuntime.Globals 
343        /// to represent the types available in the assembly.  Each top-level namespace name gets 
344        /// bound in Globals to a dynamic object representing the namespace.  Within each top-level 
345        /// namespace object, nested namespace names are bound to dynamic objects representing each 
346        /// tier of nested namespaces.  When this method encounters the same namespace-qualified name, 
347        /// it merges names together objects representing the namespaces.
348        /// </summary>
349        /// <param name="assembly"></param>
350        public void LoadAssembly(Assembly assembly) {
351            _manager.LoadAssembly(assembly);
352        }
353
354        public ObjectOperations Operations {
355            get {
356                return InvariantEngine.Operations;
357            }
358        }
359
360        public ObjectOperations CreateOperations() {
361            return InvariantEngine.CreateOperations();
362        }
363
364        public void Shutdown() {
365            List<LanguageContext> lcs;
366            lock (_engines) {
367                lcs = new List<LanguageContext>(_engines.Keys);
368            }
369
370            foreach (var language in lcs) {
371                language.Shutdown();
372            }
373        }
374
375        internal ScriptEngine InvariantEngine {
376            get {
377                if (_invariantEngine == null) {
378                    _invariantEngine = GetEngine(_invariantContext);
379                }
380                return _invariantEngine;
381            }
382        }
383    }
384}