From c163c06a6543ce98d9e5efa1abc35a4252bab2f9 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Tue, 15 Apr 2014 21:13:10 +0200 Subject: [PATCH 01/17] Avoids idetools crash on nil parameters. --- compiler/suggest.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 49611f6492..fc6ba2f77d 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -253,6 +253,7 @@ proc findUsages(node: PNode, s: PSym) = lastLineInfo = node.info proc findDefinition(node: PNode, s: PSym) = + if node.isNil or s.isNil: return if isTracked(node.info, s.name.s.len): suggestWriteln(symToStr(s, isLocal=false, sectionDef)) suggestQuit() From 37ac424e9a5a1564055f99ffcc0fa6ff4321d5f4 Mon Sep 17 00:00:00 2001 From: Clay Sweetser Date: Fri, 11 Apr 2014 12:33:53 -0400 Subject: [PATCH 02/17] Added Windows implementation of getFileInfo procedures --- lib/pure/os.nim | 123 +++++++++++++++++++++++++++++++++------- lib/windows/winlean.nim | 1 + 2 files changed, 104 insertions(+), 20 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index faca17e980..11c84570fa 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -448,6 +448,8 @@ proc getLastAccessTime*(file: string): TTime {.rtl, extern: "nos$1".} = proc getCreationTime*(file: string): TTime {.rtl, extern: "nos$1".} = ## Returns the `file`'s creation time. + ## Note that under posix OS's, the returned time may actually be the time at + ## which the file's attribute's were last modified. when defined(posix): var res: TStat if stat(file, res) < 0'i32: osError(osLastError()) @@ -777,6 +779,25 @@ proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} = elif defined(posix): result = path[0] == '/' +when defined(Windows): + proc openHandle(path: string, followSymlink=true): THandle = + var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL + if not followSymlink: + flags = flags or FILE_FLAG_OPEN_REPARSE_POINT + + when useWinUnicode: + result = createFileW( + newWideCString(path), 0'i32, + FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, + nil, OPEN_EXISTING, flags, 0 + ) + else: + result = createFileA( + path, 0'i32, + FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, + nil, OPEN_EXISTING, flags, 0 + ) + proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1", tags: [FReadDir].} = ## Returns True if both pathname arguments refer to the same physical @@ -787,26 +808,8 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1", ## sym-linked paths to the same file or directory. when defined(Windows): var success = true - - when useWinUnicode: - var p1 = newWideCString(path1) - var p2 = newWideCString(path2) - template openHandle(path: expr): expr = - createFileW(path, 0'i32, FILE_SHARE_DELETE or FILE_SHARE_READ or - FILE_SHARE_WRITE, nil, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL, 0) - - var f1 = openHandle(p1) - var f2 = openHandle(p2) - - else: - template openHandle(path: expr): expr = - createFileA(path, 0'i32, FILE_SHARE_DELETE or FILE_SHARE_READ or - FILE_SHARE_WRITE, nil, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL, 0) - - var f1 = openHandle(path1) - var f2 = openHandle(path2) + var f1 = openHandle(path1) + var f2 = openHandle(path2) var lastErr: TOSErrorCode if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE: @@ -1747,4 +1750,84 @@ proc expandTilde*(path: string): string = else: result = path +when defined(Windows): + type + DeviceId = int32 + FileId = int64 +type + FileInfo = object + ## Contains information associated with a file object. + id: tuple[device: DeviceId, file: FileId] # Device and file id. + kind: TPathComponent # Kind of file object - directory, symlink, etc. + size: BiggestInt # Size of file. + permissions: set[TFilePermission] # File permissions + linkCount: BiggestInt # Number of hard links the file object has. + lastAccessTime: TTime # Time file was last accessed. + lastWriteTime: TTime # Time file was last modified/written to. + creationTime: TTime # Time file was created. Not supported on all systems! + + +proc getFileInfo*(handle: THandle, result: var FileInfo) = + ## Retrieves file information for the file object represented by the given + ## handle. + ## + ## If the information cannot be retrieved, such as when the file handle + ## is invalid, an error will be thrown. + # Done: ID, Kind, Size, Permissions, Link Count + when defined(Windows): + template toTime(e): expr = winTimeToUnixTime(rdFileTime(e)) + var info: TBY_HANDLE_FILE_INFORMATION + if getFileInformationByHandle(handle, addr info) == 0: + osError(osLastError()) + result.id.device = info.dwVolumeSerialNumber + result.id.file = info.nFileIndexLow or (info.nFileIndexHigh shl 32) + result.size = info.nFileSizeLow or (info.nFileSizeHigh shl 32) + result.linkCount = info.nNumberOfLinks + result.lastAccessTime = toTime(info.ftLastAccessTime) + result.lastWriteTime = toTime(info.ftLastWriteTime) + result.creationTime = toTime(info.ftCreationTime) + + # Retrieve permissions + # TODO - Use a more accurate method of getting permissions? + if (info.dwFileAttributes and FILE_ATTRIBUTE_READONLY) != 0'i32: + result.permissions = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead, + fpOthersExec, fpOthersRead} + else: + result.permissions = {fpUserExec..fpOthersRead} + + # Retrieve file kind + # Should we include more file kinds? + result.kind = pcFile + if (info.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32: + result.kind = pcDir + # Should we optimize for the case when it's definately known that the file + # isn't a symlink (see the below procedure's "followSymlink") + if (info.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32: + result.kind = succ(result.kind) + + else: + var rawInfo: TStat + if stat(handle, rawInfo) < 0'i32: osError(osLastError()) + +proc getFileInfo*(path: string, followSymlink = true): FileInfo = + ## Retrieves file information for the file object pointed to by `path`. + ## + ## Due to intrinsic differences between operating systems, the information + ## contained by the returned `FileInfo` structure will be slightly different + ## across platforms, and in some cases, incomplete or inaccurate. + ## + ## When `followSymlink` is true, symlinks are followed and the information + ## retrieved is information related to the symlink's target. Otherwise, + ## information on the symlink itself is retrieved. + ## + ## If the information cannot be retrieved, such as when the path doesn't + ## exist, or when permission restrictions prevent the program from retrieving + ## file information, an error will be thrown. + when defined(Windows): + var handle = openHandle(path, followSymlink) + if handle == INVALID_HANDLE_VALUE: + osError(osLastError()) + getFileInfo(handle, result) + closeHandle(handle) + {.pop.} diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 69a3c5c811..51b6021ae1 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -581,6 +581,7 @@ const INVALID_FILE_SIZE* = -1'i32 FILE_FLAG_BACKUP_SEMANTICS* = 33554432'i32 + FILE_FLAG_OPEN_REPARSE_POINT* = 0x00200000'i32 # Error Constants const From 8af083c847c4f4b233f165d63a1e0994301196ef Mon Sep 17 00:00:00 2001 From: Clay Sweetser Date: Sat, 12 Apr 2014 23:05:40 -0400 Subject: [PATCH 03/17] Added Posix implementation of getFileInfo, organized code. --- lib/pure/os.nim | 97 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 66 insertions(+), 31 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 11c84570fa..25f3b940e0 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1754,6 +1754,11 @@ when defined(Windows): type DeviceId = int32 FileId = int64 +else: + type + DeviceId = TDev + FileId = TIno + type FileInfo = object ## Contains information associated with a file object. @@ -1766,6 +1771,58 @@ type lastWriteTime: TTime # Time file was last modified/written to. creationTime: TTime # Time file was created. Not supported on all systems! +proc rawToFormalFileInfo(rawInfo, formalInfo): expr = + ## 'rawInfo' is either a 'TBY_HANDLE_FILE_INFORMATION' structure on Windows, + ## or a 'TStat' structure on posix + when defined(Windows): + template toTime(e): expr = winTimeToUnixTime(rdFileTime(e)) + template merge(a, b): expr = a or (b shl 32) + formalInfo.id.device = rawInfo.dwVolumeSerialNumber + formalInfo.id.file = merge(rawInfo.nFileIndexLow, rawInfo.nFileIndexHigh) + formalInfo.size = merge(rawInfo.nFileSizeLow, rawInfo.nFileSizeHigh) + formalInfo.linkCount = rawInfo.nNumberOfLinks + formalInfo.lastAccessTime = toTime(rawInfo.ftLastAccessTime) + formalInfo.lastWriteTime = toTime(rawInfo.ftLastWriteTime) + formalInfo.creationTime = toTime(rawInfo.ftCreationTime) + + # Retrieve basic permissions + if (info.dwFileAttributes and FILE_ATTRIBUTE_READONLY) != 0'i32: + formalInfo.permissions = {fpUserExec, fpUserRead, fpGroupExec, + fpGroupRead, fpOthersExec, fpOthersRead} + else: + result.permissions = {fpUserExec..fpOthersRead} + + # Retrieve basic file kind + result.kind = pcFile + if (info.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32: + formalInfo.kind = pcDir + if (info.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32: + formalInfo.kind = succ(result.kind) + + else: + formalInfo.id = (rawInfo.st_dev, rawInfo.st_ino) + formalInfo.size = getFileSize(handle) + formalInfo.linkCount = BiggestInt(rawInfo.st_Nlink) + formalInfo.lastAccessTime = rawInfo.st_atime + formalInfo.lastWriteTime = rawInfo.st_mtime + formalInfo.creationTime = rawInfo.st_ctime + + result.permissions = {} + if (a.st_mode and S_IRUSR) != 0'i32: result.incl(fpUserRead) + if (a.st_mode and S_IWUSR) != 0'i32: result.incl(fpUserWrite) + if (a.st_mode and S_IXUSR) != 0'i32: result.incl(fpUserExec) + + if (a.st_mode and S_IRGRP) != 0'i32: result.incl(fpGroupRead) + if (a.st_mode and S_IWGRP) != 0'i32: result.incl(fpGroupWrite) + if (a.st_mode and S_IXGRP) != 0'i32: result.incl(fpGroupExec) + + if (a.st_mode and S_IROTH) != 0'i32: result.incl(fpOthersRead) + if (a.st_mode and S_IWOTH) != 0'i32: result.incl(fpOthersWrite) + if (a.st_mode and S_IXOTH) != 0'i32: result.incl(fpOthersExec) + + result.kind = pcFile + if S_ISDIR(s.st_mode): result.kind = pcDir + if S_ISLNK(s.st_mode): succ(result.kind) proc getFileInfo*(handle: THandle, result: var FileInfo) = ## Retrieves file information for the file object represented by the given @@ -1775,39 +1832,15 @@ proc getFileInfo*(handle: THandle, result: var FileInfo) = ## is invalid, an error will be thrown. # Done: ID, Kind, Size, Permissions, Link Count when defined(Windows): - template toTime(e): expr = winTimeToUnixTime(rdFileTime(e)) - var info: TBY_HANDLE_FILE_INFORMATION - if getFileInformationByHandle(handle, addr info) == 0: + var rawInfo: TBY_HANDLE_FILE_INFORMATION + if getFileInformationByHandle(handle, addr rawInfo) == 0: osError(osLastError()) - result.id.device = info.dwVolumeSerialNumber - result.id.file = info.nFileIndexLow or (info.nFileIndexHigh shl 32) - result.size = info.nFileSizeLow or (info.nFileSizeHigh shl 32) - result.linkCount = info.nNumberOfLinks - result.lastAccessTime = toTime(info.ftLastAccessTime) - result.lastWriteTime = toTime(info.ftLastWriteTime) - result.creationTime = toTime(info.ftCreationTime) - - # Retrieve permissions - # TODO - Use a more accurate method of getting permissions? - if (info.dwFileAttributes and FILE_ATTRIBUTE_READONLY) != 0'i32: - result.permissions = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead, - fpOthersExec, fpOthersRead} - else: - result.permissions = {fpUserExec..fpOthersRead} - - # Retrieve file kind - # Should we include more file kinds? - result.kind = pcFile - if (info.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32: - result.kind = pcDir - # Should we optimize for the case when it's definately known that the file - # isn't a symlink (see the below procedure's "followSymlink") - if (info.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32: - result.kind = succ(result.kind) - + rawToFormalFileInfo(rawInfo, result) else: var rawInfo: TStat - if stat(handle, rawInfo) < 0'i32: osError(osLastError()) + if fstat(handle, rawInfo) < 0'i32: + osError(osLastError()) + rawToFormalFileInfo(rawInfo, result) proc getFileInfo*(path: string, followSymlink = true): FileInfo = ## Retrieves file information for the file object pointed to by `path`. @@ -1828,6 +1861,8 @@ proc getFileInfo*(path: string, followSymlink = true): FileInfo = if handle == INVALID_HANDLE_VALUE: osError(osLastError()) getFileInfo(handle, result) - closeHandle(handle) + discard closeHandle(handle) + else: + var {.pop.} From 79f0c66b4de25d5b75d729106f7b6f80835ca0ac Mon Sep 17 00:00:00 2001 From: Clay Sweetser Date: Sun, 13 Apr 2014 19:03:42 -0400 Subject: [PATCH 04/17] Completed Linux/Posix implementation of getFileInfo Moved parts of getFileInfo into a helper template. --- lib/pure/os.nim | 64 ++++++++++++++++++++++++++--------------- lib/windows/winlean.nim | 3 ++ 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 25f3b940e0..bfc63dfc66 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1771,7 +1771,8 @@ type lastWriteTime: TTime # Time file was last modified/written to. creationTime: TTime # Time file was created. Not supported on all systems! -proc rawToFormalFileInfo(rawInfo, formalInfo): expr = +template rawToFormalFileInfo(rawInfo, formalInfo): expr = + ## Transforms the native file info structure into the one nimrod uses. ## 'rawInfo' is either a 'TBY_HANDLE_FILE_INFORMATION' structure on Windows, ## or a 'TStat' structure on posix when defined(Windows): @@ -1786,7 +1787,7 @@ proc rawToFormalFileInfo(rawInfo, formalInfo): expr = formalInfo.creationTime = toTime(rawInfo.ftCreationTime) # Retrieve basic permissions - if (info.dwFileAttributes and FILE_ATTRIBUTE_READONLY) != 0'i32: + if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_READONLY) != 0'i32: formalInfo.permissions = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead, fpOthersExec, fpOthersRead} else: @@ -1794,37 +1795,40 @@ proc rawToFormalFileInfo(rawInfo, formalInfo): expr = # Retrieve basic file kind result.kind = pcFile - if (info.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32: + if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32: formalInfo.kind = pcDir - if (info.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32: + if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32: formalInfo.kind = succ(result.kind) else: + template checkAndIncludeMode(rawMode, formalMode: expr) = + if (rawInfo.st_mode and rawMode) != 0'i32: + formalInfo.permissions.incl(formalMode) formalInfo.id = (rawInfo.st_dev, rawInfo.st_ino) - formalInfo.size = getFileSize(handle) - formalInfo.linkCount = BiggestInt(rawInfo.st_Nlink) + formalInfo.size = rawInfo.st_size + formalInfo.linkCount = rawInfo.st_Nlink formalInfo.lastAccessTime = rawInfo.st_atime formalInfo.lastWriteTime = rawInfo.st_mtime formalInfo.creationTime = rawInfo.st_ctime result.permissions = {} - if (a.st_mode and S_IRUSR) != 0'i32: result.incl(fpUserRead) - if (a.st_mode and S_IWUSR) != 0'i32: result.incl(fpUserWrite) - if (a.st_mode and S_IXUSR) != 0'i32: result.incl(fpUserExec) + checkAndIncludeMode(S_IRUSR, fpUserRead) + checkAndIncludeMode(S_IWUSR, fpUserWrite) + checkAndIncludeMode(S_IXUSR, fpUserExec) - if (a.st_mode and S_IRGRP) != 0'i32: result.incl(fpGroupRead) - if (a.st_mode and S_IWGRP) != 0'i32: result.incl(fpGroupWrite) - if (a.st_mode and S_IXGRP) != 0'i32: result.incl(fpGroupExec) + checkAndIncludeMode(S_IRGRP, fpGroupRead) + checkAndIncludeMode(S_IWGRP, fpGroupWrite) + checkAndIncludeMode(S_IXGRP, fpGroupExec) - if (a.st_mode and S_IROTH) != 0'i32: result.incl(fpOthersRead) - if (a.st_mode and S_IWOTH) != 0'i32: result.incl(fpOthersWrite) - if (a.st_mode and S_IXOTH) != 0'i32: result.incl(fpOthersExec) + checkAndIncludeMode(S_IROTH, fpOthersRead) + checkAndIncludeMode(S_IWOTH, fpOthersWrite) + checkAndIncludeMode(S_IXOTH, fpOthersExec) - result.kind = pcFile - if S_ISDIR(s.st_mode): result.kind = pcDir - if S_ISLNK(s.st_mode): succ(result.kind) + formalInfo.kind = pcFile + if S_ISDIR(rawInfo.st_mode): formalInfo.kind = pcDir + if S_ISLNK(rawInfo.st_mode): formalInfo.kind.inc() -proc getFileInfo*(handle: THandle, result: var FileInfo) = +proc getFileInfo*(handle: TFileHandle, result: var FileInfo) = ## Retrieves file information for the file object represented by the given ## handle. ## @@ -1833,7 +1837,10 @@ proc getFileInfo*(handle: THandle, result: var FileInfo) = # Done: ID, Kind, Size, Permissions, Link Count when defined(Windows): var rawInfo: TBY_HANDLE_FILE_INFORMATION - if getFileInformationByHandle(handle, addr rawInfo) == 0: + # We have to use the super special '_get_osfhandle' call (wrapped above) + # To transform the C file descripter to a native file handle. + var realHandle = get_osfhandle(handle) + if getFileInformationByHandle(realHandle, addr rawInfo) == 0: osError(osLastError()) rawToFormalFileInfo(rawInfo, result) else: @@ -1857,12 +1864,23 @@ proc getFileInfo*(path: string, followSymlink = true): FileInfo = ## exist, or when permission restrictions prevent the program from retrieving ## file information, an error will be thrown. when defined(Windows): - var handle = openHandle(path, followSymlink) + var + handle = openHandle(path, followSymlink) + rawInfo: TBY_HANDLE_FILE_INFORMATION if handle == INVALID_HANDLE_VALUE: osError(osLastError()) - getFileInfo(handle, result) + if getFileInformationByHandle(handle, addr rawInfo) == 0: + osError(osLastError()) + rawToFormalFileInfo(rawInfo, result) discard closeHandle(handle) else: - var + var rawInfo: TStat + if followSymlink: + if lstat(path, rawInfo) < 0'i32: + osError(osLastError()) + else: + if stat(path, rawInfo) < 0'i32: + osError(osLastError()) + rawToFormalFileInfo(rawInfo, result) {.pop.} diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 51b6021ae1..683f8cc230 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -716,3 +716,6 @@ proc WSASend*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD, bytesSent: PDWord, flags: DWORD, lpOverlapped: POverlapped, completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {. stdcall, importc: "WSASend", dynlib: "Ws2_32.dll".} + +proc get_osfhandle*(fd:TFileHandle): THandle {. + importc:"__get_osfhandle", header:"".} \ No newline at end of file From 73570cbe38e46362210ab829100f13429c90eef0 Mon Sep 17 00:00:00 2001 From: Clay Sweetser Date: Tue, 15 Apr 2014 02:28:50 -0400 Subject: [PATCH 05/17] Allowed getFileInfo to accept TFile objects. --- lib/pure/os.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index bfc63dfc66..30a5ae9492 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1828,7 +1828,7 @@ template rawToFormalFileInfo(rawInfo, formalInfo): expr = if S_ISDIR(rawInfo.st_mode): formalInfo.kind = pcDir if S_ISLNK(rawInfo.st_mode): formalInfo.kind.inc() -proc getFileInfo*(handle: TFileHandle, result: var FileInfo) = +proc getFileInfo*(handle: TFileHandle): FileInfo = ## Retrieves file information for the file object represented by the given ## handle. ## @@ -1849,6 +1849,9 @@ proc getFileInfo*(handle: TFileHandle, result: var FileInfo) = osError(osLastError()) rawToFormalFileInfo(rawInfo, result) +proc getFileInfo*(file: TFile): FileInfo = + result = getFileInfo(file.fileHandle()) + proc getFileInfo*(path: string, followSymlink = true): FileInfo = ## Retrieves file information for the file object pointed to by `path`. ## From 733b289209ef4c665d158a5fd69a483920c71272 Mon Sep 17 00:00:00 2001 From: Clay Sweetser Date: Tue, 15 Apr 2014 02:29:37 -0400 Subject: [PATCH 06/17] Fixed _get_osfhandle declaration. --- lib/windows/winlean.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 683f8cc230..3449a3eba1 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -718,4 +718,4 @@ proc WSASend*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD, stdcall, importc: "WSASend", dynlib: "Ws2_32.dll".} proc get_osfhandle*(fd:TFileHandle): THandle {. - importc:"__get_osfhandle", header:"".} \ No newline at end of file + importc:"_get_osfhandle", header:"".} \ No newline at end of file From 9ce0ac38e5816910730843f4ef0c565e3c839ad0 Mon Sep 17 00:00:00 2001 From: Clay Sweetser Date: Tue, 15 Apr 2014 02:30:15 -0400 Subject: [PATCH 07/17] Added tests for getFileInfo proc. --- tests/stdlib/tgetfileinfo.nim | 93 +++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 tests/stdlib/tgetfileinfo.nim diff --git a/tests/stdlib/tgetfileinfo.nim b/tests/stdlib/tgetfileinfo.nim new file mode 100644 index 0000000000..49a0190619 --- /dev/null +++ b/tests/stdlib/tgetfileinfo.nim @@ -0,0 +1,93 @@ +discard """ + output: "" +""" + +import os, strutils +# Cases +# 1 - String : Existing File : Symlink true +# 2 - String : Existing File : Symlink false +# 3 - String : Non-existing File : Symlink true +# 4 - String : Non-existing File : Symlink false +# 5 - Handle : Valid File +# 6 - Handle : Invalid File +# 7 - Handle : Valid Handle +# 8 - Handle : Invalid Handle + +proc genBadFileName(limit = 100): string = + ## Generates a filename of a nonexistant file. + ## Returns "" if generation fails. + result = "a" + var hitLimit = true + + for i in 0..100: + if existsFile(result): + result.add("a") + else: + hitLimit = false + break + if hitLimit: + result = "" + +proc caseOneAndTwo(followLink: bool) = + try: + discard getFileInfo(getAppFilename(), followLink) + #echo("String : Existing File : Symlink $# : Success" % $followLink) + except EOS: + echo("String : Existing File : Symlink $# : Failure" % $followLink) + +proc caseThreeAndFour(followLink: bool) = + var invalidName = genBadFileName() + try: + discard getFileInfo(invalidName, true) + echo("String : Non-existing File : Symlink $# : Failure" % $followLink) + except EOS: + #echo("String : Non-existing File : Symlink $# : Success" % $followLink) + +proc testGetFileInfo = + # Case 1 + caseOneAndTwo(true) + + # Case 2 + caseOneAndTwo(false) + + # Case 3 + caseThreeAndFour(true) + + # Case 4 + caseThreeAndFour(false) + + # Case 5 and 7 + block: + let + testFile = open(getAppFilename()) + testHandle = fileHandle(testFile) + try: + discard getFileInfo(testFile) + #echo("Handle : Valid File : Success") + except EIO: + echo("Handle : Valid File : Failure") + + try: + discard getFileInfo(testHandle) + #echo("Handle : Valid File : Success") + except EIO: + echo("Handle : Valid File : Failure") + + # Case 6 and 8 + block: + let + testFile: TFile = nil + testHandle = TFileHandle(-1) + try: + discard getFileInfo(testFile) + echo("Handle : Invalid File : Failure") + except EIO, EOS: + #echo("Handle : Invalid File : Success") + + try: + discard getFileInfo(testHandle) + echo("Handle : Invalid File : Failure") + except EIO, EOS: + #echo("Handle : Invalid File : Success") + +testGetFileInfo() \ No newline at end of file From e2fe9dd9abb9ce258285c6f3717f5089fb64bbb9 Mon Sep 17 00:00:00 2001 From: ReneSac Date: Thu, 17 Apr 2014 21:28:08 -0300 Subject: [PATCH 08/17] Additions and clarifications to tutorial 1. --- doc/tut1.txt | 67 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 17 deletions(-) diff --git a/doc/tut1.txt b/doc/tut1.txt index 5a20629a2c..8d7a252eb6 100644 --- a/doc/tut1.txt +++ b/doc/tut1.txt @@ -132,7 +132,7 @@ a backslash: TMyObject {.final, pure, acyclic.} = object # comment continues: \ # we have lots of space here to comment 'TMyObject'. # This line belongs to the comment as it's properly aligned. - + Comments are tokens; they are only allowed at certain places in the input file as they belong to the syntax tree! This feature enables perfect source-to-source @@ -148,7 +148,20 @@ the syntax, watch their indentation: # comment has not the correct indentation level -> syntax error! **Note**: To comment out a large piece of code, it is often better to use a -``when false:`` statement. +``when false:`` statement. + +.. code-block:: nimrod + when false: + brokenCode() + +Another option is to use the `discard`_ statement together with +*long string literals* to create block comments: + +.. code-block:: nimrod + discard """ You can have any nimrod code text commented + out inside this with no indentation restrictions. + yes("May I ask a pointless question?") """ + Numbers @@ -575,27 +588,44 @@ Some terminology: in the example ``question`` is called a (formal) *parameter*, Result variable --------------- -A procedure that returns a value has an implicit ``result`` variable that -represents the return value. A ``return`` statement with no expression is a -shorthand for ``return result``. So all three code snippets are equivalent: +A procedure that returns a value has an implicit ``result`` variable declared +that represents the return value. A ``return`` statement with no expression is a +shorthand for ``return result``. The ``result`` value is always returned +automatically at the end a procedure if there is no ``return`` statement at +the exit. .. code-block:: nimrod - return 42 - -.. code-block:: nimrod - result = 42 - return - -.. code-block:: nimrod - result = 42 - return result + proc sumTillNegative(x: varargs[int]): int = + for i in x: + if i < 0: + return + result = result + i + + echo sumTillNegative() # echos 0 + echo sumTillNegative(3, 4, 5) # echos 12 + echo sumTillNegative(3, 4 , -1 , 6) # echos 7 +The ``result`` variable is already implicitly declared at the start of the +function and initialised with the type's default value, so declaring it again +with 'var return', for example, would shadow it with a normal variable of the +same name. + Parameters ---------- -Parameters are constant in the procedure body. Their value cannot be changed -because this allows the compiler to implement parameter passing in the most -efficient way. If the procedure needs to modify the argument for the +Parameters are constant in the procedure body. By default, their value cannot be +changed because this allows the compiler to implement parameter passing in the +most efficient way. If a mutable variable is needed inside the procedure, it has +to be declared with ``var`` in the procedure body. Shadowing the parameter name +is possible, and actually an idiom: + +.. code-block:: nimrod + proc printSeq(s: seq, nprinted: int = -1) = + var nprinted = if nprinted == -1: s.len else: min(nprinted, s.len) + for i in 0 .. Date: Sat, 19 Apr 2014 13:31:55 -0300 Subject: [PATCH 09/17] Referential data types may need to be declared. And other small fixes. --- doc/tut1.txt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/doc/tut1.txt b/doc/tut1.txt index 8d7a252eb6..46eda7ae37 100644 --- a/doc/tut1.txt +++ b/doc/tut1.txt @@ -148,12 +148,12 @@ the syntax, watch their indentation: # comment has not the correct indentation level -> syntax error! **Note**: To comment out a large piece of code, it is often better to use a -``when false:`` statement. +``when false:`` statement. .. code-block:: nimrod when false: brokenCode() - + Another option is to use the `discard`_ statement together with *long string literals* to create block comments: @@ -161,7 +161,6 @@ Another option is to use the `discard`_ statement together with discard """ You can have any nimrod code text commented out inside this with no indentation restrictions. yes("May I ask a pointless question?") """ - Numbers @@ -606,9 +605,11 @@ the exit. echo sumTillNegative(3, 4 , -1 , 6) # echos 7 The ``result`` variable is already implicitly declared at the start of the -function and initialised with the type's default value, so declaring it again -with 'var return', for example, would shadow it with a normal variable of the -same name. +function, so declaring it again with 'var result', for example, would shadow it +with a normal variable of the same name. The result variable is also already +initialised with the type's default value. Note that referential data types will +be ``nil`` at the start of the procedure, and thus may require manual +initialisation. Parameters From 57cc8237f72b26cb56f8f250e382f8a0e091aea1 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 20 Apr 2014 21:54:59 +0100 Subject: [PATCH 10/17] Fixes #1093. --- compiler/vm.nim | 7 ++++++- compiler/vmgen.nim | 10 +--------- tests/macros/texprcolonexpr.nim | 19 +++++++++++++++++++ 3 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 tests/macros/texprcolonexpr.nim diff --git a/compiler/vm.nim b/compiler/vm.nim index fb87492504..69a410f187 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -354,6 +354,11 @@ template handleJmpBack() {.dirty.} = globalError(c.debug[pc], errTooManyIterations) dec(c.loopIterations) +proc skipColon(n: PNode): PNode = + result = n + if n.kind == nkExprColonExpr: + result = n.sons[1] + proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var pc = start var tos = tos @@ -454,7 +459,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeBC(rkNode) let src = regs[rb].node if src.kind notin {nkEmpty..nkNilLit}: - let n = src.sons[rc] + let n = src.sons[rc].skipColon regs[ra].node = n else: stackTrace(c, tos, pc, errNilAccess) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 7c0c3d4f59..dbb63e1669 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -316,15 +316,7 @@ proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) = c.patch(L1) proc canonValue*(n: PNode): PNode = - if n.kind == nkExprColonExpr: - result = n.sons[1] - elif n.hasSubnodeWith(nkExprColonExpr): - result = n.copyNode - newSeq(result.sons, n.len) - for i in 0.. " + Call + Ident !"name" + Ident !"a" + ExprColonExpr + Ident !"b" + Ident !"cint" + NilLit nil +''' +""" +import macros + +macro def(x: stmt): stmt {.immediate.} = + echo treeRepr(x) + +def name(a, b:cint) => nil From 171f4a21e8b2180cfeb4dcdb0ebde95f73b3dec3 Mon Sep 17 00:00:00 2001 From: EXetoC Date: Sun, 20 Apr 2014 23:03:31 +0200 Subject: [PATCH 11/17] Fix spawn ICE on invalid argument. --- compiler/semfold.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index caaab22912..79abfaf4dd 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -404,7 +404,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh, mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, - mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym: + mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn: discard of mRand: result = newIntNodeT(math.random(a.getInt.int), n) From dad99376a55e61f83606956a0c1cab289387d1d5 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 20 Apr 2014 22:08:03 +0100 Subject: [PATCH 12/17] Param name and type combos now work in type sig. sugar. --- lib/pure/future.nim | 11 +++++++++-- tests/closure/tclosuremacro.nim | 8 ++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/pure/future.nim b/lib/pure/future.nim index 2401c4f722..349ee2dcc0 100644 --- a/lib/pure/future.nim +++ b/lib/pure/future.nim @@ -26,8 +26,15 @@ proc createProcType(p, b: PNimrodNode): PNimrodNode {.compileTime.} = for i in 0 .. x + y) noReturn(() -> void => echo("noReturn")) -when false: - proc pass2(f: (int, int) -> int): (int) -> int = - (x: int) -> int => f(2, x) +proc pass2(f: (int, int) -> int): (int) -> int = + (x: int) -> int => f(2, x) - #echo pass2((x, y) => x + y) +echo pass2((x, y) => x + y)(4) From d29cf2d0e2390e1642b0bd755e506826c1578252 Mon Sep 17 00:00:00 2001 From: EXetoC Date: Sun, 20 Apr 2014 23:09:29 +0200 Subject: [PATCH 13/17] Add test for bad spawn argument. --- tests/system/tsysspawnbadarg.nim | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/system/tsysspawnbadarg.nim diff --git a/tests/system/tsysspawnbadarg.nim b/tests/system/tsysspawnbadarg.nim new file mode 100644 index 0000000000..ace074602c --- /dev/null +++ b/tests/system/tsysspawnbadarg.nim @@ -0,0 +1,7 @@ +discard """ + line: 7 + errormsg: "'spawn' takes a call expression of type void" + cmd: "nimrod $target --threads:on $options $file" +""" + +spawn(1) From 29261a0eaed477b50a3f330dc14c09606b417220 Mon Sep 17 00:00:00 2001 From: flaviut Date: Sun, 20 Apr 2014 17:10:15 -0400 Subject: [PATCH 14/17] Document vmgen.nim a bit --- compiler/vmgen.nim | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 7c0c3d4f59..0089f68b68 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -70,6 +70,9 @@ proc echoCode*(c: PCtx, start=0) {.deprecated.} = echo buf proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) = + ## Takes the registers `b` and `c`, applies the operation `opc` to them, and + ## stores the result into register `a` + ## The node is needed for debug information assert opc.ord < 255 let ins = (opc.uint32 or (a.uint32 shl 8'u32) or (b.uint32 shl 16'u32) or @@ -78,6 +81,10 @@ proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) = ctx.debug.add(n.info) proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) = + # Takes the `b` register and the immediate `imm`, appies the operation `opc`, + # and stores the output value into `a`. + # `imm` is signed and must be within [-127, 128] + assert(imm >= -127 and imm <= 128) let ins = (opc.uint32 or (a.uint32 shl 8'u32) or (b.uint32 shl 16'u32) or (imm+byteExcess).uint32 shl 24'u32).TInstr @@ -85,6 +92,9 @@ proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) = c.debug.add(n.info) proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) = + # Applies `opc` to `bx` and stores it into register `a` + # `bx` must be signed and in the range [-32767, 32768] + assert(bx >= -32767 and bx <= 32768) let ins = (opc.uint32 or a.uint32 shl 8'u32 or (bx+wordExcess).uint32 shl 16'u32).TInstr c.code.add(ins) From 443fdd6d694b4fe5508fff12394aca62855fa684 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 20 Apr 2014 22:17:56 +0100 Subject: [PATCH 15/17] Fixed docs in future module. --- lib/pure/future.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/future.nim b/lib/pure/future.nim index 349ee2dcc0..73c20e7082 100644 --- a/lib/pure/future.nim +++ b/lib/pure/future.nim @@ -54,7 +54,7 @@ proc createProcType(p, b: PNimrodNode): PNimrodNode {.compileTime.} = macro `=>`*(p, b: expr): expr {.immediate.} = ## Syntax sugar for anonymous procedures. ## - ## ..code-block:: nimrod + ## .. code-block:: nimrod ## ## proc passTwoAndTwo(f: (int, int) -> int): int = ## f(2, 2) @@ -105,7 +105,7 @@ macro `=>`*(p, b: expr): expr {.immediate.} = macro `->`*(p, b: expr): expr {.immediate.} = ## Syntax sugar for procedure types. ## - ## ..code-block:: nimrod + ## .. code-block:: nimrod ## ## proc pass2(f: (float, float) -> float): float = ## f(2, 2) From ca2b73f64a9ed49f4914dcf37bfa1b3727317f6b Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 20 Apr 2014 22:35:17 +0100 Subject: [PATCH 16/17] Revert 4b09baa0a and 33fcd1123. --- lib/system/sysstr.nim | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index eb9d2000ba..4244bae4cb 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -252,10 +252,8 @@ proc nimIntToStr(x: int): string {.compilerRtl.} = proc nimFloatToStr(x: float): string {.compilerproc.} = var buf: array [0..59, char] - c_sprintf(buf, "%#.f", x) - result = $buf - if result[len(result)-1] == '.': - result.add("0") + c_sprintf(buf, "%#.16e", x) + return $buf proc nimInt64ToStr(x: int64): string {.compilerRtl.} = result = newString(sizeof(x)*4) From 5cf8c05a226ba617a6e6de8ebe7e82c19d680b98 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 20 Apr 2014 22:55:49 +0100 Subject: [PATCH 17/17] Fixes #1119. --- compiler/vm.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/vm.nim b/compiler/vm.nim index 69a410f187..8257c360e1 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1104,6 +1104,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = c.module) of opcGorge: decodeBC(rkNode) + createStr regs[ra] regs[ra].node.strVal = opGorge(regs[rb].node.strVal, regs[rc].node.strVal) of opcNError: