edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/ClassInitGenerator/Program.cs;C429806 File: Program.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/ClassInitGenerator/Program.cs;C429806 (server) 5/9/2008 2:50 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/ClassInitGenerator/Program.cs;Loader9 @@ -671,8 +671,6 @@ _output.Write("Context.FalseClass = "); } else if (def.QualifiedName == "Exception") { _output.Write("Context.ExceptionClass = "); - } else if (def.QualifiedName == "RuntimeError") { - _output.Write("Context.RuntimeErrorClass = "); } else if (def.QualifiedName == "StandardError") { _output.Write("Context.StandardErrorClass = "); } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Driver.cs;C420856 File: Driver.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Driver.cs;C420856 (server) 5/9/2008 2:50 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Driver.cs;Loader9 @@ -59,6 +59,7 @@ _engine = IronRuby.GetEngine(_env); _executionContext = IronRuby.GetExecutionContext(_engine); _engine.Options.InterpretedMode = _driver.InterpretedMode; + ((RubyEngineOptions)_engine.Options).ShowWarnings = true; } } @@ -278,5 +279,11 @@ Snippets.Shared.Dump(); } } + + internal string/*!*/ MakeTempDir() { + string dir = Path.Combine(Path.GetTempPath(), _testRuntime.TestName); + Directory.CreateDirectory(dir); + return dir; + } } } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/IronRuby.Tests.csproj;C429806 File: IronRuby.Tests.csproj =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/IronRuby.Tests.csproj;C429806 (server) 5/9/2008 2:50 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/IronRuby.Tests.csproj;Loader9 @@ -70,7 +70,7 @@ - + =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/RubyTests.cs;C429806 File: RubyTests.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/RubyTests.cs;C429806 (server) 5/9/2008 2:50 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/RubyTests.cs;Loader9 @@ -187,9 +187,14 @@ // TODO: interop, hosting: Scenario_RubyConsole2, Scenario_RubyConsole3, Scenario_RubyConsole4, - Scenario_RubyRequire1, - // TODO: Ctlr+F5 doesn't work: Scenario_RubyRequire2, - // TODO: interop, file loading: Scenario_RubyRequire3, + + Loader_Assemblies1, + + Require1, + RequireInterop1, + Load1, + LibraryLoader1, + Scenario_ClrFields1, Scenario_ClrTypes1, Scenario_ClrMethods1, =================================================================== rename, edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/LoaderTests.cs;C417101 File: LoaderTests.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/LoaderTests.cs;C417101 (server) 5/9/2008 2:50 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/LoaderTests.cs;Loader9 @@ -27,14 +27,56 @@ using Ruby.Compiler; using Ruby.Compiler.Ast; using Ruby.Runtime; +using System.Reflection; +using Ruby.Builtins; namespace Ruby.Tests { public partial class Tests { - public void Scenario_RubyRequire1() { + + public void Loader_Assemblies1() { + string assembly; + string type; + string str; + bool b; + + str = "a.rb"; + b = Loader.TryParseAssemblyName(str, out type, out assembly); + Assert(b == false); + + str = "IronRuby"; + b = Loader.TryParseAssemblyName(str, out type, out assembly); + Assert(b == false); + + str = @"..\foo\bar\a,b.rb"; + b = Loader.TryParseAssemblyName(str, out type, out assembly); + Assert(b == false); + + str = "Ruby.Runtime.RubyContext, IronRuby, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"; + b = Loader.TryParseAssemblyName(str, out type, out assembly); + Assert(b == true && + assembly == "IronRuby, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" && + type == "Ruby.Runtime.RubyContext" + ); + + str = "IronRuby, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"; + b = Loader.TryParseAssemblyName(str, out type, out assembly); + Assert(b == true && assembly == str && type == null); + + str = "IronRuby, Culture=neutral, PublicKeyToken=31bf3856ad364e35"; + b = Loader.TryParseAssemblyName(str, out type, out assembly); + Assert(b == true && assembly == str && type == null); + + str = "IronRuby, Version=1.0.0.0"; + b = Loader.TryParseAssemblyName(str, out type, out assembly); + Assert(b == true && assembly == str && type == null); + } + + public void Require1() { try { - File.WriteAllText("a.rb", @" -C = 123 -"); + string temp = _driver.MakeTempDir(); + ExecutionContext.Loader.SetLoadPaths(temp); + File.WriteAllText(Path.Combine(temp, "a.rb"), @"C = 123"); + AssertOutput(delegate() { CompilerTest(@" puts(require('a')) @@ -44,51 +86,81 @@ true 123 "); + + AssertOutput(delegate() { + CompilerTest(@" +puts(require('a.rb')) +puts C +"); + }, @" +false +123 +"); } finally { File.Delete("a.rb"); } } - public void Scenario_RubyRequire2() { + public void Load1() { try { - File.WriteAllText("a.rb", @" -C = 123 + string temp = _driver.MakeTempDir(); + ExecutionContext.Loader.SetLoadPaths(temp); + + File.WriteAllText(Path.Combine(temp, "a.rb"), @"C = 123"); + + AssertOutput(delegate() { + CompilerTest(@" +puts(load('a.rb', true)) +puts C rescue puts 'error' "); - AssertExceptionThrown(delegate() { - CompilerTest(@" -puts(load('a',true)) -puts C + }, @" +true +error "); - }); } finally { File.Delete("a.rb"); } } - public void Scenario_RubyRequire3() { + public void RequireInterop1() { try { - File.WriteAllText("b.py", @" -def format(format, *args): - return format % (args) + string temp = _driver.MakeTempDir(); + ExecutionContext.Loader.SetLoadPaths(temp); -x = 'Python' + File.WriteAllText(Path.Combine(temp, "a.py"), @" +print 'Hello from Python' "); AssertOutput(delegate() { CompilerTest(@" -#TODO: maybe we don't need to return a module, we can mix it in instead (load 'foo', true will do) -a = require('b') -puts a -puts a.x -puts a.format('Hello from %s on %s!', 'Ruby', 'Silverlight') +require('a') "); }, @" -Microsoft.Scripting.ScriptModule -Python -Hello from Ruby on Silverlight!"); +Hello from Python +"); } finally { File.Delete("b.py"); } } + + public class TestLibraryInitializer1 : LibraryInitializer { + protected override void LoadModules() { + Context.ObjectClass.SetConstant("TEST_LIBRARY", "hello from library"); + } + } + + public void LibraryLoader1() { + ExecutionContext.DefineGlobalVariable("lib_name", MutableString.Create(typeof(TestLibraryInitializer1).AssemblyQualifiedName)); + + AssertOutput(delegate() { + CompilerTest(@" +require($lib_name) +puts TEST_LIBRARY +"); + }, @" +hello from library +"); + + } } } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializer.Generated.cs;C435539 File: Initializer.Generated.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializer.Generated.cs;C435539 (server) 5/9/2008 2:50 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializer.Generated.cs;Loader9 @@ -189,7 +189,7 @@ DefineGlobalClass("RegexpError", typeof(Ruby.Builtins.RegexpError), typeof(Ruby.Builtins.RegexpErrorOps), null, null, def39, Ruby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new Microsoft.Scripting.Utils.Function(Ruby.Builtins.RegexpErrorOps.Factory), }); - Ruby.Builtins.RubyClass def45 = Context.RuntimeErrorClass = DefineGlobalClass("RuntimeError", typeof(Ruby.Builtins.RuntimeError), null, null, null, def39, Ruby.Builtins.RubyModule.EmptyArray, null); + Ruby.Builtins.RubyClass def45 = DefineGlobalClass("RuntimeError", typeof(Ruby.Builtins.RuntimeError), null, null, null, def39, Ruby.Builtins.RubyModule.EmptyArray, null); DefineGlobalClass("SecurityError", typeof(System.Security.SecurityException), typeof(Ruby.Builtins.SecurityErrorOps), null, null, def39, Ruby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new Microsoft.Scripting.Utils.Function(Ruby.Builtins.SecurityErrorOps.Factory), }); @@ -1910,6 +1910,10 @@ new Microsoft.Scripting.Utils.Function(Ruby.Builtins.Kernel.Load), }); + module.DefineMethod("load_assembly", 0xa, new System.Delegate[] { + new Microsoft.Scripting.Utils.Function(Ruby.Builtins.Kernel.LoadAssembly), + }); + module.DefineMethod("local_variables", 0xa, new System.Delegate[] { new Microsoft.Scripting.Utils.Function(Ruby.Builtins.Kernel.GetLocalVariableNames), }); @@ -2070,6 +2074,10 @@ new Microsoft.Scripting.Utils.Function(Ruby.Builtins.Kernel.Load), }); + module.DefineMethod("load_assembly", 0x11, new System.Delegate[] { + new Microsoft.Scripting.Utils.Function(Ruby.Builtins.Kernel.LoadAssembly), + }); + module.DefineMethod("local_variables", 0x11, new System.Delegate[] { new Microsoft.Scripting.Utils.Function(Ruby.Builtins.Kernel.GetLocalVariableNames), }); =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Kernel.cs;C435539 File: Kernel.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Kernel.cs;C435539 (server) 5/9/2008 2:50 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Kernel.cs;Loader9 @@ -236,16 +236,22 @@ [RubyMethod("load", RubyMethodAttributes.PrivateInstance)] [RubyMethod("load", RubyMethodAttributes.PublicSingleton)] - public static bool Load(CodeContext/*!*/ context, object self, MutableString/*!*/ libraryName) { - return RubyUtils.GetExecutionContext(context).Loader.LoadModule(context, self, libraryName.ConvertToString(), LoadFlags.None); + public static bool Load(CodeContext/*!*/ context, object self, [NotNull]MutableString/*!*/ libraryName) { + return RubyUtils.GetExecutionContext(context).Loader.LoadFile(context, self, libraryName, LoadFlags.None); } [RubyMethod("load", RubyMethodAttributes.PrivateInstance)] [RubyMethod("load", RubyMethodAttributes.PublicSingleton)] - public static bool Load(CodeContext/*!*/ context, object self, MutableString/*!*/ libraryName, bool wrap) { - return RubyUtils.GetExecutionContext(context).Loader.LoadModule(context, self, libraryName.ConvertToString(), wrap ? LoadFlags.LoadIsolated : LoadFlags.None); + public static bool Load(CodeContext/*!*/ context, object self, [NotNull]MutableString/*!*/ libraryName, bool wrap) { + return RubyUtils.GetExecutionContext(context).Loader.LoadFile(context, self, libraryName, wrap ? LoadFlags.LoadIsolated : LoadFlags.None); } + [RubyMethod("load_assembly", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("load_assembly", RubyMethodAttributes.PublicSingleton)] + public static bool LoadAssembly(CodeContext/*!*/ context, object self, [NotNull]MutableString/*!*/ assemblyName, [Optional, NotNull]MutableString typeName) { + return RubyUtils.GetExecutionContext(context).Loader.LoadAssembly(assemblyName.ConvertToString(), typeName != null ? typeName.ConvertToString() : null, true); + } + [RubyMethod("local_variables", RubyMethodAttributes.PrivateInstance)] [RubyMethod("local_variables", RubyMethodAttributes.PublicSingleton)] public static RubyArray/*!*/ GetLocalVariableNames(CodeContext/*!*/ context, object self) { @@ -833,7 +839,7 @@ [RubyMethod("require")] [RubyMethod("require", RubyMethodAttributes.PublicSingleton)] public static bool Require(CodeContext/*!*/ context, object self, [NotNull]MutableString/*!*/ libraryName) { - return RubyUtils.GetExecutionContext(context).Loader.LoadModule(context, self, libraryName.ConvertToString(), LoadFlags.LoadOnce); + return RubyUtils.GetExecutionContext(context).Loader.LoadFile(context, self, libraryName, LoadFlags.LoadOnce | LoadFlags.AppendExtensions); } #endregion =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Compiler/Parser/Symbols.cs;C429806 File: Symbols.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Compiler/Parser/Symbols.cs;C429806 (server) 5/9/2008 2:50 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Compiler/Parser/Symbols.cs;Loader9 @@ -29,6 +29,7 @@ public static readonly SymbolId MethodMissing = SymbolTable.StringToId("method_missing"); public static readonly SymbolId RespondTo = SymbolTable.StringToId("respond_to?"); public static readonly SymbolId ToProc = SymbolTable.StringToId("to_proc"); + public static readonly SymbolId ToStr = SymbolTable.StringToId("to_str"); public static readonly SymbolId Initialize = SymbolTable.StringToId("initialize"); public static readonly SymbolId Object = SymbolTable.StringToId("Object"); public static readonly SymbolId Kernel = SymbolTable.StringToId("Kernel"); =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Loader.cs;C435539 File: Loader.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Loader.cs;C435539 (server) 5/9/2008 2:50 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Loader.cs;Loader9 @@ -30,6 +30,10 @@ using Ruby.Builtins; using Microsoft.Scripting.Utils; using Microsoft.Scripting.Runtime; +using System.Reflection; +using System.Text.RegularExpressions; +using Microsoft.Scripting.Actions; +using Ruby.Runtime.Calls; namespace Ruby.Runtime { [Flags] @@ -37,14 +41,16 @@ None = 0, LoadOnce = 1, LoadIsolated = 2, + AppendExtensions = 4, } public sealed class Loader { - private enum FileKind { + internal enum FileKind { RubySourceFile, NonRubySourceFile, Assembly, + Type, Unknown, } @@ -57,19 +63,27 @@ private readonly RubyArray/*!*/ _loadedFiles; /// - /// Thread safety: the user of this object is responsible for locking it. + /// TODO: Thread safety: the user of this object is responsible for locking it. /// public RubyArray/*!*/ LoadPaths { get { return _loadPaths; } } /// - /// Thread safety: the user of this object is responsible for locking it. + /// TODO: Thread safety: the user of this object is responsible for locking it. /// - public List/*!*/ LoadedFiles { + public RubyArray/*!*/ LoadedFiles { get { return _loadedFiles; } } + private PlatformAdaptationLayer/*!*/ Platform { + get { return DomainManager.Platform; } + } + + private ScriptDomainManager/*!*/ DomainManager { + get { return _executionContext.Context.DomainManager; } + } + internal Loader(RubyExecutionContext/*!*/ context) { Assert.NotNull(context); _executionContext = context; @@ -84,163 +98,231 @@ /// /// Returns true if a Ruby file is successfully loaded, false if it is already loaded. /// - public bool LoadModule(CodeContext/*!*/ context, object self, string/*!*/ path, LoadFlags flags) { + public bool LoadFile(CodeContext/*!*/ context, object self, MutableString/*!*/ path, LoadFlags flags) { Assert.NotNull(context, path); - // relative => RubyOps.GetGlobalVariable(":"); - // ext .rb => source file - // ext .so, .o, .dll => lib - // other exts => add .rb, .so, .o, .dll - // add name of the loaded in RubyOps.GetGlobalVariable($") - // put source content to => SCRIPT_LINES__ + string assemblyName, typeName; - SourceUnit sourceUnit; + string strPath = path.ConvertToString(); + + if (TryParseAssemblyName(strPath, out typeName, out assemblyName)) { + + if (AlreadyLoaded(context, path, flags)) { + return false; + } + + if (LoadAssembly(assemblyName, typeName, false)) { + FileLoaded(path, flags); + return true; + } + } + + return LoadSourceFile(context, self, strPath, flags); + } + + public bool LoadAssembly(string/*!*/ assemblyName, string typeName, bool throwOnError) { Assembly assembly; - FileKind kind; - bool loaded; try { - loaded = InternalLoadModule(path, flags, out kind, out sourceUnit, out assembly); - if (!loaded) { + assembly = Platform.LoadAssembly(assemblyName); + } catch (Exception e) { + if (throwOnError) throw new LoadError(e.Message, e); + return false; + } + + Type initializerType; + if (typeName != null) { + // load Ruby library: + try { + initializerType = assembly.GetType(typeName); + } catch (Exception e) { + if (throwOnError) throw new LoadError(e.Message, e); return false; } - } catch (Exception e) { - throw new LoadError(e.Message, e); + + LoadLibrary(initializerType); + } else { + // load namespaces: + try { + DomainManager.LoadAssembly(assembly); + } catch (Exception e) { + if (throwOnError) throw new LoadError(e.Message, e); + return false; + } } - switch (kind) { - case FileKind.RubySourceFile: + return true; + } - RubyCompilerOptions options = new RubyCompilerOptions(); - options.IsIncluded = true; - options.IsWrapped = (flags & LoadFlags.LoadIsolated) != 0; - sourceUnit.Compile(options, _executionContext.RuntimeErrorSink).Run(context, false); - return true; + private static Regex _AssemblyNameRegex = new Regex(@" + \s*((?[\w.+]+)\s*,)?\s* # type name + (? + [^,=]+\s* # assembly name + (,\s*[\w]+\s*=\s*[^,]+\s*)+ # properties + )", + RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline + ); - // TODO: - //case FileKind.NonRubySourceFile: - // // other langauge's code: - // ScriptModule module = sfu.CompileToModule(); - // module.Execute(); + internal static bool TryParseAssemblyName(string/*!*/ path, out string typeName, out string assemblyName) { + Match match = _AssemblyNameRegex.Match(path); + if (match.Success) { + Group typeGroup = match.Groups["type"]; + Group assemblyGroup = match.Groups["assembly"]; + Debug.Assert(assemblyGroup.Success); - // // TODO: include + typeName = typeGroup.Success ? typeGroup.Value : null; + assemblyName = assemblyGroup.Value; + return true; + } - // return module; - - case FileKind.Assembly: - try { - // TODO: - bool result = context.LanguageContext.DomainManager.LoadAssembly(assembly); - return result; - } catch (Exception e) { - throw new LoadError(e.Message, e); - } + if (path.Trim() == "mscorlib") { + typeName = null; + assemblyName = path; + return true; } - throw Assert.Unreachable; + typeName = null; + assemblyName = null; + return false; } - private FileKind ProbeFileKind(string/*!*/ path, string/*!*/ extension, out string loadPath, out string resolvedPath) { - Assert.NotNull(path, extension); + private bool LoadSourceFile(CodeContext/*!*/ context, object self, string/*!*/ path, LoadFlags flags) { + Assert.NotNull(context, path); - resolvedPath = FindFile(loadPath = path + ".rb", false); - if (resolvedPath != null) - return FileKind.RubySourceFile; + SourceUnit sourceUnit = FindSourceUnit(context, path, (flags & LoadFlags.AppendExtensions) != 0); + if (sourceUnit == null) { + throw new LoadError(String.Format("no such file to load -- {0}", path)); + } - resolvedPath = FindFile(loadPath = path + ".dll", false); - if (resolvedPath != null) - return FileKind.Assembly; + MutableString pathWithExtension = MutableString.Create(path); + if (Path.GetExtension(path).Length == 0) { + pathWithExtension.Append(Path.GetExtension(sourceUnit.Path)); + } - loadPath = null; - return FileKind.Unknown; - } + if (AlreadyLoaded(context, pathWithExtension, flags)) { + return false; + } - private FileKind GetFileKind(string/*!*/ path, out string loadPath, out string resolvedPath) { - Assert.NotNull(path); + RubyContext rubySource = sourceUnit.LanguageContext as RubyContext; + if (rubySource != null) { + RubyCompilerOptions options = new RubyCompilerOptions(); + options.IsIncluded = true; + options.IsWrapped = (flags & LoadFlags.LoadIsolated) != 0; + sourceUnit.Compile(options, _executionContext.RuntimeErrorSink).Run(context, false); + } else { + // TODO: publish scope? + sourceUnit.Execute(); + } - string extension = Path.GetExtension(path).ToLower(); - switch(extension) { - case ".rb": - resolvedPath = FindFile(loadPath = path, true); - return FileKind.RubySourceFile; - case ".dll": - resolvedPath = FindFile(loadPath = path, true); - return FileKind.Assembly; - default: - return ProbeFileKind(path, extension, out loadPath, out resolvedPath); - } + AddLoadedFile(pathWithExtension); + return true; } - // Tries to load the module identified by path. Returns whether the module was actually loaded or not, while respecting LoadFlags. - // Throws LoadError exception if path cannot be resolved using load path. - private bool InternalLoadModule(string/*!*/ path, LoadFlags loadFlags, - out FileKind kind, out SourceUnit sourceUnit, out Assembly assembly) { + private SourceUnit FindSourceUnit(CodeContext/*!*/ context, string/*!*/ path, bool appendExtensions) { Assert.NotNull(path); + bool isAbsolutePath; + string extension; - sourceUnit = null; - assembly = null; - string resolvedPath, loadPath; - bool shouldLoadOnce = (loadFlags & LoadFlags.LoadOnce) == LoadFlags.LoadOnce; + try { + isAbsolutePath = Platform.IsAbsolutePath(path); + extension = Path.GetExtension(path); + } catch (ArgumentException e) { + throw new LoadError(e.Message, e); + } - // TODO: thread safety - the list needs to be locked! - List loadedFileList = _executionContext.Loader.LoadedFiles; + // absolute path -> LoadPaths not consulted: + if (isAbsolutePath) { + return ResolveSourceUnit(path, extension, appendExtensions); + } - kind = GetFileKind(path, out loadPath, out resolvedPath); - switch (kind) { - case FileKind.RubySourceFile: - // TODO: string compare (!), only MutableStrings are allowed in $" see Experimental\Globals\Require.rb - if (shouldLoadOnce && loadedFileList.Contains(loadPath)) - return false; - sourceUnit = TryGetSourceUnit(resolvedPath); - break; + foreach (object dir in GetLoadPaths()) { + if (dir == null) { + throw RubyExceptions.CreateTypeConversionError("nil", "String"); + } - case FileKind.Assembly: - if (shouldLoadOnce && loadedFileList.Contains(loadPath)) - return false; - assembly = _executionContext.Context.DomainManager.Platform.LoadAssemblyFromPath(resolvedPath); - break; + string strDir = _toStrSite.Invoke(context, dir).ConvertToString(); - case FileKind.Unknown: - sourceUnit = TryGetSourceUnit(path); - if (sourceUnit != null) { - kind = sourceUnit.LanguageContext.LanguageGuid == RubyContext.RubyLanguageGuid ? - FileKind.RubySourceFile : - FileKind.NonRubySourceFile; - assembly = null; - } else { - kind = FileKind.Assembly; - assembly = _executionContext.Context.DomainManager.Platform.LoadAssembly(path); - if (assembly == null) - throw new LoadError(String.Format("no such file to load -- {0}", path)); + try { + SourceUnit result = ResolveSourceUnit(Path.Combine(strDir, path), extension, appendExtensions); + if (result != null) { + return result; } - break; + } catch (ArgumentException) { + // invalid characters in path + } } - if (sourceUnit != null || assembly != null) { - loadedFileList.Add(loadPath); - return true; + return null; + } + + private SourceUnit ResolveSourceUnit(string/*!*/ path, string/*!*/ extension, bool appendExtensions) { + Debug.Assert(Path.GetExtension(path) == extension); + + // MRI doesn't load file w/o extension: + if (extension.Length > 0) { + if (Platform.FileExists(path)) { + return GetSourceUnit(path, extension); + } + } else if (appendExtensions) { + List matchingExtensions = GetExtensionsOfExistingFiles(path, DomainManager.GetRegisteredFileExtensions()); + + if (matchingExtensions.Count == 1) { + return GetSourceUnit(path + matchingExtensions[0], matchingExtensions[0]); + } else if (matchingExtensions.Count > 1) { + Exception e = new AmbiguousFileNameException(path + matchingExtensions[0], path + matchingExtensions[1]); + throw new LoadError(e.Message, e); + } } - return false; + return null; } - private SourceUnit/*!*/ TryGetSourceUnit(string/*!*/ path) { - return _executionContext.Context.DomainManager.Host.TryGetSourceFileUnit(_executionContext.Context, - path, StringUtils.DefaultEncoding, SourceCodeKind.File);// TODO: encoding + private SourceUnit GetSourceUnit(string/*!*/ path, string/*!*/ extension) { + Assert.NotNull(path, extension); + + LanguageContext language; + if (!DomainManager.TryGetLanguageContextByFileExtension(extension, out language)) { + // Ruby by default: + language = _executionContext.Context; + } + + // TODO: default encoding: + return DomainManager.Host.TryGetSourceFileUnit(language, path, StringUtils.DefaultEncoding, SourceCodeKind.File); } - internal string/*!*/[]/*!*/ GetLoadPaths() { - string[] result; + private List/*!*/ GetExtensionsOfExistingFiles(string/*!*/ path, IEnumerable/*!*/ extensions) { + // all extensions that could be appended to the path to get an sexisting file: + List result = new List(); + foreach (string extension in extensions) { + Debug.Assert(extension != null && extension.StartsWith(".")); + string fullPath = path + extension; + if (Platform.FileExists(fullPath)) { + result.Add(extension); + } + } + return result; + } + + #region Global Variables + + private readonly DynamicSite _toStrSite = DynamicSite.Create(ConvertToStrAction.Instance); + + internal object[]/*!*/ GetLoadPaths() { + lock (_loadedFiles) { + object[] result = new object[_loadPaths.Count]; + _loadPaths.CopyTo(result); + return result; + } + } + + public void SetLoadPaths(params string/*!*/[]/*!*/ paths) { + ContractUtils.RequiresNotNullItems(paths, "paths"); + lock (_loadPaths) { - result = new string[_loadPaths.Count]; - for (int i = 0; i < result.Length; i++) { - MutableString path = _loadPaths[i] as MutableString; - if (path == null) { - throw RubyExceptions.CreateTypeError(String.Format("Can't convert $:[{0}] into String", i)); - } - result[i] = path.ConvertToString(); + _loadPaths.Clear(); + foreach (string path in paths) { + _loadPaths.Add(MutableString.Create(path)); } } - return result; } internal void AddLoadPaths(IEnumerable/*!*/ paths) { @@ -253,48 +335,104 @@ } } - private string FindFile(string/*!*/ filePath, bool mustExist) { - if (_executionContext.Context.DomainManager.Platform.FileExists(filePath)) { - return filePath; + internal void AddLoadedFile(MutableString/*!*/ path) { + lock (_loadedFiles) { + _loadedFiles.Add(path); } + } - foreach (string path in GetLoadPaths()) { - string fullPath; + internal object[]/*!*/ GetLoadedFiles() { + lock (_loadedFiles) { + object[] result = new object[_loadedFiles.Count]; + _loadedFiles.CopyTo(result); + return result; + } + } - try { - fullPath = Path.Combine(path, filePath); + private bool AlreadyLoaded(CodeContext/*!*/ context, MutableString/*!*/ path, LoadFlags flags) { + return (flags & LoadFlags.LoadOnce) != 0 && IsFileLoaded(context, path); + } - if (_executionContext.Context.DomainManager.Platform.FileExists(fullPath)) { - return fullPath; - } + private void FileLoaded(MutableString/*!*/ path, LoadFlags flags) { + if ((flags & LoadFlags.LoadOnce) != 0) { + AddLoadedFile(path); + } + } - } catch (ArgumentException) { - // invalid characters in path + private bool IsFileLoaded(CodeContext/*!*/ context, MutableString/*!*/ path) { + foreach (object file in GetLoadedFiles()) { + if (file == null) { + throw RubyExceptions.CreateTypeConversionError("nil", "String"); } + + // case sensitive comparison: + if (path.Equals(_toStrSite.Invoke(context, file))) { + return true; + } } - return null; + return false; } - public static string/*!*/ GetIronRubyAssemblyName(string/*!*/ baseName) { - ContractUtils.RequiresNotNull(baseName, "baseName"); + #endregion + + #region IronRuby Libraries + + /// + internal bool TryLoadStandardRubyLibraries() { + Assembly assembly; + try { + assembly = _executionContext.Context.DomainManager.Platform.LoadAssembly(GetIronRubyAssemblyLongName("IronRuby.Libraries")); + } catch (FileNotFoundException) { + return false; + } catch (Exception e) { + throw new LoadError(e.Message, e); + } + + object[] attributes = assembly.GetCustomAttributes(typeof(RubyLibraryAttribute), false); + if (attributes.Length == 1) { + LoadLibrary(((RubyLibraryAttribute)attributes[0]).Initializer); + } else { + throw new LoadError(String.Format("Missing attribute {0} on {1}", typeof(RubyLibraryAttribute).FullName, assembly.FullName)); + } + + return true; + } + + public static string/*!*/ GetIronRubyAssemblyLongName(string/*!*/ simpleName) { + ContractUtils.RequiresNotNull(simpleName, "simpleName"); #if SIGNED - return baseName + ", Version=" + Assembly.GetExecutingAssembly().GetName().Version.ToString() + ", Culture=neutral, PublicKeyToken=31bf3856ad364e35"; + return simpleName + ", Version=" + Assembly.GetExecutingAssembly().GetName().Version.ToString() + ", Culture=neutral, PublicKeyToken=31bf3856ad364e35"; #else - return baseName; + return simpleName; #endif } - internal void LoadStandardRubyLibraries() { - Assembly assembly = _executionContext.Context.DomainManager.Platform.LoadAssembly(GetIronRubyAssemblyName("IronRuby.Libraries")); - object[] attributes = assembly.GetCustomAttributes(typeof(RubyLibraryAttribute), false); + /// + private void LoadLibrary(Type/*!*/ initializerType) { + LibraryInitializer initializer; + try { + initializer = Activator.CreateInstance(initializerType) as LibraryInitializer; + } catch (TargetInvocationException e) { + throw new LoadError(e.Message, e); + } catch (Exception e) { + throw new LoadError(e.Message, e); + } - if (attributes.Length == 1) { - LibraryInitializer initializer = (LibraryInitializer)Activator.CreateInstance(((RubyLibraryAttribute)attributes[0]).Initializer); + if (initializer == null) { + throw new LoadError(String.Format("Specified type {0} is not a subclass of {1}", + initializerType.FullName, + typeof(LibraryInitializer).FullName) + ); + } + + try { initializer.LoadModules(_executionContext); - } else { - throw new InvalidImplementationException("Cannot find RubyLibraryAttribute in library assembly"); + } catch (Exception e) { + throw new LoadError(e.Message, e); } } + + #endregion } } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyExceptions.cs;C435539 edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyExecutionContext.cs;C435539 File: RubyExecutionContext.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyExecutionContext.cs;C435539 (server) 5/9/2008 2:50 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyExecutionContext.cs;Loader9 @@ -27,6 +27,7 @@ using Microsoft.Scripting.Runtime; using System.Collections.ObjectModel; using System.Text; +using System.IO; namespace Ruby.Runtime { @@ -104,8 +105,7 @@ private RubyClass/*!*/ _trueClass; private RubyClass/*!*/ _falseClass; private RubyClass/*!*/ _exceptionClass; - private RubyClass/*!*/ _runtimeErrorClass; - private RubyClass/*!*/ _standardErrorClass; + private RubyClass _standardErrorClass; private Action/*!*/ _classSingletonTrait; private Action/*!*/ _singletonSingletonTrait; @@ -119,9 +119,8 @@ public RubyClass/*!*/ NilClass { get { return _nilClass; } set { _nilClass = value; } } public RubyClass/*!*/ TrueClass { get { return _trueClass; } set { _trueClass = value; } } public RubyClass/*!*/ FalseClass { get { return _falseClass; } set { _falseClass = value; } } - public RubyClass/*!*/ ExceptionClass { get { return _exceptionClass; } set { _exceptionClass = value; } } - public RubyClass/*!*/ RuntimeErrorClass { get { return _runtimeErrorClass; } set { _runtimeErrorClass = value; } } - public RubyClass/*!*/ StandardErrorClass { get { return _standardErrorClass; } set { _standardErrorClass = value; } } + public RubyClass ExceptionClass { get { return _exceptionClass; } set { _exceptionClass = value; } } + public RubyClass StandardErrorClass { get { return _standardErrorClass; } set { _standardErrorClass = value; } } internal Action/*!*/ ClassSingletonTrait { get { return _classSingletonTrait; } } @@ -237,11 +236,18 @@ _itemSeparator = null; _loader = new Loader(this); - _loader.LoadStandardRubyLibraries(); - Debug.Assert(_classClass != null && _moduleClass != null && _nilClass != null && _exceptionClass != null && - _runtimeErrorClass != null && _standardErrorClass != null); + if (!_loader.TryLoadStandardRubyLibraries()) { + Action empty = delegate(RubyModule/*!*/ module) { }; + RegisterPrimitives(empty, empty, empty, empty, empty, empty, empty, empty, empty, empty, empty); + } else { + // TODO: + Debug.Assert(_exceptionClass != null && _standardErrorClass != null && _nilClass != null); + } + Debug.Assert(_classClass != null && _moduleClass != null); + + // needs to run before globals and constants are initialized: InitializeFileDescriptors(); =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyOps.cs;C435539 File: RubyOps.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyOps.cs;C435539 (server) 5/9/2008 2:50 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyOps.cs;Loader9 @@ -903,6 +903,15 @@ return result; } + // emitted (ProtocolConversionAction): + public static MutableString/*!*/ ToStringValidator(string/*!*/ className, object obj) { + MutableString result = obj as MutableString; + if (result == null) { + throw new InvalidOperationException(String.Format("{0}#to_str should return String", className)); + } + return result; + } + // emitted (ProtocolConversionAction): public static Exception/*!*/ CreateTypeConversionError(string/*!*/ fromType, string/*!*/ toType) { return RubyExceptions.CreateTypeConversionError(fromType, toType); =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/ProtocolConversionAction.cs;C429806 File: ProtocolConversionAction.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/ProtocolConversionAction.cs;C429806 (server) 5/9/2008 2:50 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/ProtocolConversionAction.cs;Loader9 @@ -165,4 +165,24 @@ return other != null; } } + + public sealed class ConvertToStrAction : ProtocolConversionAction, IEquatable { + public static readonly ConvertToStrAction Instance = new ConvertToStrAction(); + + protected override SymbolId ToMethodName { get { return Symbols.ToStr; } } + protected override string/*!*/ TargetTypeName { get { return "String"; } } + protected override string/*!*/ ConversionResultValidator { get { return "ToStringValidator"; } } + + private ConvertToStrAction() + : base() { + } + + public static ConvertToStrAction/*!*/ Make() { + return Instance; + } + + public bool Equals(ConvertToStrAction other) { + return other != null; + } + } } =================================================================== edit: $/Dev10/feature/vs_langs01/ndp/fx/src/Core/Microsoft/Scripting/PlatformAdaptationLayer.cs;C418157 File: PlatformAdaptationLayer.cs =================================================================== --- $/Dev10/feature/vs_langs01/ndp/fx/src/Core/Microsoft/Scripting/PlatformAdaptationLayer.cs;C418157 (server) 5/9/2008 2:50 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/ndp/fx/src/Core/Microsoft/Scripting/PlatformAdaptationLayer.cs;Loader9 @@ -211,14 +211,35 @@ #endif } + /// Invalid path. public virtual string/*!*/ GetFullPath(string/*!*/ path) { #if !SILVERLIGHT - return Path.GetFullPath(path); + try { + return Path.GetFullPath(path); + } catch (Exception e) { + throw new ArgumentException("Specified path is invalid", "path", e); + } #else throw new NotImplementedException(); #endif } + /// Invalid path. + public virtual bool IsAbsolutePath(string/*!*/ path) { +#if !SILVERLIGHT + // GetPathRoot returns either : + // "" -> relative to the current dir + // "\" -> relative to the drive of the current dir + // "X:" -> relative to the current dir, possibly on a different drive + // "X:\" -> absolute + return + Environment.OSVersion.Platform != PlatformID.Unix && Path.GetPathRoot(path).EndsWith(@":\") || + Environment.OSVersion.Platform == PlatformID.Unix && Path.IsPathRooted(path); +#else + throw new NotImplementedException(); +#endif + } + public virtual string/*!*/ CurrentDirectory { get { #if !SILVERLIGHT ===================================================================