/// <summary> /// Gets a new ScriptFile instance which is identified by the given file /// path and initially contains the given buffer contents. /// </summary> /// <param name="filePath"></param> /// <param name="initialBuffer"></param> /// <returns></returns> public ScriptFile GetFileBuffer(string filePath, string initialBuffer) { Validate.IsNotNullOrWhitespaceString("filePath", filePath); if (IsNonFileUri(filePath)) { return(null); } // Resolve the full file path ResolvedFile resolvedFile = this.ResolveFilePath(filePath); string keyName = resolvedFile.LowercaseClientUri; // Make sure the file isn't already loaded into the workspace ScriptFile scriptFile = null; if (!this.workspaceFiles.TryGetValue(keyName, out scriptFile)) { scriptFile = new ScriptFile(resolvedFile.FilePath, resolvedFile.ClientUri, initialBuffer); this.workspaceFiles.Add(keyName, scriptFile); Logger.Write(TraceEventType.Verbose, "Opened file as in-memory buffer: " + resolvedFile.FilePath); } return(scriptFile); }
internal Assembly ResolveAssembly(string /*!*/ fullName) { Utils.Log(String.Format("Resolving assembly: '{0}'", fullName), "RESOLVE_ASSEMBLY"); AssemblyName assemblyName = new AssemblyName(fullName); ResolvedFile file = FindFile(assemblyName.Name, true, ArrayUtils.EmptyStrings).FirstOrDefault(); if (file == null || file.SourceUnit != null) { return(null); } Utils.Log(String.Format("Assembly '{0}' resolved: found in '{1}'", fullName, file.Path), "RESOLVE_ASSEMBLY"); try { Assembly assembly = Platform.LoadAssemblyFromPath(file.Path); #if !SILVERLIGHT // TODO: this API is broken if (AssemblyName.ReferenceMatchesDefinition(assemblyName, assembly.GetName())) #endif { Utils.Log(String.Format("Assembly '{0}' loaded for '{1}'", assembly.FullName, fullName), "RESOLVE_ASSEMBLY"); DomainManager.LoadAssembly(assembly); return(assembly); } } catch (Exception e) { throw RubyExceptions.CreateLoadError(e); } #if !SILVERLIGHT return(null); #endif }
/// <summary> /// Resolves a URI identifier into an actual file on disk if it exists. /// </summary> /// <param name="clientUri">The URI identifying the file</param> /// <returns></returns> private ResolvedFile ResolveFilePath(string clientUri) { bool canReadFromDisk = false; string filePath = clientUri; if (!IsPathInMemoryOrNonFileUri(clientUri)) { if (clientUri.StartsWith(@"file://")) { // VS Code encodes the ':' character in the drive name, which can lead to problems parsing // the URI, so unencode it if present. See https://github.com/Microsoft/vscode/issues/2990 clientUri = clientUri.Replace("%3A/", ":/", StringComparison.OrdinalIgnoreCase); // Client sent the path in URI format, extract the local path and trim // any extraneous slashes Uri fileUri = new Uri(clientUri); filePath = fileUri.LocalPath; if (filePath.StartsWith("//") || filePath.StartsWith("\\\\") || filePath.StartsWith("/")) { filePath = filePath.Substring(1); } } // Clients could specify paths with escaped space, [ and ] characters which .NET APIs // will not handle. These paths will get appropriately escaped just before being passed // into the SqlTools engine. filePath = UnescapePath(filePath); // Client paths are handled a bit differently because of how we currently identifiers in // ADS. The URI is passed around as an identifier - but for things we control like connecting // an editor the URI we pass in is NOT escaped fully. This is a problem for certain functionality // which is handled by VS Code - such as Intellise Completion - as the URI passed in there is // the fully escaped URI. That means we need to do some extra work to make sure that the URI values // are consistent. // So to solve that we'll make sure to unescape ALL uri's that are passed in and store that value for // use as an identifier (filePath will be the actual file path on disk). // # and ? are still always escaped though by ADS so we need to escape those again to get them to actually // match clientUri = Uri.UnescapeDataString(UnescapePath(clientUri)); clientUri = clientUri.Replace("#", "%23"); clientUri = clientUri.Replace("?", "%3F"); // switch to unix path separators on non-Windows platforms if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { filePath = filePath.Replace('\\', '/'); clientUri = clientUri.Replace('\\', '/'); } // Get the absolute file path ResolvedFile resolvedFile = FileUtilities.TryGetFullPath(filePath, clientUri); filePath = resolvedFile.FilePath; canReadFromDisk = resolvedFile.CanReadFromDisk; } Logger.Write(TraceEventType.Verbose, "Resolved path: " + clientUri); return(new ResolvedFile(filePath, clientUri, canReadFromDisk)); }
/// <summary> /// Searches file in load directories and then appends extensions. /// </summary> private ResolvedFile FindFile(string /*!*/ path, bool appendExtensions, string[] sourceFileExtensions) { Assert.NotNull(path); bool isAbsolutePath; #if SILVERLIGHT { #else if (path.StartsWith("~/", StringComparison.Ordinal) || path.StartsWith("~\\", StringComparison.Ordinal)) { path = RubyUtils.ExpandPath(_context.Platform, path); isAbsolutePath = true; } else { #endif try { isAbsolutePath = Platform.IsAbsolutePath(path); } catch (ArgumentException e) { throw RubyExceptions.CreateLoadError(e); } } string extension = RubyUtils.GetExtension(path); // Absolute path -> load paths not consulted. if (isAbsolutePath) { return(ResolveFile(path, extension, appendExtensions, sourceFileExtensions)); } string[] loadPaths = GetLoadPathStrings(); if (loadPaths.Length == 0) { return(null); } // If load paths are non-empty and the path starts with .\ or ..\ then MRI also ignores the load paths. if (path.StartsWith("./", StringComparison.Ordinal) || path.StartsWith("../", StringComparison.Ordinal) || path.StartsWith(".\\", StringComparison.Ordinal) || path.StartsWith("..\\", StringComparison.Ordinal)) { return(ResolveFile(path, extension, appendExtensions, sourceFileExtensions)); } foreach (var dir in loadPaths) { ResolvedFile result = ResolveFile(RubyUtils.CombinePaths(dir, path), extension, appendExtensions, sourceFileExtensions); if (result != null) { return(result); } } return(null); }
private bool LoadFromPath(Scope /*!*/ globalScope, object self, string /*!*/ path, LoadFlags flags) { Assert.NotNull(globalScope, path); ResolvedFile file = FindFile(globalScope, path, (flags & LoadFlags.AppendExtensions) != 0); if (file == null) { throw new LoadError(String.Format("no such file to load -- {0}", path)); } MutableString pathWithExtension = MutableString.Create(path); if (file.AppendedExtension != null) { pathWithExtension.Append(file.AppendedExtension); } if (AlreadyLoaded(pathWithExtension, flags) || _unfinishedFiles.Contains(pathWithExtension.ToString())) { return(false); } try { // save path as is, no canonicalization nor combination with an extension or directory: _unfinishedFiles.Push(pathWithExtension.ToString()); if (file.SourceUnit != null) { RubyContext rubySource = file.SourceUnit.LanguageContext as RubyContext; if (rubySource != null) { ExecuteRubySourceUnit(file.SourceUnit, globalScope, flags); } else { file.SourceUnit.Execute(); } } else { Debug.Assert(file.Path != null); try { Assembly asm = Platform.LoadAssemblyFromPath(Platform.GetFullPath(file.Path)); DomainManager.LoadAssembly(asm); } catch (Exception e) { throw new LoadError(e.Message, e); } } FileLoaded(pathWithExtension, flags); } finally { _unfinishedFiles.Pop(); } return(true); }
/// <summary> /// Checks if a given URI is contained in a workspace /// </summary> /// <param name="filePath"></param> /// <returns>Flag indicating if the file is tracked in workspace</returns> public bool ContainsFile(string filePath) { Validate.IsNotNullOrWhitespaceString("filePath", filePath); // Resolve the full file path ResolvedFile resolvedFile = this.ResolveFilePath(filePath); string keyName = resolvedFile.LowercaseClientUri; ScriptFile scriptFile = null; return(this.workspaceFiles.TryGetValue(keyName, out scriptFile)); }
private ResolvedFile ResolveFilePath(string filePath) { bool canReadFromDisk = false; if (!IsPathInMemoryOrNonFileUri(filePath)) { if (filePath.StartsWith(@"file://")) { // VS Code encodes the ':' character in the drive name, which can lead to problems parsing // the URI, so unencode it if present. See https://github.com/Microsoft/vscode/issues/2990 filePath = filePath.Replace("%3A/", ":/", StringComparison.OrdinalIgnoreCase); // Client sent the path in URI format, extract the local path and trim // any extraneous slashes Uri fileUri = new Uri(filePath); filePath = fileUri.LocalPath; if (filePath.StartsWith("//") || filePath.StartsWith("\\\\") || filePath.StartsWith("/")) { filePath = filePath.Substring(1); } } // Clients could specify paths with escaped space, [ and ] characters which .NET APIs // will not handle. These paths will get appropriately escaped just before being passed // into the SqlTools engine. filePath = UnescapePath(filePath); // switch to unix path separators on non-Windows platforms if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { filePath = filePath.Replace('\\', '/'); } // Get the absolute file path ResolvedFile resolvedFile = FileUtilities.TryGetFullPath(filePath); filePath = resolvedFile.FilePath; canReadFromDisk = resolvedFile.CanReadFromDisk; } Logger.Write(LogLevel.Verbose, "Resolved path: " + filePath); return(new ResolvedFile(filePath, canReadFromDisk)); }
/// <summary> /// Gets an open file in the workspace. If the file isn't open but /// exists on the filesystem, load and return it. Virtual method to /// allow for mocking /// </summary> /// <param name="filePath">The file path at which the script resides.</param> /// <exception cref="FileNotFoundException"> /// <paramref name="filePath"/> is not found. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="filePath"/> contains a null or empty string. /// </exception> public virtual ScriptFile GetFile(string filePath) { Validate.IsNotNullOrWhitespaceString("filePath", filePath); if (IsNonFileUri(filePath)) { return(null); } // Resolve the full file path ResolvedFile resolvedFile = this.ResolveFilePath(filePath); string keyName = resolvedFile.LowercaseClientUri; // Make sure the file isn't already loaded into the workspace ScriptFile scriptFile = null; if (!this.workspaceFiles.TryGetValue(keyName, out scriptFile)) { if (IsUntitled(resolvedFile.FilePath) || !resolvedFile.CanReadFromDisk || !File.Exists(resolvedFile.FilePath)) { // It's either not a registered untitled file, or not a valid file on disk // so any attempt to read from disk will fail. return(null); } // This method allows FileNotFoundException to bubble up // if the file isn't found. using (FileStream fileStream = new FileStream(resolvedFile.FilePath, FileMode.Open, FileAccess.Read)) using (StreamReader streamReader = new StreamReader(fileStream, Encoding.UTF8)) { scriptFile = new ScriptFile(resolvedFile.FilePath, resolvedFile.ClientUri, streamReader); this.workspaceFiles.Add(keyName, scriptFile); } Logger.Write(TraceEventType.Verbose, "Opened file on disk: " + resolvedFile.FilePath); } return(scriptFile); }
private ResolvedFile ResolveFilePath(string filePath) { bool canReadFromDisk = false; if (!IsPathInMemoryOrNonFileUri(filePath)) { if (filePath.StartsWith(@"file://")) { // Client sent the path in URI format, extract the local path and trim // any extraneous slashes Uri fileUri = new Uri(filePath); filePath = fileUri.LocalPath; if (filePath.StartsWith("//") || filePath.StartsWith("\\\\") || filePath.StartsWith("/")) { filePath = filePath.Substring(1); } } // Clients could specify paths with escaped space, [ and ] characters which .NET APIs // will not handle. These paths will get appropriately escaped just before being passed // into the SqlTools engine. filePath = UnescapePath(filePath); // switch to unix path separators on non-Windows platforms if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { filePath = filePath.Replace('\\', '/'); } // Get the absolute file path ResolvedFile resolvedFile = FileUtilities.TryGetFullPath(filePath); filePath = resolvedFile.FilePath; canReadFromDisk = resolvedFile.CanReadFromDisk; } Logger.Write(LogLevel.Verbose, "Resolved path: " + filePath); return(new ResolvedFile(filePath, canReadFromDisk)); }
private bool LoadFromPath(Scope globalScope, object self, string /*!*/ path, RubyEncoding /*!*/ pathEncoding, LoadFlags flags, out object loaded) { Assert.NotNull(pathEncoding, path); string[] sourceFileExtensions; if ((flags & LoadFlags.AnyLanguage) != 0) { sourceFileExtensions = DomainManager.Configuration.GetFileExtensions(); } else { sourceFileExtensions = DomainManager.Configuration.GetFileExtensions(_context); } IList <ResolvedFile> files = FindFile(path, (flags & LoadFlags.AppendExtensions) != 0, sourceFileExtensions); if (files.Count == 0) { // MRI: doesn't throw an exception if the path is in $" (performs resolution first though): if (AlreadyLoaded(path, null, flags, sourceFileExtensions)) { loaded = null; return(false); } throw RubyExceptions.CreateLoadError(String.Format("no such file to load -- {0}", path)); } ResolvedFile file = files.First(); string pathWithExtension = path; if (file.AppendedExtension != null) { pathWithExtension += file.AppendedExtension; } if (AlreadyLoaded(path, files, flags) || _unfinishedFiles.Contains(file.Path)) { if ((flags & LoadFlags.ResolveLoaded) != 0) { if (file.SourceUnit != null) { Scope loadedScope; if (!LoadedScripts.TryGetValue(file.Path, out loadedScope)) { throw RubyExceptions.CreateLoadError(String.Format("no such file to load -- {0}", file.Path)); } loaded = loadedScope; } else { loaded = Platform.LoadAssemblyFromPath(file.Path); } } else { loaded = null; } return(false); } try { // save path as is, no canonicalization nor combination with an extension or directory: _unfinishedFiles.Push(file.Path); if (file.SourceUnit != null) { AddScriptLines(file.SourceUnit); ScriptCode compiledCode; if (file.SourceUnit.LanguageContext == _context) { compiledCode = CompileRubySource(file.SourceUnit, flags); } else { compiledCode = file.SourceUnit.Compile(); } loaded = Execute(globalScope, compiledCode); } else { Debug.Assert(file.Path != null); try { Assembly assembly = Platform.LoadAssemblyFromPath(file.Path); DomainManager.LoadAssembly(assembly); loaded = assembly; } catch (Exception e) { throw RubyExceptions.CreateLoadError(e); } } FileLoaded(MutableString.Create(file.Path, pathEncoding), flags); } finally { _unfinishedFiles.Pop(); } return(true); }
private ResolvedFile FindFile(string /*!*/ path, bool appendExtensions, string[] sourceFileExtensions) { Assert.NotNull(path); bool isAbsolutePath; string extension; string home = null; #if !SILVERLIGHT if (path.StartsWith("~/", StringComparison.Ordinal) || path.StartsWith("~\\", StringComparison.Ordinal)) { try { home = Environment.GetEnvironmentVariable("HOME"); } catch (SecurityException) { home = null; } if (home == null) { throw RubyExceptions.CreateArgumentError(String.Format("couldn't find HOME environment -- expanding `{0}'", path)); } } #endif try { if (home != null) { path = RubyUtils.CombinePaths(home, path.Substring(2)); } isAbsolutePath = Platform.IsAbsolutePath(path); extension = RubyUtils.GetExtension(path); } catch (ArgumentException e) { throw RubyExceptions.CreateLoadError(e); } // Absolute path -> load paths not consulted. if (isAbsolutePath) { return(ResolveFile(path, extension, appendExtensions, sourceFileExtensions)); } string[] loadPaths = GetLoadPathStrings(); if (loadPaths.Length == 0) { return(null); } // If load paths are non-empty and the path starts with .\ or ..\ then MRI also ignores the load paths. if (path.StartsWith("./", StringComparison.Ordinal) || path.StartsWith("../", StringComparison.Ordinal) || path.StartsWith(".\\", StringComparison.Ordinal) || path.StartsWith("..\\", StringComparison.Ordinal)) { return(ResolveFile(path, extension, appendExtensions, sourceFileExtensions)); } foreach (var dir in loadPaths) { try { ResolvedFile result = ResolveFile(RubyUtils.CombinePaths(dir, path), extension, appendExtensions, sourceFileExtensions); if (result != null) { return(result); } } catch (ArgumentException) { // invalid characters in path } } return(null); }
private ResolvedFile FindFile(Scope /*!*/ globalScope, string /*!*/ path, bool appendExtensions) { Assert.NotNull(path); bool isAbsolutePath; string extension; string home = null; #if !SILVERLIGHT if (path.StartsWith("~/") || path.StartsWith("~\\")) { try { home = Environment.GetEnvironmentVariable("HOME"); } catch (SecurityException) { home = null; } if (home == null) { throw RubyExceptions.CreateArgumentError(String.Format("couldn't find HOME environment -- expanding `{0}'", path)); } } #endif try { if (home != null) { path = Path.Combine(home, path.Substring(2)); } isAbsolutePath = Platform.IsAbsolutePath(path); extension = Path.GetExtension(path); } catch (ArgumentException e) { throw new LoadError(e.Message, e); } string[] knownExtensions = DomainManager.Configuration.GetFileExtensions(); Array.Sort(knownExtensions, DlrConfiguration.FileExtensionComparer); // Absolute path -> load paths not consulted. if (isAbsolutePath) { return(ResolveFile(path, extension, appendExtensions, knownExtensions)); } string[] loadPaths = GetLoadPathStrings(); if (loadPaths.Length == 0) { return(null); } // If load paths are non-empty and the path starts with .\ or ..\ then MRI also ignores the load paths. if (path.StartsWith("./") || path.StartsWith("../") || path.StartsWith(".\\") || path.StartsWith("..\\")) { return(ResolveFile(path, extension, appendExtensions, knownExtensions)); } foreach (var dir in loadPaths) { try { ResolvedFile result = ResolveFile(Path.Combine(dir, path), extension, appendExtensions, knownExtensions); if (result != null) { return(result); } } catch (ArgumentException) { // invalid characters in path } } return(null); }